partitioned 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/examples/created_at_referencing_awards.rb +2 -2
- data/lib/monkey_patch_activerecord.rb +35 -1
- data/lib/monkey_patch_postgres.rb +19 -1
- data/lib/partitioned/active_record_overrides.rb +1 -1
- data/lib/partitioned/bulk_methods_mixin.rb +2 -2
- data/lib/partitioned/by_integer_field.rb +5 -0
- data/lib/partitioned/partitioned_base/configurator/data.rb +8 -2
- data/lib/partitioned/partitioned_base/configurator/dsl.rb +19 -0
- data/lib/partitioned/partitioned_base/configurator/reader.rb +31 -0
- data/lib/partitioned/partitioned_base/partition_manager.rb +49 -8
- data/lib/partitioned/partitioned_base/sql_adapter.rb +7 -0
- data/lib/partitioned/partitioned_base.rb +58 -34
- data/lib/partitioned/version.rb +1 -1
- data/partitioned.gemspec +1 -1
- data/spec/monkey_patch_posgres_spec.rb +4 -4
- metadata +2 -8
@@ -105,7 +105,7 @@
|
|
105
105
|
# partitioned do |partition|
|
106
106
|
# partition.foreign_key lambda {|model, *partition_key_values|
|
107
107
|
# return Configurator::Data::ForeignKey.
|
108
|
-
# new(:employee_id, Employee.
|
108
|
+
# new(:employee_id, Employee.partition_table_name(*partition_key_values), :id)
|
109
109
|
# }
|
110
110
|
# end
|
111
111
|
# end
|
@@ -702,7 +702,7 @@ class Award < ByEmployeeCreatedAt
|
|
702
702
|
|
703
703
|
partitioned do |partition|
|
704
704
|
partition.foreign_key lambda {|model, *partition_key_values|
|
705
|
-
return Configurator::Data::ForeignKey.new(:employee_id, Employee.
|
705
|
+
return Configurator::Data::ForeignKey.new(:employee_id, Employee.partition_table_name(*partition_key_values), :id)
|
706
706
|
}
|
707
707
|
end
|
708
708
|
|
@@ -13,6 +13,40 @@ module ActiveRecord
|
|
13
13
|
# Patches for Persistence to allow certain partitioning (that related to the primary key) to work.
|
14
14
|
#
|
15
15
|
module Persistence
|
16
|
+
# Deletes the record in the database and freezes this instance to reflect
|
17
|
+
# that no changes should be made (since they can't be persisted).
|
18
|
+
def destroy
|
19
|
+
destroy_associations
|
20
|
+
|
21
|
+
if persisted?
|
22
|
+
IdentityMap.remove(self) if IdentityMap.enabled?
|
23
|
+
pk = self.class.primary_key
|
24
|
+
column = self.class.columns_hash[pk]
|
25
|
+
substitute = connection.substitute_at(column, 0)
|
26
|
+
|
27
|
+
using_arel_table = self.respond_to?(:dynamic_arel_table) ? dynamic_arel_table() : self.class.arel_table
|
28
|
+
relation = self.class.unscoped.where(
|
29
|
+
using_arel_table[pk].eq(substitute))
|
30
|
+
|
31
|
+
relation.bind_values = [[column, id]]
|
32
|
+
relation.delete_all
|
33
|
+
end
|
34
|
+
|
35
|
+
@destroyed = true
|
36
|
+
freeze
|
37
|
+
end
|
38
|
+
|
39
|
+
# Updates the associated record with values matching those of the instance attributes.
|
40
|
+
# Returns the number of affected rows.
|
41
|
+
def update(attribute_names = @attributes.keys)
|
42
|
+
attributes_with_values = arel_attributes_values(false, false, attribute_names)
|
43
|
+
return 0 if attributes_with_values.empty?
|
44
|
+
klass = self.class
|
45
|
+
using_arel_table = self.respond_to?(:dynamic_arel_table) ? dynamic_arel_table() : klass.arel_table
|
46
|
+
stmt = klass.unscoped.where(using_arel_table[klass.primary_key].eq(id)).arel.compile_update(attributes_with_values)
|
47
|
+
klass.connection.update stmt
|
48
|
+
end
|
49
|
+
|
16
50
|
#
|
17
51
|
# patch the create method to prefetch the primary key if needed
|
18
52
|
#
|
@@ -65,7 +99,7 @@ module ActiveRecord
|
|
65
99
|
# that are used to determine the child table this insert should be
|
66
100
|
# redirected to)
|
67
101
|
#
|
68
|
-
actual_arel_table = @klass.dynamic_arel_table(Hash[*values.map{|k,v| [k.name,v]}.flatten]) if @klass.respond_to?
|
102
|
+
actual_arel_table = @klass.dynamic_arel_table(Hash[*values.map{|k,v| [k.name,v]}.flatten]) if @klass.respond_to?(:dynamic_arel_table)
|
69
103
|
actual_arel_table = @table unless actual_arel_table
|
70
104
|
im.into actual_arel_table
|
71
105
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'active_record'
|
2
2
|
require 'active_record/base'
|
3
3
|
require 'active_record/connection_adapters/abstract_adapter'
|
4
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
4
5
|
|
5
6
|
#
|
6
7
|
# Patching {ActiveRecord::ConnectionAdapters::TableDefinition} and
|
@@ -28,6 +29,23 @@ module ActiveRecord::ConnectionAdapters
|
|
28
29
|
# to take advantage of these SQL builders.
|
29
30
|
#
|
30
31
|
class PostgreSQLAdapter < AbstractAdapter
|
32
|
+
|
33
|
+
#
|
34
|
+
# Returns the sequence name for a table's primary key or some other specified key.
|
35
|
+
#
|
36
|
+
# the default version strips off the schema name on the table (if it exists), as:
|
37
|
+
# serial_sequence(table_name, pk || 'id').split('.').last
|
38
|
+
# i can't see any good reason for that -- in fact, it seems completely
|
39
|
+
# broken -- if you have a table public.foos and other.foos, you'll fail to
|
40
|
+
# get the correct schema if you fetch the default schema name from model
|
41
|
+
# associated with other.foos
|
42
|
+
#
|
43
|
+
def default_sequence_name(table_name, pk = nil) #:nodoc:
|
44
|
+
serial_sequence(table_name, pk || 'id')
|
45
|
+
rescue ActiveRecord::StatementInvalid => e
|
46
|
+
"#{table_name}_#{pk || 'id'}_seq"
|
47
|
+
end
|
48
|
+
|
31
49
|
#
|
32
50
|
# Get the next value in a sequence. Used on INSERT operation for
|
33
51
|
# partitioning like by_id because the ID is required before the insert
|
@@ -36,7 +54,7 @@ module ActiveRecord::ConnectionAdapters
|
|
36
54
|
# @param [String] sequence_name the name of the sequence to fetch the next value from
|
37
55
|
# @return [Integer] the value from the sequence
|
38
56
|
def next_sequence_value(sequence_name)
|
39
|
-
return execute("select nextval('#{sequence_name}')").field_values("nextval").first
|
57
|
+
return execute("select nextval('#{sequence_name}')").field_values("nextval").first.to_i
|
40
58
|
end
|
41
59
|
|
42
60
|
#
|
@@ -23,7 +23,7 @@ module Partitioned
|
|
23
23
|
# @return [Hash] hash of key value pairs associated with persistent attributes
|
24
24
|
def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
|
25
25
|
attrs = super
|
26
|
-
actual_arel_table = dynamic_arel_table(
|
26
|
+
actual_arel_table = dynamic_arel_table()
|
27
27
|
return Hash[*attrs.map{|k,v| [actual_arel_table[k.name], v]}.flatten]
|
28
28
|
end
|
29
29
|
|
@@ -81,7 +81,7 @@ module Partitioned
|
|
81
81
|
# set :created_at if need be
|
82
82
|
row[:created_at] ||= created_at_value
|
83
83
|
end.group_by do |row|
|
84
|
-
respond_to?(:
|
84
|
+
respond_to?(:partition_table_name) ? partition_table_name(*partition_key_values(row)) : table_name
|
85
85
|
end.each do |table_name, rows_for_table|
|
86
86
|
column_names = rows_for_table[0].keys.sort{|a,b| a.to_s <=> b.to_s}
|
87
87
|
sql_insert_string = "insert into #{table_name} (#{column_names.join(',')}) values "
|
@@ -189,7 +189,7 @@ module Partitioned
|
|
189
189
|
returning = []
|
190
190
|
|
191
191
|
rows.group_by do |row|
|
192
|
-
respond_to?(:
|
192
|
+
respond_to?(:partition_table_name) ? partition_table_name(*partition_key_values(row)) : table_name
|
193
193
|
end.each do |table_name, rows_for_table|
|
194
194
|
column_names = rows_for_table[0].keys.sort{|a,b| a.to_s <=> b.to_s}
|
195
195
|
rows_for_table.each_slice(options[:slice_size]) do |update_slice|
|
@@ -23,6 +23,11 @@ module Partitioned
|
|
23
23
|
return integer_field_value / partition_table_size * partition_table_size
|
24
24
|
end
|
25
25
|
|
26
|
+
def self.partition_generate_range(start_value, end_value, step = :default)
|
27
|
+
step = partition_table_size if step == :default
|
28
|
+
return Range.new(start_value, end_value).step(step)
|
29
|
+
end
|
30
|
+
|
26
31
|
partitioned do |partition|
|
27
32
|
partition.on lambda {|model| return model.partition_integer_field }
|
28
33
|
|
@@ -40,8 +40,9 @@ module Partitioned
|
|
40
40
|
|
41
41
|
attr_accessor :on_field, :indexes, :foreign_keys, :last_partitions_order_by_clause,
|
42
42
|
:schema_name, :name_prefix, :base_name,
|
43
|
-
:part_name, :table_name, :parent_table_schema_name,
|
44
|
-
:parent_table_name, :check_constraint, :encoded_name
|
43
|
+
:part_name, :table_name, :table_alias_name, :parent_table_schema_name,
|
44
|
+
:parent_table_name, :check_constraint, :encoded_name,
|
45
|
+
:janitorial_creates_needed, :janitorial_archives_needed, :janitorial_drops_needed
|
45
46
|
|
46
47
|
def initialize
|
47
48
|
@on_field = nil
|
@@ -57,6 +58,7 @@ module Partitioned
|
|
57
58
|
@part_name = nil
|
58
59
|
|
59
60
|
@table_name = nil
|
61
|
+
@table_alias_name = nil
|
60
62
|
|
61
63
|
@parent_table_schema_name = nil
|
62
64
|
@parent_table_name = nil
|
@@ -64,6 +66,10 @@ module Partitioned
|
|
64
66
|
@check_constraint = nil
|
65
67
|
|
66
68
|
@encoded_name = nil
|
69
|
+
|
70
|
+
@janitorial_creates_needed = nil
|
71
|
+
@janitorial_archives_needed = nil
|
72
|
+
@janitorial_drops_needed = nil
|
67
73
|
end
|
68
74
|
end
|
69
75
|
end
|
@@ -550,6 +550,11 @@ module Partitioned
|
|
550
550
|
data.table_name = value
|
551
551
|
end
|
552
552
|
|
553
|
+
# a reasonable alias for this table
|
554
|
+
def table_alias_name(value)
|
555
|
+
data.table_alias_name = value
|
556
|
+
end
|
557
|
+
|
553
558
|
#
|
554
559
|
# The table name of the table who is the direct ancestor of a child table.
|
555
560
|
#
|
@@ -627,6 +632,20 @@ module Partitioned
|
|
627
632
|
def parent_table_schema_name(value)
|
628
633
|
data.parent_table_schema_name = value
|
629
634
|
end
|
635
|
+
|
636
|
+
#
|
637
|
+
|
638
|
+
def janitorial_creates_needed(value)
|
639
|
+
data.janitorial_creates_needed = value
|
640
|
+
end
|
641
|
+
|
642
|
+
def janitorial_archives_needed(value)
|
643
|
+
data.janitorial_archives_needed = value
|
644
|
+
end
|
645
|
+
|
646
|
+
def janitorial_drops_needed(value)
|
647
|
+
data.janitorial_drops_needed = value
|
648
|
+
end
|
630
649
|
end
|
631
650
|
end
|
632
651
|
end
|
@@ -26,6 +26,10 @@ module Partitioned
|
|
26
26
|
@parent_table_name = nil
|
27
27
|
|
28
28
|
@encoded_name = nil
|
29
|
+
|
30
|
+
@janitorial_creates_needed = nil
|
31
|
+
@janitorial_archives_needed = nil
|
32
|
+
@janitorial_drops_needed = nil
|
29
33
|
end
|
30
34
|
|
31
35
|
#
|
@@ -100,6 +104,13 @@ module Partitioned
|
|
100
104
|
return collect_first(*partition_key_values, &:table_name)
|
101
105
|
end
|
102
106
|
|
107
|
+
#
|
108
|
+
# A reasonable alias for this partition table
|
109
|
+
#
|
110
|
+
def table_alias_name(*partition_key_values)
|
111
|
+
return collect_first(*partition_key_values, &:table_alias_name)
|
112
|
+
end
|
113
|
+
|
103
114
|
#
|
104
115
|
# The name of the child table without the schema name or name prefix.
|
105
116
|
#
|
@@ -135,6 +146,26 @@ module Partitioned
|
|
135
146
|
return @last_partitions_order_by_clause
|
136
147
|
end
|
137
148
|
|
149
|
+
def janitorial_creates_needed
|
150
|
+
unless @janitorial_creates_needed
|
151
|
+
@janitorial_creates_needed = collect_first(&:janitorial_creates_needed)
|
152
|
+
end
|
153
|
+
return @janitorial_creates_needed
|
154
|
+
end
|
155
|
+
|
156
|
+
def janitorial_archives_needed
|
157
|
+
unless @janitorial_archives_needed
|
158
|
+
@janitorial_archives_needed = collect_first(&:janitorial_archives_needed)
|
159
|
+
end
|
160
|
+
return @janitorial_archives_needed
|
161
|
+
end
|
162
|
+
|
163
|
+
def janitorial_drops_needed
|
164
|
+
unless @janitorial_drops_needed
|
165
|
+
@janitorial_drops_needed = collect_first(&:janitorial_drops_needed)
|
166
|
+
end
|
167
|
+
return @janitorial_drops_needed
|
168
|
+
end
|
138
169
|
|
139
170
|
protected
|
140
171
|
|
@@ -14,6 +14,17 @@ module Partitioned
|
|
14
14
|
@parent_table_class = parent_table_class
|
15
15
|
end
|
16
16
|
|
17
|
+
#
|
18
|
+
# Archive partitions that need such.
|
19
|
+
# uses #archive_old_partition_key_values_set as the list of
|
20
|
+
# partitions to remove.
|
21
|
+
#
|
22
|
+
def archive_old_partitions
|
23
|
+
archive_old_partition_key_values_set.each do |*partition_key_values|
|
24
|
+
archive_old_partition(*partition_key_values)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
17
28
|
#
|
18
29
|
# Drop partitions that are no longer necessary.
|
19
30
|
# uses #old_partition_key_values_set as the list of
|
@@ -64,10 +75,20 @@ module Partitioned
|
|
64
75
|
# Used by #create_new_partitions and generally called once a day to update
|
65
76
|
# the database with new soon-to-be needed child tables.
|
66
77
|
#
|
67
|
-
# Typically overridden by the concrete class as this is pure business logic.
|
68
|
-
#
|
69
78
|
def new_partition_key_values_set
|
70
|
-
|
79
|
+
return configurator.janitorial_creates_needed
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# An array of key values (each key value is an array of keys) that represent
|
84
|
+
# the child partitions that should be archived probably because they are
|
85
|
+
# about to be dropped.
|
86
|
+
#
|
87
|
+
# Used by #archive_old_partitions and generally called once a day to clean up
|
88
|
+
# unneeded child tables.
|
89
|
+
#
|
90
|
+
def archive_old_partition_key_values_set
|
91
|
+
return configurator.janitorial_archives_needed
|
71
92
|
end
|
72
93
|
|
73
94
|
#
|
@@ -77,10 +98,16 @@ module Partitioned
|
|
77
98
|
# Used by #drop_old_partitions and generally called once a day to clean up
|
78
99
|
# unneeded child tables.
|
79
100
|
#
|
80
|
-
# Typically overridden by the concrete class as this is pure business logic.
|
81
|
-
#
|
82
101
|
def old_partition_key_values_set
|
83
|
-
|
102
|
+
return configurator.janitorial_drops_needed
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# Archive a specific partition from the database given
|
107
|
+
# the key value(s) of its check constraint columns.
|
108
|
+
#
|
109
|
+
def archive_old_partition(*partition_key_values)
|
110
|
+
archive_partition_table(*partition_key_values)
|
84
111
|
end
|
85
112
|
|
86
113
|
#
|
@@ -131,10 +158,24 @@ module Partitioned
|
|
131
158
|
# :method: partition_table_name
|
132
159
|
# delegated to Partitioned::PartitionedBase::PartitionManager::SqlAdapter#partition_table_name
|
133
160
|
|
161
|
+
##
|
162
|
+
# :method: partition_table_alias_name
|
163
|
+
# delegated to Partitioned::PartitionedBase::PartitionManager::SqlAdapter#partition_table_alias_name
|
164
|
+
|
165
|
+
##
|
166
|
+
# :method: sql_adapter
|
167
|
+
# delegated to Partitioned::PartitionedBase#sql_adapter
|
168
|
+
|
169
|
+
##
|
170
|
+
# :method: configurator
|
171
|
+
# delegated to Partitioned::PartitionedBase#configurator
|
172
|
+
|
134
173
|
extend Forwardable
|
135
|
-
def_delegators :parent_table_class, :sql_adapter
|
174
|
+
def_delegators :parent_table_class, :sql_adapter, :configurator
|
136
175
|
def_delegators :sql_adapter, :drop_partition_table, :create_partition_table, :add_partition_table_index,
|
137
|
-
:add_references_to_partition_table, :create_partition_schema, :add_parent_table_rules,
|
176
|
+
:add_references_to_partition_table, :create_partition_schema, :add_parent_table_rules,
|
177
|
+
:partition_table_name, :partition_table_alias_name
|
178
|
+
|
138
179
|
end
|
139
180
|
end
|
140
181
|
end
|
@@ -126,6 +126,13 @@ module Partitioned
|
|
126
126
|
return configurator.table_name(*partition_key_values)
|
127
127
|
end
|
128
128
|
|
129
|
+
#
|
130
|
+
# A reasonable alias for the partition table
|
131
|
+
#
|
132
|
+
def partition_table_alias_name(*partition_key_values)
|
133
|
+
return configurator.table_alias_name(*partition_key_values)
|
134
|
+
end
|
135
|
+
|
129
136
|
#
|
130
137
|
# Create a single child table.
|
131
138
|
#
|
@@ -61,7 +61,7 @@ module Partitioned
|
|
61
61
|
# @return [String] the fully qualified name of the database table, ie: foos_partitions.p17
|
62
62
|
def partition_table_name
|
63
63
|
symbolized_attributes = attributes.symbolize_keys
|
64
|
-
return self.class.
|
64
|
+
return self.class.partition_table_name(*self.class.partition_keys.map{|attribute_name| symbolized_attributes[attribute_name]})
|
65
65
|
end
|
66
66
|
|
67
67
|
#
|
@@ -119,7 +119,7 @@ module Partitioned
|
|
119
119
|
new_arel_table = @arel_tables[key_values]
|
120
120
|
arel_engine_hash = {:engine => self.arel_engine}
|
121
121
|
arel_engine_hash[:as] = as unless as.blank?
|
122
|
-
new_arel_table = Arel::Table.new(self.
|
122
|
+
new_arel_table = Arel::Table.new(self.partition_table_name(*key_values), arel_engine_hash)
|
123
123
|
return new_arel_table
|
124
124
|
end
|
125
125
|
|
@@ -135,19 +135,8 @@ module Partitioned
|
|
135
135
|
return self.class.dynamic_arel_table(key_values, as)
|
136
136
|
end
|
137
137
|
|
138
|
-
# :from_partition_scope is generally not used directly,
|
139
|
-
# use helper self.from_partition so that the derived class
|
140
|
-
# can be passed into :from_partition_scope
|
141
138
|
#
|
142
|
-
#
|
143
|
-
scope :from_partition_scope, lambda { |target_class, *partition_field|
|
144
|
-
{
|
145
|
-
:from => "#{target_class.partition_name(*partition_field)} AS #{target_class.table_name}"
|
146
|
-
}
|
147
|
-
}
|
148
|
-
|
149
|
-
#
|
150
|
-
# Real scope (uses #from_partition_scope). This scope is used to target the
|
139
|
+
# This scoping is used to target the
|
151
140
|
# active record find() to a specific child table and alias it to the name of the
|
152
141
|
# parent table (so activerecord can generally work with it)
|
153
142
|
#
|
@@ -157,29 +146,16 @@ module Partitioned
|
|
157
146
|
#
|
158
147
|
# where KEY is the key value(s) used as the check constraint on Foo's table.
|
159
148
|
#
|
160
|
-
# Because the scope is specific to a class (a class method) but unlike
|
161
|
-
# class methods is not inherited, one must use this form (#from_partition) instead
|
162
|
-
# of #from_partition_scope to get the most derived classes specific active record scope.
|
163
|
-
#
|
164
149
|
# @param [*Array<Object>] partition_field the field values to partition on
|
165
150
|
# @return [Hash] the scoping
|
166
151
|
def self.from_partition(*partition_field)
|
167
|
-
|
152
|
+
table_alias_name = partition_table_alias_name(*partition_field)
|
153
|
+
from("#{partition_table_name(*partition_field)} AS #{table_alias_name}").
|
154
|
+
tap{|relation| relation.table.table_alias = table_alias_name}
|
168
155
|
end
|
169
156
|
|
170
|
-
# :from_partitioned_without_alias_scope is generally not used directly,
|
171
|
-
# use helper self.from_partitioned_without_alias so that the derived class
|
172
|
-
# can be passed into :from_partitioned_without_alias_scope
|
173
157
|
#
|
174
|
-
#
|
175
|
-
scope :from_partitioned_without_alias_scope, lambda { |target_class, *partition_field|
|
176
|
-
{
|
177
|
-
:from => target_class.partition_name(*partition_field)
|
178
|
-
}
|
179
|
-
}
|
180
|
-
|
181
|
-
#
|
182
|
-
# Real scope (uses #from_partitioned_without_alias_scope). This scope is used to target the
|
158
|
+
# This scope is used to target the
|
183
159
|
# active record find() to a specific child table. Is probably best used in advanced
|
184
160
|
# activerecord queries when a number of tables are involved in the query.
|
185
161
|
#
|
@@ -205,7 +181,9 @@ module Partitioned
|
|
205
181
|
# @param [*Array<Object>] partition_field the field values to partition on
|
206
182
|
# @return [Hash] the scoping
|
207
183
|
def self.from_partitioned_without_alias(*partition_field)
|
208
|
-
|
184
|
+
table_alias_name = partition_table_name(*partition_field)
|
185
|
+
from(table_alias_name).
|
186
|
+
tap{|relation| relation.table.table_alias = table_alias_name}
|
209
187
|
end
|
210
188
|
|
211
189
|
#
|
@@ -252,8 +230,24 @@ module Partitioned
|
|
252
230
|
#
|
253
231
|
# For a parent table name foos, that would be foos_partitions
|
254
232
|
#
|
233
|
+
# N.B.: if the parent table is not in the default schema ("public") the name of the
|
234
|
+
# partition schema is prefixed by the schema name of the parent table and an
|
235
|
+
# underscore. That is, if a parent table schema/table name is "other.foos"
|
236
|
+
# the schema for its partitions will be "other_foos_partitions"
|
237
|
+
#
|
255
238
|
partition.schema_name lambda {|model|
|
256
|
-
|
239
|
+
schema_parts = []
|
240
|
+
table_parts = model.table_name.split('.')
|
241
|
+
# table_parts should be either ["table_name"] or ["schema_name", "table_name"]
|
242
|
+
if table_parts.length == 2
|
243
|
+
# XXX should we find the schema_path here and accept anything in the path as "public"
|
244
|
+
unless table_parts.first == "public"
|
245
|
+
schema_parts << table_parts.first
|
246
|
+
end
|
247
|
+
end
|
248
|
+
schema_parts << table_parts.last
|
249
|
+
schema_parts << 'partitions'
|
250
|
+
return schema_parts.join('_')
|
257
251
|
}
|
258
252
|
|
259
253
|
#
|
@@ -272,7 +266,9 @@ module Partitioned
|
|
272
266
|
# The child table is defined by the partition key values passed in.
|
273
267
|
#
|
274
268
|
partition.parent_table_schema_name lambda {|model, *partition_key_values|
|
275
|
-
|
269
|
+
table_parts = model.table_name.split('.')
|
270
|
+
# table_parts should be either ["table_name"] or ["schema_name", "table_name"]
|
271
|
+
return table_parts.first if table_parts.length == 2
|
276
272
|
return "public"
|
277
273
|
}
|
278
274
|
|
@@ -308,6 +304,13 @@ module Partitioned
|
|
308
304
|
return "#{configurator.schema_name}.#{configurator.part_name(*partition_key_values)}"
|
309
305
|
}
|
310
306
|
|
307
|
+
#
|
308
|
+
# A reasonable alias for this table
|
309
|
+
#
|
310
|
+
partition.table_alias_name lambda {|model, *partition_key_values|
|
311
|
+
return model.configurator.parent_table_name(*partition_key_values).gsub('.', '_')
|
312
|
+
}
|
313
|
+
|
311
314
|
#
|
312
315
|
# The name of the child table without a schema name or prefix. this is used to
|
313
316
|
# build child table names for multi-level partitions.
|
@@ -366,6 +369,13 @@ module Partitioned
|
|
366
369
|
partition_manager.add_parent_table_rules(*partition_key_values)
|
367
370
|
end
|
368
371
|
|
372
|
+
##
|
373
|
+
# :method: archive_old_partitions
|
374
|
+
# delegated to Partitioned::PartitionedBase::PartitionManager#archive_old_partitions
|
375
|
+
def self.archive_old_partitions
|
376
|
+
partition_manager.archive_old_partitions
|
377
|
+
end
|
378
|
+
|
369
379
|
##
|
370
380
|
# :method: drop_old_partitions
|
371
381
|
# delegated to Partitioned::PartitionedBase::PartitionManager#drop_old_partitions
|
@@ -380,6 +390,13 @@ module Partitioned
|
|
380
390
|
partition_manager.create_new_partitions
|
381
391
|
end
|
382
392
|
|
393
|
+
##
|
394
|
+
# :method: archive_old_partition
|
395
|
+
# delegated to Partitioned::PartitionedBase::PartitionManager#archive_old_partition
|
396
|
+
def self.archive_old_partition(*partition_key_values)
|
397
|
+
partition_manager.archive_old_partition(*partition_key_values)
|
398
|
+
end
|
399
|
+
|
383
400
|
##
|
384
401
|
# :method: drop_old_partition
|
385
402
|
# delegated to Partitioned::PartitionedBase::PartitionManager#drop_old_partition
|
@@ -421,5 +438,12 @@ module Partitioned
|
|
421
438
|
def self.partition_name(*partition_key_values)
|
422
439
|
return partition_manager.partition_table_name(*partition_key_values)
|
423
440
|
end
|
441
|
+
|
442
|
+
##
|
443
|
+
# :method: partition_table_alias_name
|
444
|
+
# delegated to Partitioned::PartitionedBase::PartitionManager#partition_table_alias_name
|
445
|
+
def self.partition_table_alias_name(*partition_key_values)
|
446
|
+
return partition_manager.partition_table_alias_name(*partition_key_values)
|
447
|
+
end
|
424
448
|
end
|
425
449
|
end
|
data/lib/partitioned/version.rb
CHANGED
data/partitioned.gemspec
CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.name = 'partitioned'
|
8
8
|
s.version = Partitioned::VERSION
|
9
9
|
s.license = 'New BSD License'
|
10
|
-
s.date = '2012-
|
10
|
+
s.date = '2012-09-11'
|
11
11
|
s.summary = "Postgres table partitioning support for ActiveRecord."
|
12
12
|
s.description = "A gem providing support for table partitioning in ActiveRecord. Support is only available for postgres databases. Other features include child table management (creation and deletion) and bulk data creating and updating."
|
13
13
|
s.authors = ["Keith Gabryelski", "Aleksandr Dembskiy"]
|
@@ -45,12 +45,12 @@ module ActiveRecord::ConnectionAdapters
|
|
45
45
|
describe "next_sequence_value" do
|
46
46
|
|
47
47
|
it "returns next_sequence_value" do
|
48
|
-
ActiveRecord::Base.connection.next_sequence_value(Employee.sequence_name).should ==
|
48
|
+
ActiveRecord::Base.connection.next_sequence_value(Employee.sequence_name).should == 1
|
49
49
|
ActiveRecord::Base.connection.execute <<-SQL
|
50
50
|
insert into employees(name, company_id) values ('Nikita', 1);
|
51
51
|
SQL
|
52
|
-
ActiveRecord::Base.connection.next_sequence_value(Employee.sequence_name).should ==
|
53
|
-
ActiveRecord::Base.connection.next_sequence_value(Employee.sequence_name).should ==
|
52
|
+
ActiveRecord::Base.connection.next_sequence_value(Employee.sequence_name).should == 3
|
53
|
+
ActiveRecord::Base.connection.next_sequence_value(Employee.sequence_name).should == 4
|
54
54
|
end
|
55
55
|
|
56
56
|
end # next_sequence_value
|
@@ -173,4 +173,4 @@ module ActiveRecord::ConnectionAdapters
|
|
173
173
|
|
174
174
|
end # PostgreSQLAdapter
|
175
175
|
|
176
|
-
end # ActiveRecord::ConnectionAdapters
|
176
|
+
end # ActiveRecord::ConnectionAdapters
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: partitioned
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-
|
13
|
+
date: 2012-09-11 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: pg
|
@@ -175,18 +175,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
175
175
|
- - ! '>='
|
176
176
|
- !ruby/object:Gem::Version
|
177
177
|
version: '0'
|
178
|
-
segments:
|
179
|
-
- 0
|
180
|
-
hash: -2034428678854769428
|
181
178
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
182
179
|
none: false
|
183
180
|
requirements:
|
184
181
|
- - ! '>='
|
185
182
|
- !ruby/object:Gem::Version
|
186
183
|
version: '0'
|
187
|
-
segments:
|
188
|
-
- 0
|
189
|
-
hash: -2034428678854769428
|
190
184
|
requirements: []
|
191
185
|
rubyforge_project:
|
192
186
|
rubygems_version: 1.8.24
|