pg_partitioner 0.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 +7 -0
- data/.gitignore +9 -0
- data/Gemfile +4 -0
- data/README.md +79 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/partitioner_pg/separation_type/month.rb +155 -0
- data/lib/partitioner_pg/version.rb +3 -0
- data/lib/partitioner_pg.rb +28 -0
- data/partitioner_pg.gemspec +33 -0
- metadata +81 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ad50f500db988df0b9399ef603c453e2bc9b0ce6d7ce0b125649a59e17790239
|
4
|
+
data.tar.gz: cfa5c1befeecc7a4029a28423ae106e5e23ce855d018c1a58b02bb05e475e6e9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 996cac41d300d33993934556b190f44beb9aef5816ae5b478d2cc7a18ea13fe3ab654e871e40d0565a426f77ba0931cda4a09e9248f2be68fc2dc7df89cac987
|
7
|
+
data.tar.gz: d32a8af42bcd352cf688b1db13e69cfc156fb64029e7ebb608eced97ced5a0fb88bb173dddc70051358b57c29e0e47d21dc5454f3ecbbbfe0a960d14111ef45b
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# PgPartitioner
|
2
|
+
|
3
|
+
It's a gem for a partitioning Postgresql tables in Ruby on Rails.
|
4
|
+
Now gem can help you with partitioning tables by months. New table for each month.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'partitioner_pg'
|
12
|
+
```
|
13
|
+
|
14
|
+
## Usage
|
15
|
+
|
16
|
+
1) Include PartitionerPg into your model: `include PartitionerPg`. Also, you can specify parting column and some indexes.
|
17
|
+
Example:
|
18
|
+
```ruby
|
19
|
+
class YourModelName < ActiveRecord::Base
|
20
|
+
extend PartitionerPg::SeparationType::Month
|
21
|
+
|
22
|
+
# Point date or datetime column which will be used for monthly partitioning. Optional (:created_at uses by default)
|
23
|
+
def self.parting_column
|
24
|
+
:date
|
25
|
+
end
|
26
|
+
|
27
|
+
# List of indexes. Names of indexes will be generated by active migration. Optional.
|
28
|
+
def self.partition_table_indexes
|
29
|
+
[
|
30
|
+
%w[column1 column2]
|
31
|
+
]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Map of indexes. Full name of named index will be generated as "index_[partition_name]_[name_of_index_from_map]". Optional.
|
35
|
+
def self.partition_table_named_indexes
|
36
|
+
{
|
37
|
+
:three_cols => %w[column1 column2 column3]
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
# List of unique indexes. Names of indexes wiil be generated by active migration. Optional.
|
42
|
+
def self.partition_table_unique_indexes
|
43
|
+
[
|
44
|
+
%w[column1 column2 column3 column4]
|
45
|
+
]
|
46
|
+
end
|
47
|
+
|
48
|
+
# Map of named unique indexes. Optional.
|
49
|
+
def self.partition_table_named_unique_indexes
|
50
|
+
{
|
51
|
+
:uniq => %w[column5 column6 column7]
|
52
|
+
}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
```
|
56
|
+
2) Create migration and add some instructions to it. Generate migration:
|
57
|
+
|
58
|
+
```
|
59
|
+
rails g migration make_partitioning_of_you_table
|
60
|
+
```
|
61
|
+
|
62
|
+
add instructions to migration:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
class YouMigrationClassName
|
66
|
+
def change
|
67
|
+
YourModel.create_partitioning_by_month_trigger_sql
|
68
|
+
YourModel.create_current_month_table
|
69
|
+
YourModel.create_next_month_table
|
70
|
+
end
|
71
|
+
end
|
72
|
+
```
|
73
|
+
|
74
|
+
3) For correct work you need to create next partition every month.
|
75
|
+
We recommend you to create a rake task and run it once a month by crontab. Code for a rake task:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
YourModel.create_next_month_table
|
79
|
+
```
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "partitioner"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
module PartitionerPg
|
2
|
+
module SeparationType
|
3
|
+
module Month
|
4
|
+
def create_current_month_table
|
5
|
+
create_month_tablecreate_month_table(Date.today)
|
6
|
+
end
|
7
|
+
|
8
|
+
def create_next_month_table
|
9
|
+
create_month_table(Date.today.next_month)
|
10
|
+
end
|
11
|
+
|
12
|
+
def drop_old_month_table
|
13
|
+
drop_month_table(Date.today.prev_month.prev_month)
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_month_table(date = Date.today)
|
17
|
+
date_start = date.at_beginning_of_month
|
18
|
+
date_end = date.at_beginning_of_month.next_month
|
19
|
+
partition_table_name = name_of_partition_table(date)
|
20
|
+
sql = "CREATE TABLE IF NOT EXISTS #{partition_table_name} (
|
21
|
+
CHECK ( #{parting_column} >= DATE('#{date_start}') AND #{parting_column} < DATE('#{date_end}') )
|
22
|
+
) INHERITS (#{table_name});"
|
23
|
+
execute_sql(sql)
|
24
|
+
sql = "ALTER TABLE #{partition_table_name} ADD PRIMARY KEY (id);"
|
25
|
+
execute_sql(sql)
|
26
|
+
|
27
|
+
create_partition_indexes(partition_table_name)
|
28
|
+
create_partition_named_indexes(partition_table_name)
|
29
|
+
create_partition_unique_indexes(partition_table_name)
|
30
|
+
create_partition_named_unique_indexes(partition_table_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def drop_month_table(date = Date.today)
|
34
|
+
sql = "DROP TABLE IF EXISTS #{name_of_partition_table(date)};"
|
35
|
+
execute_sql(sql)
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_partitioning_by_month_trigger_sql
|
39
|
+
sql = "CREATE OR REPLACE FUNCTION #{table_name}_insert_trigger() RETURNS trigger AS
|
40
|
+
$$
|
41
|
+
DECLARE
|
42
|
+
curY varchar(4);
|
43
|
+
curM varchar(2);
|
44
|
+
tbl varchar(30);
|
45
|
+
BEGIN
|
46
|
+
select cast(DATE_PART('year', new.#{parting_column}) as varchar) into curY;
|
47
|
+
select lpad(cast(DATE_PART('month', new.#{parting_column}) as varchar), 2, '0') into curM;
|
48
|
+
tbl := '#{table_name}_y' || curY || 'm' || curM;
|
49
|
+
EXECUTE format('INSERT into %I values ($1.*);', tbl) USING NEW;
|
50
|
+
return NEW;
|
51
|
+
END;
|
52
|
+
$$
|
53
|
+
LANGUAGE plpgsql;
|
54
|
+
|
55
|
+
CREATE TRIGGER #{table_name}_insert
|
56
|
+
BEFORE INSERT ON #{table_name}
|
57
|
+
FOR EACH ROW
|
58
|
+
EXECUTE PROCEDURE #{table_name}_insert_trigger();
|
59
|
+
|
60
|
+
-- Trigger function to delete from the master table after the insert
|
61
|
+
CREATE OR REPLACE FUNCTION #{table_name}_delete_trigger() RETURNS trigger
|
62
|
+
AS $$
|
63
|
+
DECLARE
|
64
|
+
r #{table_name}%rowtype;
|
65
|
+
BEGIN
|
66
|
+
DELETE FROM ONLY #{table_name} where id = new.id returning * into r;
|
67
|
+
RETURN r;
|
68
|
+
end;
|
69
|
+
$$
|
70
|
+
LANGUAGE plpgsql;
|
71
|
+
|
72
|
+
CREATE TRIGGER #{table_name}_after_insert
|
73
|
+
AFTER INSERT ON #{table_name}
|
74
|
+
FOR EACH ROW
|
75
|
+
EXECUTE PROCEDURE #{table_name}_delete_trigger();"
|
76
|
+
|
77
|
+
execute_sql(sql)
|
78
|
+
end
|
79
|
+
|
80
|
+
def drop_partitioning_by_month_trigger_sql
|
81
|
+
"DROP TRIGGER #{table_name}_insert ON #{table_name};
|
82
|
+
DROP FUNCTION #{table_name}_insert_trigger();
|
83
|
+
DROP TRIGGER #{table_name}_after_insert ON #{table_name};
|
84
|
+
DROP FUNCTION #{table_name}_delete_trigger();"
|
85
|
+
end
|
86
|
+
|
87
|
+
def create_partition_indexes(partition_table_name)
|
88
|
+
custom_indexes = partition_table_indexes.presence
|
89
|
+
return unless custom_indexes
|
90
|
+
|
91
|
+
custom_indexes.each { |custom_index| create_custom_index(partition_table_name, custom_index) }
|
92
|
+
end
|
93
|
+
|
94
|
+
def create_partition_named_indexes(partition_table_name)
|
95
|
+
custom_indexes = partition_table_named_indexes.presence
|
96
|
+
return unless custom_indexes
|
97
|
+
|
98
|
+
custom_indexes.map do |name, custom_index|
|
99
|
+
index_name = "index_#{partition_table_name}_#{name}"
|
100
|
+
create_custom_named_index(partition_table_name, custom_index, index_name)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def create_partition_unique_indexes(partition_table_name)
|
105
|
+
custom_unique_indexes = partition_table_unique_indexes.presence
|
106
|
+
return unless custom_unique_indexes
|
107
|
+
|
108
|
+
custom_unique_indexes.each { |custom_index| create_custom_index(partition_table_name, custom_index, true) }
|
109
|
+
end
|
110
|
+
|
111
|
+
def create_partition_named_unique_indexes(partition_table_name)
|
112
|
+
custom_indexes = partition_table_named_unique_indexes.presence
|
113
|
+
return unless custom_indexes
|
114
|
+
|
115
|
+
custom_indexes.map do |name, custom_index|
|
116
|
+
index_name = "index_#{partition_table_name}_#{name}"
|
117
|
+
create_custom_named_index(partition_table_name, custom_index, index_name, true)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def create_custom_index(table_name, index_fields, is_unique = false)
|
122
|
+
ActiveRecord::Migration.add_index table_name, index_fields, unique: is_unique
|
123
|
+
end
|
124
|
+
|
125
|
+
def create_custom_named_index(table_name, index_fields, name, is_unique = false)
|
126
|
+
ActiveRecord::Migration.add_index table_name, index_fields, name: name, unique: is_unique
|
127
|
+
end
|
128
|
+
|
129
|
+
def name_of_partition_table(date = Date.today)
|
130
|
+
date.strftime("#{table_name}_y%Ym%m")
|
131
|
+
end
|
132
|
+
|
133
|
+
def execute_sql(sql_string)
|
134
|
+
ActiveRecord::Base.connection.execute(sql_string)
|
135
|
+
end
|
136
|
+
|
137
|
+
# Template method
|
138
|
+
# Column which will determine partition for row (must be date or datetime type). Default value is :created_at
|
139
|
+
def parting_column
|
140
|
+
:created_at
|
141
|
+
end
|
142
|
+
|
143
|
+
# Template method
|
144
|
+
def partition_table_indexes; end
|
145
|
+
|
146
|
+
def partition_table_named_indexes; end
|
147
|
+
|
148
|
+
# Template method
|
149
|
+
def partition_table_unique_indexes; end
|
150
|
+
|
151
|
+
# Template method
|
152
|
+
def partition_table_named_unique_indexes; end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "partitioner_pg/version"
|
2
|
+
require "partitioner_pg/separation_type/month"
|
3
|
+
|
4
|
+
module PartitionerPg
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
#available types - :month
|
8
|
+
def initialize(type)
|
9
|
+
@type = type
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
include SeparationType::Month
|
14
|
+
|
15
|
+
def partitioner_pg(type)
|
16
|
+
new(type.to_sym)
|
17
|
+
puts TEST0
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def execute_sql(sql)
|
23
|
+
connection.execute(sql)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'partitioner_pg/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'pg_partitioner'
|
7
|
+
spec.version = PartitionerPg::VERSION
|
8
|
+
spec.authors = ['Ovsapyan Mishel']
|
9
|
+
spec.email = ['ovasapyan.mn@gmail.com']
|
10
|
+
|
11
|
+
spec.summary = 'Gem for a partitoning PG tables'
|
12
|
+
spec.description = 'Gem for a partitoning PG tables'
|
13
|
+
spec.homepage = 'http://appodeal.com'
|
14
|
+
|
15
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
16
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
17
|
+
if spec.respond_to?(:metadata)
|
18
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
19
|
+
else
|
20
|
+
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
21
|
+
'public gem pushes.'
|
22
|
+
end
|
23
|
+
|
24
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
25
|
+
f.match(%r{^(test|spec|features)/})
|
26
|
+
end
|
27
|
+
spec.bindir = 'exe'
|
28
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
29
|
+
spec.require_paths = ['lib']
|
30
|
+
|
31
|
+
spec.add_development_dependency 'bundler', '~> 1.13'
|
32
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pg_partitioner
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ovsapyan Mishel
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-10-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.13'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.13'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
description: Gem for a partitoning PG tables
|
42
|
+
email:
|
43
|
+
- ovasapyan.mn@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".gitignore"
|
49
|
+
- Gemfile
|
50
|
+
- README.md
|
51
|
+
- Rakefile
|
52
|
+
- bin/console
|
53
|
+
- bin/setup
|
54
|
+
- lib/partitioner_pg.rb
|
55
|
+
- lib/partitioner_pg/separation_type/month.rb
|
56
|
+
- lib/partitioner_pg/version.rb
|
57
|
+
- partitioner_pg.gemspec
|
58
|
+
homepage: http://appodeal.com
|
59
|
+
licenses: []
|
60
|
+
metadata:
|
61
|
+
allowed_push_host: https://rubygems.org
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirements: []
|
77
|
+
rubygems_version: 3.2.3
|
78
|
+
signing_key:
|
79
|
+
specification_version: 4
|
80
|
+
summary: Gem for a partitoning PG tables
|
81
|
+
test_files: []
|