pg_partitioning 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +97 -0
- data/Rakefile +23 -0
- data/config/locales/en.yml +28 -0
- data/lib/generators/partitioning_generator.rb +18 -0
- data/lib/pg_partitioning/input_master.rb +83 -0
- data/lib/pg_partitioning/partitioning_master.rb +78 -0
- data/lib/pg_partitioning/printer.rb +28 -0
- data/lib/pg_partitioning/strategies/base.rb +72 -0
- data/lib/pg_partitioning/strategies/date.rb +73 -0
- data/lib/pg_partitioning/strategies/equal.rb +43 -0
- data/lib/pg_partitioning/strategies/step.rb +57 -0
- data/lib/pg_partitioning/version.rb +3 -0
- data/lib/pg_partitioning.rb +4 -0
- data/lib/tasks/pg_partitioning_tasks.rake +4 -0
- data/spec/db_helpers.rb +36 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/bandit.rb +4 -0
- data/spec/dummy/app/models/crime.rb +3 -0
- data/spec/dummy/app/models/gang.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +29 -0
- data/spec/dummy/config/application.rb +32 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +28 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +41 -0
- data/spec/dummy/config/environments/production.rb +79 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +56 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/migrate/20160306173540_create_gangs.rb +9 -0
- data/spec/dummy/db/migrate/20160306174017_create_bandits.rb +12 -0
- data/spec/dummy/db/migrate/20160306174042_create_crimes.rb +10 -0
- data/spec/dummy/db/schema.rb +47 -0
- data/spec/dummy/log/development.log +3421 -0
- data/spec/dummy/log/production.log +0 -0
- data/spec/dummy/log/test.log +154834 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/factories/bandits.rb +23 -0
- data/spec/factories/crimes.rb +6 -0
- data/spec/factories/gangs.rb +5 -0
- data/spec/input_master_spec.rb +74 -0
- data/spec/models/bandit_spec.rb +13 -0
- data/spec/models/crime_spec.rb +10 -0
- data/spec/models/gang_spec.rb +10 -0
- data/spec/partitioning_master_spec.rb +156 -0
- data/spec/rails_helper.rb +67 -0
- data/spec/shared_examples.rb +28 -0
- data/spec/spec_helper.rb +92 -0
- metadata +213 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3218f90c14f4626030e7eee68580ba3e8ab6beec
|
4
|
+
data.tar.gz: 6f71ac05ee7bb5fdf82f65335aeba6ab7cc1073a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2051b32762a47edc84ce1cf597282dc4700280dd7bb32f747497b6d143a7eefe32f712cb44937dd968bddadfc32e2aec5999d14a05b55c3cf22d1177fa820231
|
7
|
+
data.tar.gz: 0b3f91c0c0f69906e733ac45ce794ddacb773bde5bd0e595f2da720d71168a360f9c8711ff96f28044b11d31e333c8595f0dcd4851052633bf97e2fe66748a73
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2016 Victor M.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
= PgPartitioning
|
2
|
+
|
3
|
+
{<img src="https://codeclimate.com/github/victor-magarlamov/pg_partitioning/badges/gpa.svg" />}[https://codeclimate.com/github/victor-magarlamov/pg_partitioning]
|
4
|
+
{<img src="https://codeclimate.com/github/victor-magarlamov/pg_partitioning/badges/coverage.svg" />}[https://codeclimate.com/github/victor-magarlamov/pg_partitioning/coverage]
|
5
|
+
|
6
|
+
This project rocks and uses MIT-LICENSE.
|
7
|
+
|
8
|
+
== Install
|
9
|
+
|
10
|
+
Put this line in your Gemfile:
|
11
|
+
gem 'pg_partitioning'
|
12
|
+
|
13
|
+
Then bundle:
|
14
|
+
% bundle install
|
15
|
+
|
16
|
+
== Usage
|
17
|
+
|
18
|
+
% RAILS_ENV=production rails g partitioning
|
19
|
+
|
20
|
+
1) Select the partitioning mode:
|
21
|
+
* by single column +value+
|
22
|
+
* by dates +template+
|
23
|
+
* by +range+
|
24
|
+
|
25
|
+
2) Enter +table+ name.
|
26
|
+
|
27
|
+
3) Enter +column+ name.
|
28
|
+
|
29
|
+
4) Enter condition depending on the selected mode - template +pattern+ or range +step+.
|
30
|
+
|
31
|
+
What will happen then...
|
32
|
+
|
33
|
+
1) The generator will create two triggers: before insert and after insert. The before insert trigger executes the procedure that created nested table and inserts a new record into it. The after trigger clears the master table.
|
34
|
+
|
35
|
+
2) Foreign keys which reference to the master table will delete.
|
36
|
+
|
37
|
+
3) Old data will be migrated from master table to child tables.
|
38
|
+
|
39
|
+
For more details, see ...
|
40
|
+
|
41
|
+
== Examples
|
42
|
+
|
43
|
+
Given we have table 'bandits':
|
44
|
+
|
45
|
+
| id | name | specialization | born_at
|
46
|
+
|
47
|
+
=== Example 1: Partitioning by single column value
|
48
|
+
|
49
|
+
Enter mode: 0
|
50
|
+
Enter table: bandits
|
51
|
+
Enter column: specialization
|
52
|
+
|
53
|
+
Create two bandits:
|
54
|
+
|
55
|
+
Bandit.create([{name: 'Al Capone', specialization: 'bootlegger'},
|
56
|
+
{name: 'Black Bart', specialization: 'robber'}])
|
57
|
+
|
58
|
+
Now we have three tables:
|
59
|
+
|
60
|
+
* bandits - master - not contains real data
|
61
|
+
* bandits_bootlegger - nested - contains only the bootleggers
|
62
|
+
* bandits_robber - nested - contains only the robbers
|
63
|
+
|
64
|
+
Check this...
|
65
|
+
|
66
|
+
SELECT COUNT(*) FROM bandits; (2)
|
67
|
+
SELECT COUNT(*) FROM ONLY bandits; (0)
|
68
|
+
SELECT COUNT(*) FROM ONLY bandits_bootlegger; (1)
|
69
|
+
SELECT COUNT(*) FROM ONLY bandits_robber; (1)
|
70
|
+
|
71
|
+
=== Example 2: Partitioning by range
|
72
|
+
|
73
|
+
Enter mode: 1
|
74
|
+
Enter table: bandits
|
75
|
+
Enter column: id
|
76
|
+
Enter step: 10
|
77
|
+
|
78
|
+
All bandits with ID from 0 to 9 will be recorded to table 'bandits_0'.
|
79
|
+
All bandits with ID from 10 to 19 will be recorded to table 'bandits_1'.
|
80
|
+
All bandits with ID from 20 to 29 will be recorded to table 'bandits_2' etc.
|
81
|
+
|
82
|
+
=== Example 3: Partitioning by date template
|
83
|
+
|
84
|
+
Enter mode: 2
|
85
|
+
Enter table: bandits
|
86
|
+
Enter column: born_at
|
87
|
+
Enter pattern: YYYYMM
|
88
|
+
|
89
|
+
Create two bandits:
|
90
|
+
|
91
|
+
Bandit.create([{name: 'Al Capone', born_at: '1899-01-17'},
|
92
|
+
{name: 'Charles Luciano', born_at: '1897-11-24'}])
|
93
|
+
|
94
|
+
And now we have two child tables:
|
95
|
+
|
96
|
+
* bandits_1899_01
|
97
|
+
* bandits_1897_11
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'PgPartitioning'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
Bundler::GemHelper.install_tasks
|
23
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
en:
|
2
|
+
pg_partitioning:
|
3
|
+
enter: "Enter the %{quest}"
|
4
|
+
quests:
|
5
|
+
mode: "type of the partitioning - equal(0), step(1), date(2): "
|
6
|
+
table_name: "name of the table: "
|
7
|
+
column_name: "name of the column: "
|
8
|
+
cond_step: "step (e.g. 1000): "
|
9
|
+
cond_date: "pattern (e.g. YYYY_MM or MM): "
|
10
|
+
failure:
|
11
|
+
answer: "I cannot accept your answer!"
|
12
|
+
mode: "Invalid value."
|
13
|
+
step: "Invalid value. Should be eat then 0"
|
14
|
+
pattern: "Invalid value."
|
15
|
+
no_table: "Table with some column is not exists!"
|
16
|
+
column_type: "Invalid column type."
|
17
|
+
blank_cond: "Value is not set."
|
18
|
+
other: "An error has occurred."
|
19
|
+
progress:
|
20
|
+
insert_master: "Insert master func: %{state}"
|
21
|
+
before_insert_trigger: "Before Insert trigger: %{state}"
|
22
|
+
drop_master: "Drop master func: %{state}"
|
23
|
+
after_insert_trigger: "After Insert trigger: %{state}"
|
24
|
+
drop_fk: "Delete foreign keys: %{state}"
|
25
|
+
migration: "Migrate old data: %{state}"
|
26
|
+
finish: "Done!"
|
27
|
+
messages:
|
28
|
+
partition_mode: "Recommend to set constraint_exclusion in 'partition' state for best perfomance (your current state is %{current})."
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'pg_partitioning/input_master'
|
2
|
+
require 'pg_partitioning/partitioning_master'
|
3
|
+
|
4
|
+
class PartitioningGenerator < Rails::Generators::Base
|
5
|
+
|
6
|
+
def partitioning
|
7
|
+
input_master = PgPartitioning::InputMaster.new
|
8
|
+
input_master.intro
|
9
|
+
|
10
|
+
mode = input_master.ask_mode.to_i
|
11
|
+
table_name = input_master.ask_table_name
|
12
|
+
column_name = input_master.ask_column_name
|
13
|
+
cond = input_master.ask_cond(PgPartitioning::PartitioningMaster::MODES[mode])
|
14
|
+
|
15
|
+
master = PgPartitioning::PartitioningMaster.new(table_name, column_name, mode, cond)
|
16
|
+
master.partitioning
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'pg_partitioning/printer'
|
2
|
+
|
3
|
+
module PgPartitioning
|
4
|
+
class InputMaster
|
5
|
+
include Printer
|
6
|
+
|
7
|
+
%w(mode table_name column_name cond).each do |act|
|
8
|
+
define_method "ask_#{act}" do |argument = nil|
|
9
|
+
begin
|
10
|
+
quest_key = argument.blank? ? act : "#{act}_#{argument}"
|
11
|
+
quest_val = I18n.t "pg_partitioning.quests.#{quest_key}", raise: true
|
12
|
+
res = ask quest_val
|
13
|
+
if send "#{quest_key}_valid?", res
|
14
|
+
return res
|
15
|
+
else
|
16
|
+
alert @error_message
|
17
|
+
send "ask_#{act}", argument unless ENV['RAILS_ENV'] == 'test'
|
18
|
+
end
|
19
|
+
rescue I18n::MissingTranslationData
|
20
|
+
return nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def method_missing(name, *args)
|
26
|
+
valid?(*args) if name.to_s.include?('_valid?')
|
27
|
+
end
|
28
|
+
|
29
|
+
def intro
|
30
|
+
info <<-INTRO
|
31
|
+
Hi! It's time to do partitioning)
|
32
|
+
I can do it in different ways:
|
33
|
+
|
34
|
+
by EQUALity
|
35
|
+
- partition will be created by value of column,
|
36
|
+
- in this case you should only set column name;
|
37
|
+
|
38
|
+
by STEP
|
39
|
+
- partition will be created by value of column divided by the step and rounded,
|
40
|
+
- in this case you should else set number of the step,
|
41
|
+
- a type of the column should be a numeric;
|
42
|
+
|
43
|
+
by DATE
|
44
|
+
- partition will be created by pattern from date,
|
45
|
+
- in this case you should else set pattern for parsing the date,
|
46
|
+
- acceptable (itself or in combination): Y, YY, YYY, YYYY, MM, D, DD, DDD, W, WW, HH24
|
47
|
+
- a type of the column should be a date/timestamp.
|
48
|
+
INTRO
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
def ask(text)
|
53
|
+
text_color WHITE
|
54
|
+
print I18n.t('pg_partitioning.enter', quest: text)
|
55
|
+
gets.chomp
|
56
|
+
end
|
57
|
+
|
58
|
+
def valid?(text)
|
59
|
+
res = !text.blank?
|
60
|
+
@error_message = I18n.t 'pg_partitioning.failure.answer' unless res
|
61
|
+
res
|
62
|
+
end
|
63
|
+
|
64
|
+
def mode_valid?(text)
|
65
|
+
res = %w(0 1 2).include? text
|
66
|
+
@error_message = I18n.t 'pg_partitioning.failure.mode' unless res
|
67
|
+
res
|
68
|
+
end
|
69
|
+
|
70
|
+
def cond_step_valid?(text)
|
71
|
+
res = text.to_i > 0
|
72
|
+
@error_message = I18n.t "pg_partitioning.failure.step" unless res
|
73
|
+
res
|
74
|
+
end
|
75
|
+
|
76
|
+
def cond_date_valid?(text)
|
77
|
+
res = /Y{1,4}|M{2}|D{1,3}|W{1,2}|HH24/ =~ text
|
78
|
+
@error_message = I18n.t "pg_partitioning.failure.pattern" unless res
|
79
|
+
res
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'pg_partitioning/printer'
|
2
|
+
require 'pg_partitioning/strategies/equal'
|
3
|
+
require 'pg_partitioning/strategies/date'
|
4
|
+
require 'pg_partitioning/strategies/step'
|
5
|
+
|
6
|
+
module PgPartitioning
|
7
|
+
class PartitioningMaster
|
8
|
+
include Printer
|
9
|
+
|
10
|
+
MODES = %w(equal step date).freeze
|
11
|
+
|
12
|
+
def initialize(table_name, column_name, mode, cond=nil)
|
13
|
+
@table_name = table_name
|
14
|
+
@column_name = column_name
|
15
|
+
@cond = cond
|
16
|
+
@sql = ActiveRecord::Base.connection()
|
17
|
+
|
18
|
+
klass = "PgPartitioning::Strategies::#{MODES[mode].classify}"
|
19
|
+
@strategy = klass.constantize.new(@table_name, @column_name, @cond, @sql)
|
20
|
+
end
|
21
|
+
|
22
|
+
def partitioning
|
23
|
+
@strategy.partitioning!
|
24
|
+
|
25
|
+
drop_foreign_keys
|
26
|
+
migration
|
27
|
+
|
28
|
+
mode = show_value_of('constraint_exclusion')
|
29
|
+
if mode != 'partition'
|
30
|
+
alert I18n.t('pg_partitioning.messages.partition_mode', current: mode)
|
31
|
+
end
|
32
|
+
|
33
|
+
info I18n.t 'pg_partitioning.progress.finish'
|
34
|
+
rescue => e
|
35
|
+
alert e.message || I18n.t('pg_partitioning.failure.other')
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def drop_foreign_keys
|
40
|
+
fk_info = []
|
41
|
+
query = ActiveRecord::Base.send(
|
42
|
+
:sanitize_sql_array,
|
43
|
+
["SELECT tc.constraint_name, tc.table_name, kcu.column_name,
|
44
|
+
ccu.table_name AS foreign_table_name,
|
45
|
+
ccu.column_name AS foreign_column_name
|
46
|
+
FROM information_schema.table_constraints AS tc
|
47
|
+
JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
|
48
|
+
JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
|
49
|
+
WHERE constraint_type = 'FOREIGN KEY' AND ccu.table_name=?;", @table_name])
|
50
|
+
|
51
|
+
@sql.execute(query).each do |r|
|
52
|
+
fk_info << r
|
53
|
+
end
|
54
|
+
|
55
|
+
fk_info.each do |r|
|
56
|
+
@sql.execute "ALTER TABLE #{r['table_name']} DROP CONSTRAINT #{r['constraint_name']};"
|
57
|
+
end
|
58
|
+
|
59
|
+
info I18n.t('pg_partitioning.progress.drop_fk', state: 'OK')
|
60
|
+
end
|
61
|
+
|
62
|
+
def migration
|
63
|
+
@sql.execute <<-SQL
|
64
|
+
CREATE MATERIALIZED VIEW temp_table AS SELECT * FROM #{@table_name};
|
65
|
+
DELETE FROM #{@table_name};
|
66
|
+
INSERT INTO #{@table_name} (SELECT * FROM temp_table);
|
67
|
+
DROP MATERIALIZED VIEW temp_table;
|
68
|
+
SQL
|
69
|
+
info I18n.t('pg_partitioning.progress.migration', state: 'OK')
|
70
|
+
end
|
71
|
+
|
72
|
+
def show_value_of(param)
|
73
|
+
res = ''
|
74
|
+
@sql.execute("SHOW #{param};").each{ |r| res = r[param] }
|
75
|
+
res
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module PgPartitioning
|
2
|
+
module Printer
|
3
|
+
COLORS = [GREEN="\e[32m", WHITE="\e[0m", RED="\e[31m"]
|
4
|
+
|
5
|
+
def text_color(color)
|
6
|
+
print color
|
7
|
+
end
|
8
|
+
|
9
|
+
def print_row(mes, color = WHITE)
|
10
|
+
text_color color
|
11
|
+
print mes + "\n"
|
12
|
+
text_color WHITE
|
13
|
+
end
|
14
|
+
|
15
|
+
def info(mes)
|
16
|
+
print_row mes, GREEN
|
17
|
+
end
|
18
|
+
|
19
|
+
def alert(mes)
|
20
|
+
print_row mes, RED
|
21
|
+
end
|
22
|
+
|
23
|
+
def message(mes)
|
24
|
+
print_row mes
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'pg_partitioning/printer'
|
2
|
+
|
3
|
+
module PgPartitioning
|
4
|
+
module Strategies
|
5
|
+
class Base
|
6
|
+
include Printer
|
7
|
+
|
8
|
+
def initialize(table, column, cond, sql_conn)
|
9
|
+
@table_name = table
|
10
|
+
@column_name = column
|
11
|
+
@cond = cond
|
12
|
+
@sql = sql_conn
|
13
|
+
end
|
14
|
+
|
15
|
+
def partitioning!
|
16
|
+
raise error_message unless valid?
|
17
|
+
create_insert_master_function
|
18
|
+
create_trigger('insert_master', 'before')
|
19
|
+
create_drop_function
|
20
|
+
create_trigger('delete_master', 'after')
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def valid?
|
26
|
+
@data_type = column_data_type
|
27
|
+
@error_message = I18n.t "pg_partitioning.failure.no_table" if @data_type.blank?
|
28
|
+
@error_message.blank?
|
29
|
+
end
|
30
|
+
|
31
|
+
def error_message
|
32
|
+
@error_message || I18n.t("pg_partitioning.failure.other")
|
33
|
+
end
|
34
|
+
|
35
|
+
def column_data_type
|
36
|
+
res = nil
|
37
|
+
query = ActiveRecord::Base.send(:sanitize_sql_array,
|
38
|
+
["SELECT data_type FROM information_schema.columns
|
39
|
+
WHERE table_name = ? AND column_name = ?;",
|
40
|
+
@table_name, @column_name])
|
41
|
+
@sql.execute(query).each do |r|
|
42
|
+
res = r['data_type']
|
43
|
+
end
|
44
|
+
res
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_trigger(master, mode)
|
48
|
+
drop_trigger(mode)
|
49
|
+
@sql.execute "CREATE TRIGGER #{@table_name}_#{mode}_insert_trigger
|
50
|
+
#{mode} INSERT ON #{@table_name}
|
51
|
+
FOR EACH ROW EXECUTE PROCEDURE #{@table_name}_#{master}();"
|
52
|
+
info I18n.t("pg_partitioning.progress.#{mode}_insert_trigger", state: "OK")
|
53
|
+
end
|
54
|
+
|
55
|
+
def drop_trigger(mode)
|
56
|
+
@sql.execute "DROP TRIGGER IF EXISTS #{@table_name}_#{mode}_insert_trigger ON #{@table_name};"
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_drop_function
|
60
|
+
@sql.execute "CREATE OR REPLACE FUNCTION #{@table_name}_delete_master() RETURNS TRIGGER AS $$
|
61
|
+
DECLARE
|
62
|
+
row #{@table_name.to_sym}%rowtype;
|
63
|
+
BEGIN
|
64
|
+
DELETE FROM ONLY #{@table_name} WHERE id = NEW.id RETURNING * INTO row;
|
65
|
+
RETURN row;
|
66
|
+
END;
|
67
|
+
$$ LANGUAGE plpgsql;"
|
68
|
+
info I18n.t("pg_partitioning.progress.drop_master", state: "OK")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|