composite_primary_keys 9.0.10 → 10.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.rdoc +30 -4
- data/README.rdoc +10 -9
- data/lib/composite_primary_keys/arel/sqlserver.rb +2 -1
- data/lib/composite_primary_keys/associations/association_scope.rb +13 -16
- data/lib/composite_primary_keys/associations/collection_association.rb +1 -1
- data/lib/composite_primary_keys/associations/join_dependency.rb +2 -2
- data/lib/composite_primary_keys/associations/preloader/association.rb +4 -2
- data/lib/composite_primary_keys/associations/preloader/belongs_to.rb +2 -2
- data/lib/composite_primary_keys/attribute_methods/primary_key.rb +13 -0
- data/lib/composite_primary_keys/attribute_methods/write.rb +14 -0
- data/lib/composite_primary_keys/base.rb +6 -5
- data/lib/composite_primary_keys/composite_predicates.rb +4 -0
- data/lib/composite_primary_keys/connection_adapters/postgresql/database_statements.rb +26 -0
- data/lib/composite_primary_keys/connection_adapters/sqlserver_adapter.rb +1 -1
- data/lib/composite_primary_keys/core.rb +9 -12
- data/lib/composite_primary_keys/locking/optimistic.rb +21 -31
- data/lib/composite_primary_keys/nested_attributes.rb +1 -1
- data/lib/composite_primary_keys/relation/batches.rb +24 -5
- data/lib/composite_primary_keys/relation/predicate_builder/association_query_handler.rb +33 -0
- data/lib/composite_primary_keys/relation.rb +13 -7
- data/lib/composite_primary_keys/version.rb +2 -2
- data/lib/composite_primary_keys.rb +4 -3
- data/test/test_find.rb +9 -3
- data/test/test_preload.rb +7 -0
- metadata +7 -6
- data/lib/composite_primary_keys/relation/predicate_builder.rb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f506d34f44d88354550c10d7b79f38c206dfa739c9c903e59c888252db3ae9ed
|
4
|
+
data.tar.gz: 06e5788af10e07e0dbe785338aa35c8cce99df310a7e43d3605b1cefb0eaa0ac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83ec3b8f8f42e7a5c92f11c60cb86fdb8fd4bdc9e6a58f1f3305ffb5c9b6af9b2abaabcaa43eeaa2e40e19cddab4cad5d7e5501905643e7b4fad0749525ac0b7
|
7
|
+
data.tar.gz: d1a8bad2cdea8ff1eab3ba7d5c456d68f008583b26b6000a930f58d319842aa6b2a24e66d7d509bd531cce4852f062797b96e53c0f929ed8aa0c6a19d40621c2
|
data/History.rdoc
CHANGED
@@ -1,10 +1,36 @@
|
|
1
|
-
==
|
1
|
+
== 10.0.5 (2018-06-24)
|
2
|
+
* Fix in_batches (Urmo Rae)
|
2
3
|
|
3
|
-
|
4
|
+
== 10.0.4 (2018-06-17)
|
5
|
+
|
6
|
+
* Lock mysql2 gem to 0.4.x (Le Trung Kien)
|
7
|
+
* Set inversion when using preload (Le Trung Kien)
|
8
|
+
* Fixes for AR 5.1.6 (Tim Morgan)
|
9
|
+
|
10
|
+
== 10.0.3 (2018-02-25)
|
11
|
+
|
12
|
+
* Require a minimum of activerecord version 5.1.5 (Tom Hughes)
|
13
|
+
|
14
|
+
== 10.0.2 (2017-12-02)
|
15
|
+
|
16
|
+
* Fix frozen string mutation for mysql (Sergey Sein)
|
17
|
+
* Fix autosave for belongs_to associations (Fabian Mersch)
|
18
|
+
* Require a minimum of activerecord version 5.1.4 (Charlie Savage)
|
4
19
|
|
5
|
-
==
|
20
|
+
== 10.0.1 (2017-10-08)
|
6
21
|
|
7
|
-
*
|
22
|
+
* Fixed an error that happened when a table attribute had the same name as the table (Taishi Kasuga)
|
23
|
+
|
24
|
+
== 10.0.0 (2017-10-08)
|
25
|
+
|
26
|
+
* Support ActiveRecord 5.1.x (Pinak Thakkar, Jordan Owens)
|
27
|
+
* Fix Paper Trail compatibility (Sean Linsley)
|
28
|
+
* Fix typo in Readme (Mike Gunderloy)
|
29
|
+
* Improved sql server support (Artyom Nikolaev)
|
30
|
+
|
31
|
+
== 9.0.10 (2018-06-24)
|
32
|
+
|
33
|
+
* Fix AR 5.0.7 (Jordan Owens)
|
8
34
|
|
9
35
|
== 9.0.8 (2017-10-11)
|
10
36
|
|
data/README.rdoc
CHANGED
@@ -12,7 +12,7 @@ to support composite keys.
|
|
12
12
|
|
13
13
|
If you are using Rails add the following to your Gemfile:
|
14
14
|
|
15
|
-
gem 'composite_primary_keys', '=x.x.x' (see next section about what
|
15
|
+
gem 'composite_primary_keys', '=x.x.x' (see next section about what version to use)
|
16
16
|
|
17
17
|
== Versions
|
18
18
|
|
@@ -20,12 +20,13 @@ Every major version of ActiveRecord has included numerous internal changes. As
|
|
20
20
|
CPK has to be rewritten for each version of ActiveRecord. To help keep
|
21
21
|
things straight, here is the mapping:
|
22
22
|
|
23
|
-
Version
|
24
|
-
Version
|
25
|
-
Version
|
26
|
-
Version
|
27
|
-
Version
|
28
|
-
Version
|
23
|
+
Version 10.x is designed to work with ActiveRecord 5.1.x (note this is in progress)
|
24
|
+
Version 9.x is designed to work with ActiveRecord 5.0.x
|
25
|
+
Version 8.x is designed to work with ActiveRecord 4.2.x
|
26
|
+
Version 7.x is designed to work with ActiveRecord 4.1.x
|
27
|
+
Version 6.x is designed to work with ActiveRecord 4.0.x
|
28
|
+
Version 5.x is designed to work with ActiveRecord 3.2.x
|
29
|
+
Version 4.x is designed to work with ActiveRecord 3.1.x
|
29
30
|
|
30
31
|
Run the following command to list available versions:
|
31
32
|
|
@@ -155,8 +156,8 @@ built-in rake task before running tests:
|
|
155
156
|
|
156
157
|
rake sqlite:build_database
|
157
158
|
|
158
|
-
|
159
|
-
|
159
|
+
For sqlite3 to work correctly, you must manually require 'composite_primary_keys/connection_adapters/sqlite3_adapter' after
|
160
|
+
loading the CPK gem.
|
160
161
|
|
161
162
|
=== SqlServer
|
162
163
|
|
@@ -16,7 +16,8 @@ module Arel
|
|
16
16
|
|
17
17
|
def primary_Key_From_Table t
|
18
18
|
return unless t
|
19
|
-
column_name = schema_cache.primary_keys(t.name) ||
|
19
|
+
column_name = @connection.schema_cache.primary_keys(t.name) ||
|
20
|
+
@connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name)
|
20
21
|
|
21
22
|
# CPK
|
22
23
|
# column_name ? t[column_name] : nil
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Associations
|
3
3
|
class AssociationScope
|
4
|
-
|
5
4
|
def self.get_bind_values(owner, chain)
|
6
5
|
binds = []
|
7
6
|
last_reflection = chain.last
|
@@ -23,31 +22,29 @@ module ActiveRecord
|
|
23
22
|
binds
|
24
23
|
end
|
25
24
|
|
26
|
-
def last_chain_scope(scope, table, reflection, owner
|
27
|
-
join_keys = reflection.join_keys
|
25
|
+
def last_chain_scope(scope, table, reflection, owner)
|
26
|
+
join_keys = reflection.join_keys
|
28
27
|
key = join_keys.key
|
29
28
|
foreign_key = join_keys.foreign_key
|
30
29
|
|
31
30
|
# CPK
|
32
|
-
#value = transform_value(owner[foreign_key])
|
33
|
-
#scope = scope
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
hash
|
31
|
+
# value = transform_value(owner[foreign_key])
|
32
|
+
# scope = apply_scope(scope, table, key, value)
|
33
|
+
Array(key).zip(Array(foreign_key)).each do |a_join_key, a_foreign_key|
|
34
|
+
value = transform_value(owner[a_foreign_key])
|
35
|
+
scope = apply_scope(scope, table, a_join_key, value)
|
38
36
|
end
|
39
|
-
scope = scope.where(table.name => joins)
|
40
37
|
|
41
38
|
if reflection.type
|
42
39
|
polymorphic_type = transform_value(owner.class.base_class.name)
|
43
|
-
scope = scope
|
40
|
+
scope = apply_scope(scope, table, reflection.type, polymorphic_type)
|
44
41
|
end
|
45
42
|
|
46
43
|
scope
|
47
44
|
end
|
48
45
|
|
49
|
-
def next_chain_scope(scope, table, reflection,
|
50
|
-
join_keys = reflection.join_keys
|
46
|
+
def next_chain_scope(scope, table, reflection, foreign_table, next_reflection)
|
47
|
+
join_keys = reflection.join_keys
|
51
48
|
key = join_keys.key
|
52
49
|
foreign_key = join_keys.foreign_key
|
53
50
|
|
@@ -57,11 +54,11 @@ module ActiveRecord
|
|
57
54
|
|
58
55
|
if reflection.type
|
59
56
|
value = transform_value(next_reflection.klass.base_class.name)
|
60
|
-
scope = scope
|
57
|
+
scope = apply_scope(scope, table, reflection.type, value)
|
61
58
|
end
|
62
59
|
|
63
|
-
scope
|
60
|
+
scope.joins!(join(foreign_table, constraint))
|
64
61
|
end
|
65
62
|
end
|
66
63
|
end
|
67
|
-
end
|
64
|
+
end
|
@@ -29,7 +29,7 @@ module CompositePrimaryKeys
|
|
29
29
|
ids.map! { |i| pk_type.cast(i) }
|
30
30
|
# CPK
|
31
31
|
if reflection.association_primary_key.is_a?(Array)
|
32
|
-
predicate =
|
32
|
+
predicate = CompositePrimaryKeys::Predicates.cpk_in_predicate(klass.arel_table, reflection.association_primary_key, ids)
|
33
33
|
records = klass.where(predicate).index_by do |r|
|
34
34
|
reflection.association_primary_key.map{ |k| r.send(k) }
|
35
35
|
end.values_at(*ids)
|
@@ -18,7 +18,7 @@ module ActiveRecord
|
|
18
18
|
end
|
19
19
|
|
20
20
|
silence_warnings do
|
21
|
-
def instantiate(result_set,
|
21
|
+
def instantiate(result_set, &block)
|
22
22
|
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
23
23
|
|
24
24
|
seen = Hash.new { |i, object_id|
|
@@ -47,7 +47,7 @@ module ActiveRecord
|
|
47
47
|
primary_key ? row_hash[primary_key] : row_hash
|
48
48
|
end
|
49
49
|
|
50
|
-
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases)
|
50
|
+
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
|
51
51
|
construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
|
52
52
|
}
|
53
53
|
end
|
@@ -3,9 +3,9 @@ module ActiveRecord
|
|
3
3
|
class Preloader
|
4
4
|
class Association
|
5
5
|
silence_warnings do
|
6
|
-
def
|
6
|
+
def records_for(ids)
|
7
7
|
# CPK
|
8
|
-
# scope.where(
|
8
|
+
# scope.where(association_key_name => ids)
|
9
9
|
|
10
10
|
if reflection.foreign_key.is_a?(Array)
|
11
11
|
predicate = cpk_in_predicate(table, reflection.foreign_key, ids)
|
@@ -43,6 +43,8 @@ module ActiveRecord
|
|
43
43
|
records.each do |record, owner_key|
|
44
44
|
owners_map[owner_key].each do |owner|
|
45
45
|
records_by_owner[owner] << record
|
46
|
+
association = owner.association(reflection.name)
|
47
|
+
association.set_inverse_instance(record)
|
46
48
|
end
|
47
49
|
end
|
48
50
|
end
|
@@ -2,7 +2,7 @@ module ActiveRecord
|
|
2
2
|
module Associations
|
3
3
|
class Preloader
|
4
4
|
class BelongsTo
|
5
|
-
def
|
5
|
+
def records_for(ids)
|
6
6
|
# CPK
|
7
7
|
# scope.where(association_key.in(ids))
|
8
8
|
|
@@ -10,7 +10,7 @@ module ActiveRecord
|
|
10
10
|
predicate = cpk_in_predicate(table, association_key_name, ids)
|
11
11
|
scope.where(predicate)
|
12
12
|
else
|
13
|
-
scope.where(
|
13
|
+
scope.where(association_key_name => ids)
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -16,6 +16,19 @@ module ActiveRecord
|
|
16
16
|
end
|
17
17
|
|
18
18
|
end
|
19
|
+
|
20
|
+
def id_in_database
|
21
|
+
sync_with_transaction_state
|
22
|
+
# CPK
|
23
|
+
# attribute_in_database(self.class.primary_key)
|
24
|
+
if self.composite?
|
25
|
+
self.class.primary_keys.map do |key_attr|
|
26
|
+
attribute_in_database(key_attr)
|
27
|
+
end
|
28
|
+
else
|
29
|
+
attribute_in_database(self.class.primary_key)
|
30
|
+
end
|
31
|
+
end
|
19
32
|
end
|
20
33
|
end
|
21
34
|
end
|
@@ -2,6 +2,20 @@ module ActiveRecord
|
|
2
2
|
module AttributeMethods
|
3
3
|
module Write
|
4
4
|
silence_warnings do
|
5
|
+
def write_attribute(attr_name, value)
|
6
|
+
name = if self.class.attribute_alias?(attr_name)
|
7
|
+
# CPK
|
8
|
+
# self.class.attribute_alias(attr_name).to_s
|
9
|
+
self.class.attribute_alias(attr_name)
|
10
|
+
else
|
11
|
+
# CPK
|
12
|
+
# attr_name.to_s
|
13
|
+
attr_name
|
14
|
+
end
|
15
|
+
|
16
|
+
write_attribute_with_type_cast(name, value, true)
|
17
|
+
end
|
18
|
+
|
5
19
|
def write_attribute_with_type_cast(attr_name, value, should_type_cast)
|
6
20
|
# CPK
|
7
21
|
if attr_name.kind_of?(Array)
|
@@ -47,15 +47,16 @@ module ActiveRecord
|
|
47
47
|
|
48
48
|
module CompositeClassMethods
|
49
49
|
def primary_keys
|
50
|
-
unless defined?
|
51
|
-
reset_primary_keys
|
52
|
-
end
|
50
|
+
@primary_keys = reset_primary_keys unless defined? @primary_keys
|
53
51
|
@primary_keys
|
54
52
|
end
|
55
53
|
|
56
54
|
# Don't like this method name, but its modeled after how AR does it
|
57
|
-
|
58
|
-
if self
|
55
|
+
def reset_primary_keys #:nodoc:
|
56
|
+
if self == base_class
|
57
|
+
# CPK
|
58
|
+
self.primary_keys = get_primary_key(base_class.name)
|
59
|
+
else
|
59
60
|
self.primary_keys = base_class.primary_keys
|
60
61
|
end
|
61
62
|
end
|
@@ -1,5 +1,9 @@
|
|
1
1
|
module CompositePrimaryKeys
|
2
2
|
module Predicates
|
3
|
+
# Similar to module_function, but does not make instance methods private.
|
4
|
+
# https://idiosyncratic-ruby.com/8-self-improvement.html
|
5
|
+
extend self
|
6
|
+
|
3
7
|
def cpk_and_predicate(predicates)
|
4
8
|
if predicates.length == 1
|
5
9
|
predicates.first
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module PostgreSQL
|
4
|
+
module DatabaseStatements
|
5
|
+
def sql_for_insert(sql, pk, id_value, sequence_name, binds) # :nodoc:
|
6
|
+
if pk.nil?
|
7
|
+
# Extract the table from the insert sql. Yuck.
|
8
|
+
table_ref = extract_table_ref_from_insert_sql(sql)
|
9
|
+
pk = primary_key(table_ref) if table_ref
|
10
|
+
end
|
11
|
+
|
12
|
+
# CPK
|
13
|
+
# if pk = suppress_composite_primary_key(pk)
|
14
|
+
# sql = "#{sql} RETURNING #{quote_column_name(pk)}"
|
15
|
+
#end
|
16
|
+
# NOTE pk can be false.
|
17
|
+
if pk
|
18
|
+
sql = "#{sql} RETURNING #{quote_column_names(pk)}"
|
19
|
+
end
|
20
|
+
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -7,7 +7,7 @@ module ActiveRecord
|
|
7
7
|
# quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted
|
8
8
|
# sql.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}"
|
9
9
|
quoted_pks = [pk].flatten.map {|pk| "INSERTED.#{SQLServer::Utils.extract_identifiers(pk).quoted}"}
|
10
|
-
sql.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT #{quoted_pks.join(", ")}"
|
10
|
+
sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT #{quoted_pks.join(", ")}"
|
11
11
|
else
|
12
12
|
"#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"
|
13
13
|
end
|
@@ -27,36 +27,33 @@ module ActiveRecord
|
|
27
27
|
return super if block_given? ||
|
28
28
|
primary_key.nil? ||
|
29
29
|
scope_attributes? ||
|
30
|
-
columns_hash.include?(inheritance_column)
|
31
|
-
|
30
|
+
columns_hash.include?(inheritance_column)
|
31
|
+
|
32
32
|
# CPK
|
33
33
|
return super if self.composite?
|
34
34
|
|
35
|
-
id
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
You are passing an instance of ActiveRecord::Base to `find`.
|
40
|
-
Please pass the id of the object by calling `.id`
|
41
|
-
MSG
|
42
|
-
end
|
35
|
+
id = ids.first
|
36
|
+
|
37
|
+
return super if id.kind_of?(Array) ||
|
38
|
+
id.is_a?(ActiveRecord::Base)
|
43
39
|
|
44
40
|
key = primary_key
|
45
41
|
|
46
42
|
statement = cached_find_by_statement(key) { |params|
|
47
43
|
where(key => params.bind).limit(1)
|
48
44
|
}
|
45
|
+
|
49
46
|
record = statement.execute([id], self, connection).first
|
50
47
|
unless record
|
51
48
|
raise RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}",
|
52
49
|
name, primary_key, id)
|
53
50
|
end
|
54
51
|
record
|
55
|
-
rescue RangeError
|
52
|
+
rescue ::RangeError
|
56
53
|
raise RecordNotFound.new("Couldn't find #{name} with an out of range value for '#{primary_key}'",
|
57
54
|
name, primary_key)
|
58
55
|
end
|
59
56
|
end
|
60
57
|
end
|
61
58
|
end
|
62
|
-
end
|
59
|
+
end
|
@@ -5,50 +5,40 @@ module ActiveRecord
|
|
5
5
|
private
|
6
6
|
|
7
7
|
silence_warnings do
|
8
|
-
def
|
8
|
+
def _update_row(attribute_names, attempted_action = "update")
|
9
9
|
return super unless locking_enabled?
|
10
|
-
return 0 if attribute_names.empty?
|
11
|
-
|
12
|
-
lock_col = self.class.locking_column
|
13
|
-
previous_lock_value = send(lock_col).to_i
|
14
|
-
increment_lock
|
15
|
-
|
16
|
-
attribute_names += [lock_col]
|
17
|
-
attribute_names.uniq!
|
18
10
|
|
19
11
|
begin
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
12
|
+
locking_column = self.class.locking_column
|
13
|
+
previous_lock_value = read_attribute_before_type_cast(locking_column)
|
14
|
+
attribute_names << locking_column
|
15
|
+
|
16
|
+
self[locking_column] += 1
|
17
|
+
|
18
|
+
if composite?
|
19
|
+
affected_rows = self.class.unscoped._update_record(
|
20
|
+
arel_attributes_with_values(attribute_names),
|
21
|
+
Hash[self.class.primary_key.zip(id_in_database)].merge(
|
22
|
+
locking_column => previous_lock_value
|
23
|
+
)
|
31
24
|
)
|
32
25
|
else
|
33
|
-
affected_rows =
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
attributes_for_update(attribute_names).map do |name|
|
38
|
-
[name, _read_attribute(name)]
|
39
|
-
end.to_h
|
26
|
+
affected_rows = self.class.unscoped._update_record(
|
27
|
+
arel_attributes_with_values(attribute_names),
|
28
|
+
self.class.primary_key => id_in_database,
|
29
|
+
locking_column => previous_lock_value
|
40
30
|
)
|
41
31
|
end
|
42
32
|
|
43
|
-
|
44
|
-
raise ActiveRecord::StaleObjectError.new(self,
|
33
|
+
if affected_rows != 1
|
34
|
+
raise ActiveRecord::StaleObjectError.new(self, attempted_action)
|
45
35
|
end
|
46
36
|
|
47
37
|
affected_rows
|
48
38
|
|
49
|
-
|
39
|
+
# If something went wrong, revert the locking_column value.
|
50
40
|
rescue Exception
|
51
|
-
|
41
|
+
self[locking_column] = previous_lock_value.to_i
|
52
42
|
raise
|
53
43
|
end
|
54
44
|
end
|
@@ -41,7 +41,7 @@ module ActiveRecord
|
|
41
41
|
attribute_ids = attribute_collection['id'] || attribute_collection[:id]
|
42
42
|
if attribute_ids
|
43
43
|
ids = CompositePrimaryKeys::CompositeKeys.parse(attribute_ids)
|
44
|
-
eq_predicates =
|
44
|
+
eq_predicates = CompositePrimaryKeys::Predicates.cpk_id_predicate(association.klass.arel_table, association.klass.primary_key, ids)
|
45
45
|
association.scope.where(eq_predicates).to_a
|
46
46
|
else
|
47
47
|
[]
|
@@ -7,11 +7,17 @@ module CompositePrimaryKeys
|
|
7
7
|
return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self)
|
8
8
|
end
|
9
9
|
|
10
|
-
if arel.orders.present?
|
11
|
-
|
10
|
+
if arel.orders.present?
|
11
|
+
act_on_ignored_order(error_on_ignore)
|
12
12
|
end
|
13
13
|
|
14
|
-
|
14
|
+
batch_limit = of
|
15
|
+
if limit_value
|
16
|
+
remaining = limit_value
|
17
|
+
batch_limit = remaining if remaining < batch_limit
|
18
|
+
end
|
19
|
+
|
20
|
+
relation = relation.reorder(batch_order).limit(batch_limit)
|
15
21
|
relation = apply_limits(relation, start, finish)
|
16
22
|
batch_relation = relation
|
17
23
|
|
@@ -39,9 +45,22 @@ module CompositePrimaryKeys
|
|
39
45
|
|
40
46
|
yield yielded_relation
|
41
47
|
|
42
|
-
break if ids.length <
|
48
|
+
break if ids.length < batch_limit
|
49
|
+
|
50
|
+
if limit_value
|
51
|
+
remaining -= ids.length
|
52
|
+
|
53
|
+
if remaining == 0
|
54
|
+
# Saves a useless iteration when the limit is a multiple of the
|
55
|
+
# batch size.
|
56
|
+
break
|
57
|
+
elsif remaining < batch_limit
|
58
|
+
relation = relation.limit(remaining)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
43
62
|
# CPK
|
44
|
-
|
63
|
+
batch_relation = relation.where(arel_attribute(primary_key).gt(primary_key_offset))
|
45
64
|
batch_relation = if composite?
|
46
65
|
# CPK
|
47
66
|
# Lexicographically select records
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class PredicateBuilder
|
3
|
+
class AssociationQueryHandler
|
4
|
+
def call(attribute, value)
|
5
|
+
queries = {}
|
6
|
+
|
7
|
+
table = value.associated_table
|
8
|
+
if value.base_class
|
9
|
+
queries[table.association_foreign_type.to_s] = value.base_class.name
|
10
|
+
end
|
11
|
+
|
12
|
+
# CPK
|
13
|
+
# queries[table.association_foreign_key.to_s] = value.ids
|
14
|
+
# predicate_builder.build_from_hash(queries)
|
15
|
+
if table.association_foreign_key.is_a?(Array)
|
16
|
+
values = case value.value
|
17
|
+
when Relation
|
18
|
+
value.ids.map {|record| record.ids}
|
19
|
+
when Array
|
20
|
+
value.ids
|
21
|
+
else
|
22
|
+
[value.ids]
|
23
|
+
end
|
24
|
+
|
25
|
+
CompositePrimaryKeys::Predicates.cpk_in_predicate(attribute.relation, table.association_foreign_key, values)
|
26
|
+
else
|
27
|
+
queries[table.association_foreign_key.to_s] = value.ids
|
28
|
+
predicate_builder.build_from_hash(queries)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -17,7 +17,7 @@ module ActiveRecord
|
|
17
17
|
end
|
18
18
|
|
19
19
|
silence_warnings do
|
20
|
-
def _update_record(values,
|
20
|
+
def _update_record(values, constraints) # :nodoc:
|
21
21
|
substitutes, binds = substitute_values values
|
22
22
|
|
23
23
|
scope = @klass.unscoped
|
@@ -27,15 +27,21 @@ module ActiveRecord
|
|
27
27
|
end
|
28
28
|
|
29
29
|
# CPK
|
30
|
+
# relation = scope.where(constraints)
|
30
31
|
if self.composite?
|
31
|
-
|
32
|
-
|
33
|
-
|
32
|
+
if constraints[@klass.primary_key]
|
33
|
+
cpks = @klass.primary_key.zip(constraints[@klass.primary_key]).to_h
|
34
|
+
others = constraints.except(@klass.primary_key)
|
35
|
+
else
|
36
|
+
cpks = Hash.new
|
37
|
+
others = Hash.new
|
38
|
+
constraints.each do |field, value|
|
39
|
+
(@klass.primary_key.include?(field) ? cpks : others)[field] = value
|
40
|
+
end
|
34
41
|
end
|
35
|
-
|
36
|
-
relation = @klass.unscoped.where(cpk_id_predicate(@klass.arel_table, @klass.primary_key, id_update))
|
42
|
+
relation = @klass.unscoped.where(cpk_id_predicate(@klass.arel_table, cpks.keys, cpks.values)).where(others)
|
37
43
|
else
|
38
|
-
relation = scope.where(
|
44
|
+
relation = scope.where(constraints)
|
39
45
|
end
|
40
46
|
|
41
47
|
|
@@ -26,7 +26,7 @@ $:.unshift(File.dirname(__FILE__)) unless
|
|
26
26
|
|
27
27
|
unless defined?(ActiveRecord)
|
28
28
|
require 'rubygems'
|
29
|
-
gem 'activerecord', '~>5.
|
29
|
+
gem 'activerecord', ['~>5.1.0', '>= 5.1.5']
|
30
30
|
require 'active_record'
|
31
31
|
end
|
32
32
|
|
@@ -59,12 +59,12 @@ require 'active_record/nested_attributes'
|
|
59
59
|
|
60
60
|
require 'active_record/connection_adapters/abstract_adapter'
|
61
61
|
require 'active_record/connection_adapters/abstract_mysql_adapter'
|
62
|
+
require 'active_record/connection_adapters/postgresql/database_statements'
|
62
63
|
|
63
64
|
require 'active_record/relation/batches'
|
64
65
|
require 'active_record/relation/where_clause'
|
65
66
|
require 'active_record/relation/calculations'
|
66
67
|
require 'active_record/relation/finder_methods'
|
67
|
-
require 'active_record/relation/predicate_builder'
|
68
68
|
require 'active_record/relation/query_methods'
|
69
69
|
|
70
70
|
# CPK files
|
@@ -98,12 +98,13 @@ require 'composite_primary_keys/nested_attributes'
|
|
98
98
|
|
99
99
|
require 'composite_primary_keys/connection_adapters/abstract_adapter'
|
100
100
|
require 'composite_primary_keys/connection_adapters/abstract_mysql_adapter'
|
101
|
+
require 'composite_primary_keys/connection_adapters/postgresql/database_statements'
|
101
102
|
|
102
103
|
require 'composite_primary_keys/relation/batches'
|
103
104
|
require 'composite_primary_keys/relation/where_clause'
|
104
105
|
require 'composite_primary_keys/relation/calculations'
|
105
106
|
require 'composite_primary_keys/relation/finder_methods'
|
106
|
-
require 'composite_primary_keys/relation/predicate_builder'
|
107
|
+
require 'composite_primary_keys/relation/predicate_builder/association_query_handler'
|
107
108
|
require 'composite_primary_keys/relation/query_methods'
|
108
109
|
|
109
110
|
require 'composite_primary_keys/composite_relation'
|
data/test/test_find.rb
CHANGED
@@ -93,13 +93,19 @@ class TestFind < ActiveSupport::TestCase
|
|
93
93
|
assert_equal(Department.count, num_found)
|
94
94
|
end
|
95
95
|
|
96
|
-
def
|
96
|
+
def test_find_by_one_association
|
97
97
|
department = departments(:engineering)
|
98
98
|
employees = Employee.where(:department => department)
|
99
|
-
assert_equal(2, employees.count)
|
99
|
+
assert_equal(2, employees.to_a.count)
|
100
100
|
end
|
101
101
|
|
102
|
-
def
|
102
|
+
def test_find_by_all_associations
|
103
|
+
departments = Department.all
|
104
|
+
employees = Employee.where(:department => departments)
|
105
|
+
assert_equal(4, employees.to_a.count)
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_expand_all
|
103
109
|
departments = Department.all
|
104
110
|
employees = Employee.where(:department => departments)
|
105
111
|
assert_equal(4, employees.count)
|
data/test/test_preload.rb
CHANGED
@@ -91,4 +91,11 @@ class TestPreload < ActiveSupport::TestCase
|
|
91
91
|
assert_equal(1, employees.first.groups_2.size)
|
92
92
|
assert_equal(2, employees.second.groups_2.size)
|
93
93
|
end
|
94
|
+
|
95
|
+
def test_preload_settings_inversion
|
96
|
+
users = User.preload(:readings).all
|
97
|
+
reading = users.first.readings.first
|
98
|
+
|
99
|
+
assert_equal(true, reading.association(:user).loaded?)
|
100
|
+
end
|
94
101
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: composite_primary_keys
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 10.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Charlie Savage
|
@@ -16,20 +16,20 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 5.
|
19
|
+
version: 5.1.0
|
20
20
|
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 5.
|
22
|
+
version: 5.1.6
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - "~>"
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: 5.
|
29
|
+
version: 5.1.0
|
30
30
|
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 5.
|
32
|
+
version: 5.1.6
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: rake
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -119,6 +119,7 @@ files:
|
|
119
119
|
- lib/composite_primary_keys/composite_relation.rb
|
120
120
|
- lib/composite_primary_keys/connection_adapters/abstract_adapter.rb
|
121
121
|
- lib/composite_primary_keys/connection_adapters/abstract_mysql_adapter.rb
|
122
|
+
- lib/composite_primary_keys/connection_adapters/postgresql/database_statements.rb
|
122
123
|
- lib/composite_primary_keys/connection_adapters/sqlite3_adapter.rb
|
123
124
|
- lib/composite_primary_keys/connection_adapters/sqlserver_adapter.rb
|
124
125
|
- lib/composite_primary_keys/core.rb
|
@@ -130,7 +131,7 @@ files:
|
|
130
131
|
- lib/composite_primary_keys/relation/batches.rb
|
131
132
|
- lib/composite_primary_keys/relation/calculations.rb
|
132
133
|
- lib/composite_primary_keys/relation/finder_methods.rb
|
133
|
-
- lib/composite_primary_keys/relation/predicate_builder.rb
|
134
|
+
- lib/composite_primary_keys/relation/predicate_builder/association_query_handler.rb
|
134
135
|
- lib/composite_primary_keys/relation/query_methods.rb
|
135
136
|
- lib/composite_primary_keys/relation/where_clause.rb
|
136
137
|
- lib/composite_primary_keys/sanitization.rb
|
@@ -1,26 +0,0 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
class PredicateBuilder
|
3
|
-
silence_warnings do
|
4
|
-
def expand(column, value)
|
5
|
-
# Find the foreign key when using queries such as:
|
6
|
-
# Post.where(author: author)
|
7
|
-
#
|
8
|
-
# For polymorphic relationships, find the foreign key and type:
|
9
|
-
# PriceEstimate.where(estimate_of: treasure)
|
10
|
-
|
11
|
-
# CPK
|
12
|
-
if Base === Array(value).first && Array(value).first.composite? && reflection = table.associated_with?(column)
|
13
|
-
columns = reflection.foreign_key
|
14
|
-
values = Array(value).map{|v| columns.map{|c| v.public_send(c) }}
|
15
|
-
cpk_predicate_builder = Class.new.extend(CompositePrimaryKeys::Predicates)
|
16
|
-
predicate = cpk_predicate_builder.cpk_in_predicate(table.send(:arel_table), columns, values)
|
17
|
-
return predicate
|
18
|
-
else
|
19
|
-
# Original code
|
20
|
-
value = AssociationQueryHandler.value_for(table, column, value) if table.associated_with?(column)
|
21
|
-
build(table.arel_attribute(column), value)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|