partitioned 1.1.1 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -23,9 +23,13 @@ module Partitioned
23
23
  {
24
24
  "on_field" => nil,
25
25
  "indexes" => [],
26
+ "janitorial_archives_needed" => nil,
27
+ "janitorial_creates_needed" => nil,
28
+ "janitorial_drops_needed" => nil,
26
29
  "foreign_keys" => [],
27
30
  "last_partitions_order_by_clause" => nil,
28
31
  "schema_name" => nil,
32
+ "table_alias_name" => nil,
29
33
  "name_prefix" => nil,
30
34
  "base_name" => nil,
31
35
  "part_name" => nil,
@@ -121,7 +121,7 @@ shared_examples_for "check that basic operations with postgres works correctly f
121
121
  context "when try to create new record outside the range of partitions" do
122
122
 
123
123
  it "raises ActiveRecord::StatementInvalid" do
124
- lambda { subject.create_many([{ :created_at => DATE_NOW + 1.year, :company_id => 1 }])
124
+ lambda { subject.create_many([{ :created_at => DATE_NOW - 1.year, :company_id => 1 }])
125
125
  }.should raise_error(ActiveRecord::StatementInvalid)
126
126
  end
127
127
 
@@ -130,7 +130,7 @@ shared_examples_for "check that basic operations with postgres works correctly f
130
130
  context "when try to update a record outside the range of partitions" do
131
131
 
132
132
  it "raises ActiveRecord::StatementInvalid" do
133
- lambda { subject.update(1, :name => 'Kevin', :created_at => DATE_NOW + 1.year)
133
+ lambda { subject.update(1, :name => 'Kevin', :created_at => DATE_NOW - 1.year)
134
134
  }.should raise_error(ActiveRecord::StatementInvalid)
135
135
  end
136
136
 
@@ -139,7 +139,7 @@ shared_examples_for "check that basic operations with postgres works correctly f
139
139
  context "when try to find a record outside the range of partitions" do
140
140
 
141
141
  it "raises ActiveRecord::StatementInvalid" do
142
- lambda { subject.from_partition(DATE_NOW + 1.year).find(1)
142
+ lambda { subject.from_partition(DATE_NOW - 1.year).find(1)
143
143
  }.should raise_error(ActiveRecord::StatementInvalid)
144
144
  end
145
145
 
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  module TablesSpecHelper
4
4
 
5
5
  class Company < ActiveRecord::Base
6
- extend Partitioned::BulkMethodsMixin
6
+ extend BulkMethodsMixin
7
7
  has_many :employees, :class_name => 'Company', :conditions => "companies.id = employees.companies_id"
8
8
  end
9
9
 
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.1.1
4
+ version: 1.1.3
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-09-13 00:00:00.000000000 Z
13
+ date: 2013-10-14 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: pg
@@ -60,6 +60,22 @@ dependencies:
60
60
  - - ! '>='
61
61
  - !ruby/object:Gem::Version
62
62
  version: '0'
63
+ - !ruby/object:Gem::Dependency
64
+ name: bulk_data_methods
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - '='
69
+ - !ruby/object:Gem::Version
70
+ version: 1.0.0
71
+ type: :runtime
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - '='
77
+ - !ruby/object:Gem::Version
78
+ version: 1.0.0
63
79
  description: A gem providing support for table partitioning in ActiveRecord. Support
64
80
  is only available for postgres databases. Other features include child table management
65
81
  (creation and deletion) and bulk data creating and updating.
@@ -68,7 +84,9 @@ executables: []
68
84
  extensions: []
69
85
  extra_rdoc_files: []
70
86
  files:
87
+ - .rspec
71
88
  - Gemfile
89
+ - Gemfile.lock
72
90
  - LICENSE
73
91
  - PARTITIONING_EXPLAINED.txt
74
92
  - README
@@ -90,7 +108,6 @@ files:
90
108
  - lib/monkey_patch_postgres.rb
91
109
  - lib/partitioned.rb
92
110
  - lib/partitioned/active_record_overrides.rb
93
- - lib/partitioned/bulk_methods_mixin.rb
94
111
  - lib/partitioned/by_created_at.rb
95
112
  - lib/partitioned/by_foreign_key.rb
96
113
  - lib/partitioned/by_id.rb
@@ -98,6 +115,7 @@ files:
98
115
  - lib/partitioned/by_monthly_time_field.rb
99
116
  - lib/partitioned/by_time_field.rb
100
117
  - lib/partitioned/by_weekly_time_field.rb
118
+ - lib/partitioned/by_yearly_time_field.rb
101
119
  - lib/partitioned/multi_level.rb
102
120
  - lib/partitioned/multi_level/configurator/data.rb
103
121
  - lib/partitioned/multi_level/configurator/dsl.rb
@@ -144,7 +162,6 @@ files:
144
162
  - spec/dummy/script/rails
145
163
  - spec/dummy/spec/spec_helper.rb
146
164
  - spec/monkey_patch_posgres_spec.rb
147
- - spec/partitioned/bulk_methods_mixin_spec.rb
148
165
  - spec/partitioned/by_created_at_spec.rb
149
166
  - spec/partitioned/by_foreign_key_spec.rb
150
167
  - spec/partitioned/by_id_spec.rb
@@ -152,6 +169,7 @@ files:
152
169
  - spec/partitioned/by_monthly_time_field_spec.rb
153
170
  - spec/partitioned/by_time_field_spec.rb
154
171
  - spec/partitioned/by_weekly_time_field_spec.rb
172
+ - spec/partitioned/by_yearly_time_field_spec.rb
155
173
  - spec/partitioned/multi_level/configurator/dsl_spec.rb
156
174
  - spec/partitioned/multi_level/configurator/reader_spec.rb
157
175
  - spec/partitioned/partitioned_base/configurator/dsl_spec.rb
@@ -219,7 +237,6 @@ test_files:
219
237
  - spec/dummy/script/rails
220
238
  - spec/dummy/spec/spec_helper.rb
221
239
  - spec/monkey_patch_posgres_spec.rb
222
- - spec/partitioned/bulk_methods_mixin_spec.rb
223
240
  - spec/partitioned/by_created_at_spec.rb
224
241
  - spec/partitioned/by_foreign_key_spec.rb
225
242
  - spec/partitioned/by_id_spec.rb
@@ -227,6 +244,7 @@ test_files:
227
244
  - spec/partitioned/by_monthly_time_field_spec.rb
228
245
  - spec/partitioned/by_time_field_spec.rb
229
246
  - spec/partitioned/by_weekly_time_field_spec.rb
247
+ - spec/partitioned/by_yearly_time_field_spec.rb
230
248
  - spec/partitioned/multi_level/configurator/dsl_spec.rb
231
249
  - spec/partitioned/multi_level/configurator/reader_spec.rb
232
250
  - spec/partitioned/partitioned_base/configurator/dsl_spec.rb
@@ -1,233 +0,0 @@
1
- module Partitioned
2
- #
3
- # MixIn used to extend ActiveRecord::Base classes implementing bulk insert and update operations
4
- # through {#create_many} and {#update_many}. {Partitioned::PartitionedBase} classes are extended
5
- # with these methods by default.
6
- #
7
- # @example to use in non-{Partitioned::PartitionedBase} class:
8
- # class Company < ActiveRecord::Base
9
- # extend Partitioned::BulkMethodsMixin
10
- # end
11
- #
12
- module BulkMethodsMixin
13
- # exception thrown when row data structures are inconsistent between rows in single call to {#create_many} or {#update_many}
14
- class BulkUploadDataInconsistent < StandardError
15
- def initialize(model, table_name, expected_columns, found_columns, while_doing)
16
- super("#{model.name}: for table: #{table_name}; #{expected_columns} != #{found_columns}; #{while_doing}")
17
- end
18
- end
19
-
20
- # BULK creation of many rows
21
- #
22
- # @example no options used
23
- # rows = [
24
- # { :name => 'Keith', :salary => 1000 },
25
- # { :name => 'Alex', :salary => 2000 }
26
- # ]
27
- # Employee.create_many(rows)
28
- #
29
- # @example with :returning option to returns key value
30
- # rows = [
31
- # { :name => 'Keith', :salary => 1000 },
32
- # { :name => 'Alex', :salary => 2000 }
33
- # ]
34
- # options = { :returning => [:id] }
35
- # Employee.create_many(rows, options)
36
- # [#<Employee id: 1>, #<Employee id: 2>]
37
- #
38
- # @example with :slice_size option (will generate two insert queries)
39
- # rows = [
40
- # { :name => 'Keith', :salary => 1000 },
41
- # { :name => 'Alex', :salary => 2000 },
42
- # { :name => 'Mark', :salary => 3000 }
43
- # ]
44
- # options = { :slice_size => 2 }
45
- # Employee.create_many(rows, options)
46
- #
47
- # @param [Array<Hash>] rows ([]) data to be inserted into database
48
- # @param [Hash] options ({}) options for bulk inserts
49
- # @option options [Integer] :slice_size (1000) how many records will be created in a single SQL query
50
- # @option options [Boolean] :check_consitency (true) ensure some modicum of sanity on the incoming dataset, specifically: does each row define the same set of key/value pairs
51
- # @option options [Array or String] :returning (nil) list of fields to return.
52
- # @return [Array<Hash>] rows returned from DB as option[:returning] requests
53
- # @raise [BulkUploadDataInconsistent] raised when key/value pairs between rows are inconsistent (check disabled with option :check_consistency)
54
- def create_many(rows, options = {})
55
- return [] if rows.blank?
56
- options[:slice_size] = 1000 unless options.has_key?(:slice_size)
57
- options[:check_consistency] = true unless options.has_key?(:check_consistency)
58
- returning_clause = ""
59
- if options[:returning]
60
- if options[:returning].is_a? Array
61
- returning_list = options[:returning].join(',')
62
- else
63
- returning_list = options[:returning]
64
- end
65
- returning_clause = " returning #{returning_list}"
66
- end
67
- returning = []
68
-
69
- created_at_value = Time.zone.now
70
-
71
- num_sequences_needed = rows.reject{|r| r[:id].present?}.length
72
- if num_sequences_needed > 0
73
- row_ids = connection.next_sequence_values(sequence_name, num_sequences_needed)
74
- else
75
- row_ids = []
76
- end
77
- rows.each do |row|
78
- # set the primary key if it needs to be set
79
- row[:id] ||= row_ids.shift
80
- end.each do |row|
81
- # set :created_at if need be
82
- row[:created_at] ||= created_at_value
83
- end.group_by do |row|
84
- respond_to?(:partition_table_name) ? partition_table_name(*partition_key_values(row)) : table_name
85
- end.each do |table_name, rows_for_table|
86
- column_names = rows_for_table[0].keys.sort{|a,b| a.to_s <=> b.to_s}
87
- sql_insert_string = "insert into #{table_name} (#{column_names.join(',')}) values "
88
- rows_for_table.map do |row|
89
- if options[:check_consistency]
90
- row_column_names = row.keys.sort{|a,b| a.to_s <=> b.to_s}
91
- if column_names != row_column_names
92
- raise BulkUploadDataInconsistent.new(self, table_name, column_names, row_column_names, "while attempting to build insert statement")
93
- end
94
- end
95
- column_values = column_names.map do |column_name|
96
- quote_value(row[column_name], columns_hash[column_name.to_s])
97
- end.join(',')
98
- "(#{column_values})"
99
- end.each_slice(options[:slice_size]) do |insert_slice|
100
- returning += find_by_sql(sql_insert_string + insert_slice.join(',') + returning_clause)
101
- end
102
- end
103
- return returning
104
- end
105
-
106
- #
107
- # BULK updates of many rows
108
- #
109
- # @return [Array<Hash>] rows returned from DB as option[:returning] requests
110
- # @raise [BulkUploadDataInconsistent] raised when key/value pairs between rows are inconsistent (check disabled with option :check_consistency)
111
- # @param [Hash] options ({}) options for bulk inserts
112
- # @option options [Integer] :slice_size (1000) how many records will be created in a single SQL query
113
- # @option options [Boolean] :check_consitency (true) ensure some modicum of sanity on the incoming dataset, specifically: does each row define the same set of key/value pairs
114
- # @option options [Array] :returning (nil) list of fields to return.
115
- # @option options [String] :returning (nil) single field to return.
116
- #
117
- # @overload update_many(rows = [], options = {})
118
- # @param [Array<Hash>] rows ([]) data to be updated
119
- # @option options [String] :set_array (built from first row passed in) the set clause
120
- # @option options [String] :where ('"#{table_name}.id = datatable.id"') the where clause
121
- #
122
- # @overload update_many(rows = {}, options = {})
123
- # @param [Hash<Hash, Hash>] rows ({}) data to be updated
124
- # @option options [String] :set_array (built from the values in the first key/value pair of `rows`) the set clause
125
- # @option options [String] :where (built from the keys in the first key/value pair of `rows`) the where clause
126
- #
127
- # @example using "set_array" to add the value of "salary" to the specific employee's salary the default where clause matches IDs so, it works here.
128
- # rows = [
129
- # { :id => 1, :salary => 1000 },
130
- # { :id => 10, :salary => 2000 },
131
- # { :id => 23, :salary => 2500 }
132
- # ]
133
- # options = { :set_array => '"salary = datatable.salary"' }
134
- # Employee.update_many(rows, options)
135
- #
136
- # @example using where clause to match salary.
137
- # rows = [
138
- # { :id => 1, :salary => 1000, :company_id => 10 },
139
- # { :id => 10, :salary => 2000, :company_id => 12 },
140
- # { :id => 23, :salary => 2500, :company_id => 5 }
141
- # ]
142
- # options = {
143
- # :set_array => '"company_id = datatable.company_id"',
144
- # :where => '"#{table_name}.salary = datatable.salary"'
145
- # }
146
- # Employee.update_many(rows, options)
147
- #
148
- # @example setting where clause to the KEY of the hash passed in and the set_array is generated from the VALUES
149
- # rows = {
150
- # { :id => 1 } => { :salary => 100000, :company_id => 10 },
151
- # { :id => 10 } => { :salary => 110000, :company_id => 12 },
152
- # { :id => 23 } => { :salary => 90000, :company_id => 5 }
153
- # }
154
- # Employee.update_many(rows)
155
- #
156
- # @note Remember that you should probably set updated_at using "updated = datatable.updated_at"
157
- # or "updated_at = now()" in the set_array if you want to follow
158
- # the standard active record model for time columns (and you have an updated_at column)
159
- def update_many(rows, options = {})
160
- return [] if rows.blank?
161
- if rows.is_a?(Hash)
162
- options[:where] = '"' + rows.keys[0].keys.map{|key| '#{table_name}.' + "#{key} = datatable.#{key}"}.join(' and ') + '"'
163
- options[:set_array] = '"' + rows.values[0].keys.map{|key| "#{key} = datatable.#{key}"}.join(',') + '"' unless options[:set_array]
164
- r = []
165
- rows.each do |key,value|
166
- r << key.merge(value)
167
- end
168
- rows = r
169
- end
170
- unless options[:set_array]
171
- column_names = rows[0].keys
172
- columns_to_remove = [:id]
173
- columns_to_remove += [partition_keys].map{|k| k.to_sym} if respond_to?(:partition_keys)
174
- options[:set_array] = '"' + (column_names - columns_to_remove.flatten).map{|cn| "#{cn} = datatable.#{cn}"}.join(',') + '"'
175
- end
176
- options[:slice_size] = 1000 unless options[:slice_size]
177
- options[:check_consistency] = true unless options.has_key?(:check_consistency)
178
- returning_clause = ""
179
- if options[:returning]
180
- if options[:returning].is_a?(Array)
181
- returning_list = options[:returning].map{|r| '#{table_name}.' + r.to_s}.join(',')
182
- else
183
- returning_list = options[:returning]
184
- end
185
- returning_clause = "\" returning #{returning_list}\""
186
- end
187
- options[:where] = '"#{table_name}.id = datatable.id"' unless options[:where]
188
-
189
- returning = []
190
-
191
- rows.group_by do |row|
192
- respond_to?(:partition_table_name) ? partition_table_name(*partition_key_values(row)) : table_name
193
- end.each do |table_name, rows_for_table|
194
- column_names = rows_for_table[0].keys.sort{|a,b| a.to_s <=> b.to_s}
195
- rows_for_table.each_slice(options[:slice_size]) do |update_slice|
196
- datatable_rows = []
197
- update_slice.each_with_index do |row,i|
198
- if options[:check_consistency]
199
- row_column_names = row.keys.sort{|a,b| a.to_s <=> b.to_s}
200
- if column_names != row_column_names
201
- raise BulkUploadDataInconsistent.new(self, table_name, column_names, row_column_names, "while attempting to build update statement")
202
- end
203
- end
204
- datatable_rows << row.map do |column_name,column_value|
205
- column_name = column_name.to_s
206
- columns_hash_value = columns_hash[column_name]
207
- if i == 0
208
- "#{quote_value(column_value, columns_hash_value)}::#{columns_hash_value.sql_type} as #{column_name}"
209
- else
210
- quote_value(column_value, columns_hash_value)
211
- end
212
- end.join(',')
213
- end
214
- datatable = datatable_rows.join(' union select ')
215
-
216
- sql_update_string = <<-SQL
217
- update #{table_name} set
218
- #{eval(options[:set_array])}
219
- from
220
- (select
221
- #{datatable}
222
- ) as datatable
223
- where
224
- #{eval(options[:where])}
225
- #{eval(returning_clause)}
226
- SQL
227
- returning += find_by_sql(sql_update_string)
228
- end
229
- end
230
- return returning
231
- end
232
- end
233
- end
@@ -1,512 +0,0 @@
1
- require 'spec_helper'
2
- require "#{File.dirname(__FILE__)}/../support/tables_spec_helper"
3
-
4
- module Partitioned
5
- module BulkMethodsMixin
6
- describe "BulkMethodsMixin" do
7
- include TablesSpecHelper
8
-
9
- before do
10
- class Employee < ActiveRecord::Base
11
- include ActiveRecordOverrides
12
- extend Partitioned::BulkMethodsMixin
13
- end
14
- create_tables
15
- end
16
-
17
- describe "create_many" do
18
-
19
- context "when call method with empty rows" do
20
- it "returns empty array" do
21
- Employee.create_many("").should == []
22
- end
23
- end # when call method with empty rows
24
-
25
- context "when try to create records with the given id" do
26
- it "records created" do
27
- Employee.create_many([{ :id => Employee.connection.next_sequence_value(Employee.sequence_name),
28
- :name => 'Keith',
29
- :company_id => 2
30
- },
31
- { :id => Employee.connection.next_sequence_value(Employee.sequence_name),
32
- :name => 'Mike',
33
- :company_id => 3
34
- },
35
- { :id => Employee.connection.next_sequence_value(Employee.sequence_name),
36
- :name => 'Alex',
37
- :company_id => 1
38
- }])
39
- Employee.all.map{ |r| r.name }.should == ["Keith", "Mike", "Alex"]
40
- end
41
- end # when try to create records with the given id
42
-
43
- context "when try to create records without the given id" do
44
- it "records created" do
45
- Employee.create_many([{ :name => 'Keith', :company_id => 2 },
46
- { :name => 'Mike', :company_id => 3 },
47
- { :name => 'Alex', :company_id => 1 }])
48
- Employee.all.map{ |r| r.name }.should == ["Keith", "Mike", "Alex"]
49
- end
50
- end # when try to create records without the given id
51
-
52
- context "when try to create records with a mixture of given ids and non-given ids" do
53
- it "records created" do
54
- Employee.create_many([{ :name => 'Keith', :company_id => 2 },
55
- { :id => Employee.connection.next_sequence_value(Employee.sequence_name),
56
- :name => 'Mike',
57
- :company_id => 3
58
- },
59
- { :name => 'Mark', :company_id => 1 },
60
- { :id => Employee.connection.next_sequence_value(Employee.sequence_name),
61
- :name => 'Alex',
62
- :company_id => 1
63
- }])
64
- Employee.all.map{ |r| r.name }.should == ["Keith", "Mike", "Mark", "Alex"]
65
- end
66
- end # when try to create records with a mixture of given ids and non-given ids
67
-
68
- context "when try to create records with the given created_at" do
69
- it "records created" do
70
- Employee.create_many([{ :name => 'Keith',
71
- :company_id => 2,
72
- :created_at => Time.zone.parse('2012-01-02')
73
- },
74
- { :name => 'Mike',
75
- :company_id => 3,
76
- :created_at => Time.zone.parse('2012-01-03')
77
- },
78
- { :name => 'Alex',
79
- :company_id => 1,
80
- :created_at => Time.zone.parse('2012-01-04')
81
- }])
82
- Employee.all.map{ |r| r.created_at }.should == [
83
- Time.zone.parse('2012-01-02'),
84
- Time.zone.parse('2012-01-03'),
85
- Time.zone.parse('2012-01-04')
86
- ]
87
- end
88
- end # when try to create records with the given created_at
89
-
90
- context "when try to create records without the given created_at" do
91
- it "records created" do
92
- Employee.create_many([{ :name => 'Keith', :company_id => 2 },
93
- { :name => 'Mike', :company_id => 3 },
94
- { :name => 'Alex', :company_id => 1 }])
95
- Employee.all.each{ |r| r.created_at.between?(Time.now - 3.minute, Time.now + 3.minute) }.
96
- should be_true
97
- end
98
- end # when try to create records without the given created_at
99
-
100
- context "when try to create records without options" do
101
- it "generates one insert queries" do
102
- Employee.should_receive(:find_by_sql).once.and_return([])
103
- Employee.create_many([{ :name => 'Keith', :company_id => 2 },
104
- { :name => 'Alex', :company_id => 1 },
105
- { :name => 'Mark', :company_id => 2 },
106
- { :name => 'Phil', :company_id => 3 }])
107
- end
108
- end # when try to create records without options
109
-
110
- context "when call method with option 'slice_size' equal 2" do
111
- it "generates two insert queries" do
112
- Employee.should_receive(:find_by_sql).twice.and_return([])
113
- Employee.create_many([{ :name => 'Keith', :company_id => 2 },
114
- { :name => 'Alex', :company_id => 1 },
115
- { :name => 'Mark', :company_id => 2 },
116
- { :name => 'Phil', :company_id => 3 }],
117
- { :slice_size => 2})
118
- end
119
- end # when call method with option 'slice_size' equal 2
120
-
121
- context "when create two records with options 'returning' equal id" do
122
- it "returns last records id" do
123
- Employee.create_many([{ :name => 'Keith', :company_id => 2 },
124
- { :name => 'Alex', :company_id => 3 }],
125
- { :returning => [:id] }).
126
- last.id.should == 2
127
- end
128
- end # when create two records with options 'returning' equal id
129
-
130
- context "when try to create two records and doesn't
131
- the same number of keys and options check_consistency equal false" do
132
- it "records created, last salary is nil" do
133
- Employee.create_many([{ :company_id => 2, :name => 'Keith', :salary => 1002 },
134
- { :name => 'Alex', :company_id => 3 }],
135
- { :check_consistency => false })
136
- Employee.find(2).salary.should == nil
137
- end
138
- end # when try to create two records and doesn't
139
- # the same number of keys and options check_consistency equal false
140
-
141
- context "when try to create two records and doesn't the same number of keys" do
142
- it "raises BulkUploadDataInconsistent" do
143
- lambda { Employee.create_many([{ :company_id => 2, :name => 'Keith', :salary => 1002 },
144
- { :name => 'Alex', :company_id => 3}])
145
- }.should raise_error(BulkUploadDataInconsistent)
146
- end
147
- end # when try to create two records and doesn't the same number of keys
148
-
149
- context "when try to create records using partitioning" do
150
-
151
- before do
152
- Partitioned::BulkMethodsMixin.send(:remove_const, :Employee)
153
- class Employee < ByForeignKey
154
- belongs_to :company, :class_name => 'Company'
155
-
156
- def self.partition_foreign_key
157
- return :company_id
158
- end
159
-
160
- partitioned do |partition|
161
- partition.index :id, :unique => true
162
- partition.foreign_key :company_id
163
- end
164
- end # Employee
165
- Employee.create_new_partition_tables(Employee.partition_generate_range(0, 4, 1))
166
- end
167
-
168
- after do
169
- Partitioned::BulkMethodsMixin.send(:remove_const, :Employee)
170
- end
171
-
172
- it "returns records" do
173
- Employee.create_many([{ :name => 'Keith', :company_id => 2 },
174
- { :name => 'Alex', :company_id => 1 },
175
- { :name => 'Phil', :company_id => 3 }])
176
- Employee.from_partition(1).where(:id => 2).first.name.should == "Alex"
177
- Employee.where(:id => 1, :company_id => 2).first.name.should == "Keith"
178
- Employee.all.map{ |r| r.name }.should == ["Alex", "Keith", "Phil"]
179
- end
180
- end # when try to create records using partitioning
181
-
182
- context "when try to create records in the table that has all the different sql types" do
183
-
184
- before do
185
- ActiveRecord::Base.connection.execute <<-SQL
186
- ALTER TABLE employees ADD COLUMN test_string character varying;
187
- ALTER TABLE employees ADD COLUMN test_float float;
188
- ALTER TABLE employees ADD COLUMN test_decimal decimal;
189
- ALTER TABLE employees ADD COLUMN test_time time;
190
- ALTER TABLE employees ADD COLUMN test_time_string time;
191
- ALTER TABLE employees ADD COLUMN test_date date;
192
- ALTER TABLE employees ADD COLUMN test_date_string date;
193
- ALTER TABLE employees ADD COLUMN test_bytea bytea;
194
- ALTER TABLE employees ADD COLUMN test_boolean boolean;
195
- ALTER TABLE employees ADD COLUMN test_xml xml;
196
- ALTER TABLE employees ADD COLUMN test_tsvector tsvector;
197
- SQL
198
- Employee.reset_column_information
199
- end
200
-
201
- after do
202
- ActiveRecord::Base.connection.reset!
203
- end
204
-
205
- context "non-null values" do
206
- it "returns record with all sql types" do
207
- lambda { Employee.create_many([{ :name => 'Keith',
208
- :company_id => 2,
209
- :created_at => Time.zone.parse('2012-12-21'),
210
- :updated_at => '2012-12-21 00:00:00',
211
- :test_string => "string",
212
- :test_float => 12.34,
213
- :test_decimal => 123456789101112,
214
- :test_time => Time.now,
215
- :test_time_string => '00:00:00',
216
- :test_date => Date.parse('2012-12-21'),
217
- :test_date_string => '2012-12-21',
218
- :test_bytea => "text".bytes.to_a,
219
- :test_boolean => false,
220
- :test_xml => ["text"].to_xml,
221
- :test_tsvector => "test string",
222
- }]) }.should_not raise_error
223
- Employee.all.size.should == 1
224
- end
225
- end # non-null values
226
-
227
- context "null values" do
228
- it "returns record with all sql types" do
229
- lambda { Employee.create_many([{ :name => 'Keith',
230
- :company_id => 2,
231
- :created_at => nil,
232
- :updated_at => nil,
233
- :salary => nil,
234
- :test_string => nil,
235
- :test_float => nil,
236
- :test_decimal => nil,
237
- :test_time => nil,
238
- :test_time_string => nil,
239
- :test_date => nil,
240
- :test_date_string => nil,
241
- :test_bytea => nil,
242
- :test_boolean => nil,
243
- :test_xml => nil,
244
- :test_tsvector => nil,
245
- }]) }.should_not raise_error
246
- Employee.all.size.should == 1
247
- end
248
- end # null values
249
-
250
- end # when try to create records in the table that has all the different sql types
251
-
252
- end # create_many
253
-
254
- describe "update_many" do
255
-
256
- before do
257
- Employee.create_many([{ :name => 'Keith', :company_id => 2 },
258
- { :name => 'Alex', :company_id => 1 },
259
- { :name => 'Mark', :company_id => 2 },
260
- { :name => 'Phil', :company_id => 3 }])
261
- end
262
-
263
- context "when call method with empty rows" do
264
- it "returns empty array" do
265
- Employee.update_many("").should == []
266
- end
267
- end # when call method with empty rows
268
-
269
- context "when try to update records without options" do
270
-
271
- context "input parameters is hash" do
272
- it "records updated" do
273
- Employee.update_many({ { :id => 1 } => {
274
- :name => 'Elvis'
275
- },
276
- { :id => 2 } => {
277
- :name => 'Freddi'
278
- } })
279
- Employee.find(1).name.should == "Elvis"
280
- Employee.find(2).name.should == "Freddi"
281
- end
282
- end # input parameters is hash
283
-
284
- context "input parameters is array" do
285
- it "records updated" do
286
- Employee.update_many([{ :id => 1,
287
- :name => 'Elvis'
288
- },
289
- { :id => 2,
290
- :name => 'Freddi'
291
- }])
292
- Employee.find(1).name.should == "Elvis"
293
- Employee.find(2).name.should == "Freddi"
294
- end
295
- end # input parameters is array
296
-
297
- context "when try to update two records and doesn't the same number of keys" do
298
- it "raises BulkUploadDataInconsistent" do
299
- lambda { Employee.update_many([{ :id => 1, :name => 'Elvis', :salary => 1002 },
300
- { :name => 'Freddi', :id => 2}])
301
- }.should raise_error(BulkUploadDataInconsistent)
302
- end
303
- end # when try to update two records and doesn't the same number of keys
304
-
305
- context "when try to update records with the given updated_at" do
306
- it "records created" do
307
- Employee.update_many([{ :id => 1,
308
- :updated_at => Time.zone.parse('2012-01-02')
309
- },
310
- { :id => 2,
311
- :updated_at => Time.zone.parse('2012-01-03')
312
- },
313
- { :id => 3,
314
- :updated_at => Time.zone.parse('2012-01-04')
315
- },
316
- { :id => 4,
317
- :updated_at => Time.zone.parse('2012-01-05')
318
- }])
319
- Employee.all.map{ |r| r.updated_at }.should == [
320
- Time.zone.parse('2012-01-02'),
321
- Time.zone.parse('2012-01-03'),
322
- Time.zone.parse('2012-01-04'),
323
- Time.zone.parse('2012-01-05')
324
- ]
325
- end
326
- end # when try to update records with the given updated_at
327
-
328
- end # when try to update records without options
329
-
330
- context "when call method with option :slice_size set is default" do
331
- it "generates one insert queries" do
332
- Employee.should_receive(:find_by_sql).once.and_return([])
333
- Employee.update_many([{ :id => 1, :name => 'Elvis' },
334
- { :id => 2, :name => 'Freddi'},
335
- { :id => 3, :name => 'Patric'},
336
- { :id => 4, :name => 'Jane'}])
337
- end
338
- end # when call method with option :slice_size set is default
339
-
340
-
341
- context "when call method with option :slice_size = 2" do
342
- it "generates two insert queries" do
343
- Employee.should_receive(:find_by_sql).twice.and_return([])
344
- Employee.update_many([{ :id => 1, :name => 'Elvis' },
345
- { :id => 2, :name => 'Freddi'},
346
- { :id => 3, :name => 'Patric'},
347
- { :id => 4, :name => 'Jane'}],
348
- { :slice_size => 2})
349
- end
350
- end # when call method with option :slice_size = 2
351
-
352
- context "when try to update two records and doesn't
353
- the same number of keys and options check_consistency equal false" do
354
- it "raises ActiveRecord::StatementInvalid" do
355
- lambda {
356
- Employee.update_many([{ :id => 1, :name => 'Elvis', :salary => 1002 },
357
- { :name => 'Freddi', :id => 2}],
358
- { :check_consistency => false })
359
- }.should raise_error(ActiveRecord::StatementInvalid)
360
- end
361
- end # when try to update two records and doesn't
362
- # the same number of keys and options check_consistency equal false
363
-
364
- context "when update two records with options 'returning' equal :name" do
365
- it "returns last records name" do
366
- Employee.update_many([{ :id => 1, :name => 'Elvis' },
367
- { :id => 2, :name => 'Freddi'}],
368
- { :returning => [:name] }).
369
- last.name.should == 'Freddi'
370
- end
371
- end # when update two records with options 'returning' equal :name
372
-
373
- context "when update method with options :set_array equal 'salary = datatable.salary'" do
374
- it "updates only salary column" do
375
- Employee.update_many([{ :id => 1, :name => 'Elvis', :salary => 12 },
376
- { :id => 2, :name => 'Freddi',:salary => 22}],
377
- { :set_array => '"salary = datatable.salary"' })
378
- Employee.find(1).name.should_not == "Elvis"
379
- Employee.find(1).salary.should == 12
380
- Employee.find(2).name.should_not == "Freddi"
381
- Employee.find(2).salary.should == 22
382
- end
383
- end # when update method with options :set_array equal 'salary = datatable.salary'
384
-
385
- context "when update method with options :where" do
386
- it "updates only name column, where salary equal input values" do
387
- Employee.update_many([{ :id => 1, :name => 'Elvis', :salary => 12 },
388
- { :id => 2, :name => 'Freddi',:salary => 22}],
389
- { :where => '"#{table_name}.salary = datatable.salary"' })
390
- Employee.find(1).name.should_not == "Elvis"
391
- Employee.find(1).salary.should == 3
392
- Employee.find(2).name.should_not == "Freddi"
393
- Employee.find(2).salary.should == 3
394
- end
395
- end # when update method with options :where
396
-
397
- context "when try to update records using partitioning" do
398
-
399
- before do
400
- drop_tables
401
- create_tables
402
- Partitioned::BulkMethodsMixin.send(:remove_const, :Employee)
403
- class Employee < ByForeignKey
404
- belongs_to :company, :class_name => 'Company'
405
-
406
- def self.partition_foreign_key
407
- return :company_id
408
- end
409
-
410
- partitioned do |partition|
411
- partition.index :id, :unique => true
412
- partition.foreign_key :company_id
413
- end
414
- end # Employee
415
- Employee.create_new_partition_tables(Employee.partition_generate_range(0, 4, 1))
416
- Employee.create_many([{ :name => 'Keith', :company_id => 2 },
417
- { :name => 'Alex', :company_id => 1 },
418
- { :name => 'Mark', :company_id => 3 }])
419
- end
420
-
421
- after do
422
- Partitioned::BulkMethodsMixin.send(:remove_const, :Employee)
423
- end
424
-
425
- it "returns records" do
426
- Employee.update_many({ { :company_id => 2, :id => 1 } => { :name => 'Indy' },
427
- { :company_id => 1, :id => 2 } => { :name => 'Larry' },
428
- { :company_id => 3, :id => 3 } => { :name => 'Filip' } })
429
- Employee.from_partition(1).where(:id => 2).first.name.should == "Larry"
430
- Employee.where(:id => 1, :company_id => 2).first.name.should == "Indy"
431
- Employee.all.map{ |r| r.name }.should == ["Larry", "Indy", "Filip"]
432
- end
433
- end # when try to update records using partitioning
434
-
435
- context "when try to update records in the table that has all the different sql types" do
436
-
437
- before do
438
- ActiveRecord::Base.connection.execute <<-SQL
439
- ALTER TABLE employees ADD COLUMN test_string character varying;
440
- ALTER TABLE employees ADD COLUMN test_float float;
441
- ALTER TABLE employees ADD COLUMN test_decimal decimal;
442
- ALTER TABLE employees ADD COLUMN test_time time;
443
- ALTER TABLE employees ADD COLUMN test_time_string time;
444
- ALTER TABLE employees ADD COLUMN test_date date;
445
- ALTER TABLE employees ADD COLUMN test_date_string date;
446
- ALTER TABLE employees ADD COLUMN test_bytea bytea;
447
- ALTER TABLE employees ADD COLUMN test_boolean boolean;
448
- ALTER TABLE employees ADD COLUMN test_xml xml;
449
- ALTER TABLE employees ADD COLUMN test_tsvector tsvector;
450
- SQL
451
- Employee.reset_column_information
452
- end
453
-
454
- after do
455
- ActiveRecord::Base.connection.reset!
456
- end
457
-
458
- context "non-null values" do
459
- it "returns record with all sql types" do
460
- lambda { Employee.update_many([{ :id => 1,
461
- :name => 'Keith',
462
- :company_id => 2,
463
- :created_at => Time.zone.parse('2012-12-21'),
464
- :updated_at => '2012-12-21 00:00:00',
465
- :test_string => "string",
466
- :test_float => 12.34,
467
- :test_decimal => 123456789101112,
468
- :test_time => Time.now,
469
- :test_time_string => '00:00:00',
470
- :test_date => Date.parse('2012-12-21'),
471
- :test_date_string => '2012-12-21',
472
- :test_bytea => "text".bytes.to_a,
473
- :test_boolean => false,
474
- :test_xml => ["text"].to_xml,
475
- :test_tsvector => "test string",
476
- }]) }.should_not raise_error
477
- Employee.find(1).test_boolean.should == false
478
- Employee.find(1).test_tsvector.should == "'string' 'test'"
479
- end
480
- end # non-null values
481
-
482
- context "null values" do
483
- it "returns record with all sql types" do
484
- lambda { Employee.update_many([{ :id => 1,
485
- :name => 'Keith',
486
- :company_id => 2,
487
- :updated_at => nil,
488
- :salary => nil,
489
- :test_string => nil,
490
- :test_float => nil,
491
- :test_decimal => nil,
492
- :test_time => nil,
493
- :test_time_string => nil,
494
- :test_date => nil,
495
- :test_date_string => nil,
496
- :test_bytea => nil,
497
- :test_boolean => nil,
498
- :test_xml => nil,
499
- :test_tsvector => nil,
500
- }]) }.should_not raise_error
501
- Employee.find(1).test_boolean.should == nil
502
- Employee.find(1).test_tsvector.should == nil
503
- end
504
- end # null values
505
-
506
- end # when try to update records in the table that has all the different sql types
507
-
508
- end # update_many
509
-
510
- end # BulkMethodsMixin
511
- end # BulkMethodsMixin
512
- end # Partitioned