composite_primary_keys 9.0.10 → 10.0.5
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 +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
|