composite_primary_keys 8.1.3 → 8.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|