composite_primary_keys 8.1.3 → 8.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5dcd8c948f14181d3e5ebb97a5a79f124e7e9cef
4
- data.tar.gz: 435b1a7ffaf2c481b09b268fd28585112a092f44
3
+ metadata.gz: 8ca795714eef0beaa1a0e91a07b09b1735b12242
4
+ data.tar.gz: 41e87e3f7ae758e975809e56d8c7a3396d604f4c
5
5
  SHA512:
6
- metadata.gz: c38c11665ea24815069cd8c2c14701108cc1516663cba835eb044db3ebcabc26d46dee9db88d9c2c6923d992ee3c0b8293825ca3f2af88411641a1ed0a4bff67
7
- data.tar.gz: cd22418296a7ffbd27c5ece78c7bc835be47bd50b4a4ed51c96bea564225fa3ebc9782fb60ba6b02a76d7784074c56c2df82d7d3e443cd7e17053ab8a25b43f9
6
+ metadata.gz: 1f142511ba73692d9599bafdf78b26e61405a76b34b4de455f313a248fec5b6a75240f56eb9f6ef87ec299edd9db3ac5fc2b7849e51ed84b1efb16e852fa62c5
7
+ data.tar.gz: ebf3f4281ee1c7c372a8b035a2e7a2a8651f7972a673c38555de38449b05d781696971eb7f507f912322c4527a28664216d4f8c69cfaafb5d11f09a71c8aea20
@@ -1,3 +1,8 @@
1
+ == 8.1.4 (2016-07-27)
2
+
3
+ * Create OR predicates in a nicely balanced tree fixing #320 (Nathan Samson)
4
+ * Fix find in batches (apurvis)
5
+
1
6
  == 8.1.3 (2016-04-16)
2
7
 
3
8
  * Make CPK work for Arel::Attributes::Attribute (Rick Xing)
@@ -53,6 +53,70 @@ module ActiveRecord
53
53
  def composite?
54
54
  false
55
55
  end
56
+
57
+ def find_in_batches(options = {})
58
+ return super unless primary_key.is_a?(Array)
59
+
60
+ # Unfortunately .count uses a subquery temp table, which is a big problem when your table is large
61
+ number_of_rows = count(primary_key.first)
62
+ batch_size = options[:batch_size] || 100000
63
+ row_number = 0
64
+
65
+ while row_number < number_of_rows
66
+ end_row_number = row_number + batch_size - 1
67
+ end_row_number = number_of_rows - 1 if end_row_number > number_of_rows - 1
68
+
69
+ # Force the necessary sorting; AR as is will sort a PK table incorrectly
70
+ start_key = order(*primary_key).
71
+ offset(row_number).
72
+ first.
73
+ attributes.
74
+ slice(*primary_key)
75
+
76
+ end_key = order(*(primary_key.map { |k| "#{k} ASC" })).
77
+ offset(end_row_number).
78
+ first.
79
+ attributes.
80
+ slice(*primary_key)
81
+
82
+ relation = self
83
+ lower_bounds = []
84
+ upper_bounds = []
85
+
86
+ # Iterate through the PKs positionally; when we have found a discrepancy between start and end
87
+ # then we know that's where the boundaries are
88
+ primary_key.each do |col|
89
+ if start_key[col] == end_key[col]
90
+ relation = relation.where("#{col} = '#{start_key[col]}'")
91
+ else
92
+ lower_bounds << [col, start_key[col]]
93
+ upper_bounds << [col, end_key[col]]
94
+ end
95
+ end
96
+
97
+ relation = relation.where(build_batch_case(lower_bounds, '>')) unless lower_bounds.empty?
98
+ relation = relation.where(build_batch_case(upper_bounds, '<')) unless upper_bounds.empty?
99
+
100
+ yield(relation.order(*primary_key))
101
+
102
+ row_number = end_row_number + 1
103
+ end
104
+ end
105
+
106
+ private
107
+
108
+ def build_batch_case(bounds, operator)
109
+ bounds = bounds.dup
110
+ bound = bounds.shift
111
+ if bounds.empty?
112
+ "#{bound[0]} #{operator}= '#{bound[1]}' "
113
+ else
114
+ sql_case = "CASE WHEN #{bound[0]} = '#{bound[1]}' THEN "
115
+ sql_case += build_batch_case(bounds, operator)
116
+ sql_case += "ELSE #{bound[0]} #{operator} '#{bound[1]}' END "
117
+ sql_case
118
+ end
119
+ end
56
120
  end
