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 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
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in partitioner_pg.gemspec
4
+ gemspec
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
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
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,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -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,3 @@
1
+ module PartitionerPg
2
+ VERSION = '0.1.0'
3
+ 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: []