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 +4 -4
- data/History.rdoc +5 -0
- data/lib/composite_primary_keys/base.rb +64 -0
- data/lib/composite_primary_keys/composite_predicates.rb +12 -7
- data/lib/composite_primary_keys/version.rb +1 -1
- data/test/fixtures/reference_type.rb +12 -12
- data/test/fixtures/tariff.rb +5 -5
- data/test/test_find_in_batches.rb +30 -0
- data/test/test_predicates.rb +20 -1
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8ca795714eef0beaa1a0e91a07b09b1735b12242
|
4
|
+
data.tar.gz: 41e87e3f7ae758e975809e56d8c7a3396d604f4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f142511ba73692d9599bafdf78b26e61405a76b34b4de455f313a248fec5b6a75240f56eb9f6ef87ec299edd9db3ac5fc2b7849e51ed84b1efb16e852fa62c5
|
7
|
+
data.tar.gz: ebf3f4281ee1c7c372a8b035a2e7a2a8651f7972a673c38555de38449b05d781696971eb7f507f912322c4527a28664216d4f8c69cfaafb5d11f09a71c8aea20
|
data/History.rdoc
CHANGED
@@ -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
|
-
|
16
|
-
|
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 =
|
19
|
-
|
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)
|
@@ -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
|
data/test/fixtures/tariff.rb
CHANGED
@@ -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
|
data/test/test_predicates.rb
CHANGED
@@ -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.
|
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-
|
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.
|
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.
|
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.
|
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
|