57
121
 
58
122
  def composite?
@@ -8,17 +8,22 @@ module CompositePrimaryKeys
8
8
  end
9
9
  end
10
10
 
11
- def cpk_or_predicate(predicates)
11
+ def cpk_or_predicate(predicates, group = true)
12
12
  if predicates.length <= 1
13
13
  predicates.first
14
14
  else
15
- predicates_copy = predicates.dup
16
- predicate = predicates_copy.shift
15
+ split_point = predicates.length / 2
16
+ predicates_first_half = predicates[0...split_point]
17
+ predicates_second_half = predicates[split_point..-1]
17
18
 
18
- or_predicate = predicates_copy.inject(predicate) do |mem, predicate|
19
- ::Arel::Nodes::Or.new(mem, predicate)
19
+ or_predicate = ::Arel::Nodes::Or.new(cpk_or_predicate(predicates_first_half, false),
20
+ cpk_or_predicate(predicates_second_half, false))
21
+
22
+ if group
23
+ ::Arel::Nodes::Grouping.new(or_predicate)
24
+ else
25
+ or_predicate
20
26
  end
21
- ::Arel::Nodes::Grouping.new(or_predicate)
22
27
  end
23
28
  end
24
29
 
@@ -53,4 +58,4 @@ ActiveRecord::Associations::JoinDependency::JoinAssociation.send(:include, Compo
53
58
  ActiveRecord::Associations::Preloader::Association.send(:include, CompositePrimaryKeys::Predicates)
54
59
  ActiveRecord::Associations::HasManyThroughAssociation.send(:include, CompositePrimaryKeys::Predicates)
55
60
  ActiveRecord::Relation.send(:include, CompositePrimaryKeys::Predicates)
56
- ActiveRecord::PredicateBuilder.send(:extend, CompositePrimaryKeys::Predicates)
61
+ ActiveRecord::PredicateBuilder.send(:extend, CompositePrimaryKeys::Predicates)
@@ -2,7 +2,7 @@ module CompositePrimaryKeys
2
2
  module VERSION
3
3
  MAJOR = 8
4
4
  MINOR = 1
5
- TINY = 3
5
+ TINY = 4
6
6
  STRING = [MAJOR, MINOR, TINY].join('.')
7
7
  end
8
8
  end
@@ -1,12 +1,12 @@
1
- class ReferenceType < ActiveRecord::Base
2
- self.primary_key = :reference_type_id
3
- has_many :reference_codes, :foreign_key => "reference_type_id", :dependent => :destroy
4
- accepts_nested_attributes_for :reference_codes
5
-
6
- validates_presence_of :type_label, :abbreviation
7
- validates_uniqueness_of :type_label
8
-
9
- before_destroy do |record|
10
- a = record
11
- end
12
- end
1
+ class ReferenceType < ActiveRecord::Base
2
+ self.primary_key = :reference_type_id
3
+ has_many :reference_codes, :foreign_key => "reference_type_id", :dependent => :destroy
4
+ accepts_nested_attributes_for :reference_codes
5
+
6
+ validates_presence_of :type_label, :abbreviation
7
+ validates_uniqueness_of :type_label
8
+
9
+ before_destroy do |record|
10
+ a = record
11
+ end
12
+ end
@@ -1,5 +1,5 @@
1
- class Tariff < ActiveRecord::Base
2
- self.primary_keys = [:tariff_id, :start_date]
3
- has_many :product_tariffs, :foreign_key => [:tariff_id, :tariff_start_date]
4
- has_many :products, :through => :product_tariffs, :foreign_key => [:tariff_id, :tariff_start_date]
5
- end
1
+ class Tariff < ActiveRecord::Base
2
+ self.primary_keys = [:tariff_id, :start_date]
3
+ has_many :product_tariffs, :foreign_key => [:tariff_id, :tariff_start_date]
4
+ has_many :products, :through => :product_tariffs, :foreign_key => [:tariff_id, :tariff_start_date]
5
+ end
@@ -0,0 +1,30 @@
1
+ require File.expand_path('../abstract_unit', __FILE__)
2
+
3
+ class TestFindInBatches < ActiveSupport::TestCase
4
+ fixtures :capitols
5
+
6
+ def test_in_batches
7
+ capitols = []
8
+ Capitol.find_in_batches do |chunk|
9
+ capitols += chunk.map(&:country)
10
+ end
11
+ assert_equal(capitols, ['Canada', 'France', 'Mexico', 'The Netherlands'])
12
+ end
13
+
14
+ def test_in_small_batches
15
+ capitols = []
16
+ Capitol.find_in_batches(batch_size: 2) do |chunk|
17
+ capitols += chunk.map(&:country)
18
+ end
19
+
20
+ assert_equal(capitols, ['Canada', 'France', 'Mexico', 'The Netherlands'])
21
+ end
22
+
23
+ def test_in_one_unit_batch
24
+ capitols = []
25
+ Capitol.find_in_batches(batch_size: 1) do |chunk|
26
+ capitols += chunk.map(&:country)
27
+ end
28
+ assert_equal(capitols, ['Canada', 'France', 'Mexico', 'The Netherlands'])
29
+ end
30
+ end
@@ -22,6 +22,25 @@ class TestEqual < ActiveSupport::TestCase
22
22
  assert_equal(with_quoted_identifiers(expected), pred.to_sql)
23
23
  end
24
24
 
25
+ def test_or_with_many_values
26
+ dep = Arel::Table.new(:departments)
27
+
28
+ predicates = Array.new
29
+
30
+ number_of_predicates = 30000 # This should really be big
31
+ number_of_predicates.times do |i|
32
+ predicates << dep[:id].eq(i)
33
+ end
34
+
35
+ connection = ActiveRecord::Base.connection
36
+ quoted = "#{connection.quote_table_name('departments')}.#{connection.quote_column_name('id')}"
37
+ expected_ungrouped = ((0...number_of_predicates).map { |i| "#{quoted} = #{i}" }).join(' OR ')
38
+ expected = "(#{expected_ungrouped})"
39
+
40
+ pred = cpk_or_predicate(predicates)
41
+ assert_equal(with_quoted_identifiers(expected), pred.to_sql)
42
+ end
43
+
25
44
  # def test_and
26
45
  # dep = Arel::Table.new(:departments)
27
46
  #
@@ -38,4 +57,4 @@ class TestEqual < ActiveSupport::TestCase
38
57
  # pred = cpk_and_predicate(predicates)
39
58
  # assert_equal(with_quoted_identifiers(expected), pred.to_sql)
40
59
  # end
41
- end
60
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: composite_primary_keys
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.1.3
4
+ version: 8.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charlie Savage
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-16 00:00:00.000000000 Z
11
+ date: 2016-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - '='
60
60
  - !ruby/object:Gem::Version
61
- version: 0.3.20
61
+ version: 0.4.4
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - '='
67
67
  - !ruby/object:Gem::Version
68
- version: 0.3.20
68
+ version: 0.4.4
69
69
  description: Composite key support for ActiveRecord
70
70
  email:
71
71
  executables: []
@@ -233,6 +233,7 @@ files:
233
233
  - test/test_equal.rb
234
234
  - test/test_exists.rb
235
235
  - test/test_find.rb
236
+ - test/test_find_in_batches.rb
236
237
  - test/test_habtm.rb
237
238
  - test/test_ids.rb
238
239
  - test/test_miscellaneous.rb
@@ -268,7 +269,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
268
269
  version: '0'
269
270
  requirements: []
270
271
  rubyforge_project:
271
- rubygems_version: 2.5.1
272
+ rubygems_version: 2.6.6
272
273
  signing_key:
273
274
  specification_version: 4
274
275
  summary: Composite key support for ActiveRecord
@@ -294,6 +295,7 @@ test_files:
294
295
  - test/test_equal.rb
295
296
  - test/test_exists.rb
296
297
  - test/test_find.rb
298
+ - test/test_find_in_batches.rb
297
299
  - test/test_habtm.rb
298
300
  - test/test_ids.rb
299
301
  - test/test_miscellaneous.rb