composite_primary_keys 8.1.0 → 8.1.1
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 +642 -625
- data/README.rdoc +5 -2
- data/lib/composite_primary_keys.rb +115 -115
- data/lib/composite_primary_keys/associations/association.rb +23 -23
- data/lib/composite_primary_keys/associations/association_scope.rb +73 -73
- data/lib/composite_primary_keys/associations/collection_association.rb +14 -14
- data/lib/composite_primary_keys/associations/has_many_association.rb +69 -69
- data/lib/composite_primary_keys/associations/join_dependency.rb +87 -87
- data/lib/composite_primary_keys/associations/preloader/association.rb +90 -90
- data/lib/composite_primary_keys/associations/singular_association.rb +18 -18
- data/lib/composite_primary_keys/attribute_methods.rb +9 -9
- data/lib/composite_primary_keys/attribute_methods/dirty.rb +29 -29
- data/lib/composite_primary_keys/attribute_methods/read.rb +24 -24
- data/lib/composite_primary_keys/attribute_methods/write.rb +30 -30
- data/lib/composite_primary_keys/attribute_set/builder.rb +19 -19
- data/lib/composite_primary_keys/base.rb +129 -135
- data/lib/composite_primary_keys/composite_arrays.rb +43 -43
- data/lib/composite_primary_keys/connection_adapters/abstract/connection_specification_changes.rb +2 -3
- data/lib/composite_primary_keys/core.rb +60 -60
- data/lib/composite_primary_keys/persistence.rb +56 -56
- data/lib/composite_primary_keys/relation.rb +68 -68
- data/lib/composite_primary_keys/relation/calculations.rb +78 -78
- data/lib/composite_primary_keys/relation/finder_methods.rb +179 -179
- data/lib/composite_primary_keys/sanitization.rb +52 -52
- data/lib/composite_primary_keys/validations/uniqueness.rb +36 -36
- data/lib/composite_primary_keys/version.rb +8 -8
- data/tasks/databases/sqlserver.rake +27 -27
- data/test/abstract_unit.rb +114 -113
- data/test/connections/databases.example.yml +25 -25
- data/test/connections/native_sqlserver/connection.rb +11 -11
- data/test/fixtures/db_definitions/mysql.sql +218 -218
- data/test/fixtures/db_definitions/postgresql.sql +220 -220
- data/test/fixtures/db_definitions/sqlite.sql +206 -206
- data/test/fixtures/db_definitions/sqlserver.drop.sql +91 -91
- data/test/fixtures/db_definitions/sqlserver.sql +226 -226
- data/test/fixtures/employee.rb +11 -11
- data/test/fixtures/salary.rb +5 -5
- data/test/test_associations.rb +341 -340
- data/test/test_attributes.rb +60 -60
- data/test/test_create.rb +157 -157
- data/test/test_delete.rb +158 -158
- data/test/test_delete_all.rb +33 -28
- data/test/test_enum.rb +21 -21
- data/test/test_equal.rb +26 -26
- data/test/test_find.rb +119 -118
- data/test/test_habtm.rb +117 -113
- data/test/test_polymorphic.rb +27 -26
- data/test/test_tutorial_example.rb +25 -25
- metadata +44 -2
@@ -1,69 +1,69 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module Associations
|
3
|
-
class HasManyAssociation
|
4
|
-
def delete_count(method, scope)
|
5
|
-
if method == :delete_all
|
6
|
-
scope.delete_all
|
7
|
-
else
|
8
|
-
# CPK
|
9
|
-
# scope.update_all(reflection.foreign_key => nil)
|
10
|
-
conds = Array(reflection.foreign_key).inject(Hash.new) do |mem, key|
|
11
|
-
mem[key] = nil
|
12
|
-
mem
|
13
|
-
end
|
14
|
-
scope.update_all(conds)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def delete_records(records, method)
|
19
|
-
if method == :destroy
|
20
|
-
records.each(&:destroy!)
|
21
|
-
update_counter(-records.length) unless inverse_updates_counter_cache?
|
22
|
-
else
|
23
|
-
if records == :all || !reflection.klass.primary_key
|
24
|
-
scope = self.scope
|
25
|
-
else
|
26
|
-
# CPK
|
27
|
-
# scope = self.scope.where(reflection.klass.primary_key => records)
|
28
|
-
table = Arel::Table.new(reflection.table_name)
|
29
|
-
and_conditions = records.map do |record|
|
30
|
-
eq_conditions = Array(reflection.association_primary_key).map do |name|
|
31
|
-
table[name].eq(record[name])
|
32
|
-
end
|
33
|
-
Arel::Nodes::And.new(eq_conditions)
|
34
|
-
end
|
35
|
-
|
36
|
-
condition = and_conditions.shift
|
37
|
-
and_conditions.each do |and_condition|
|
38
|
-
condition = condition.or(and_condition)
|
39
|
-
end
|
40
|
-
|
41
|
-
scope = self.scope.where(condition)
|
42
|
-
end
|
43
|
-
|
44
|
-
if method == :delete_all
|
45
|
-
update_counter(-scope.delete_all)
|
46
|
-
else
|
47
|
-
# CPK
|
48
|
-
# update_counter(-scope.update_all(reflection.foreign_key => nil))
|
49
|
-
update_hash = Array(reflection.foreign_key).inject(Hash.new) do |hash, key|
|
50
|
-
hash[key] = nil
|
51
|
-
hash
|
52
|
-
end
|
53
|
-
update_counter(-scope.update_all(update_hash))
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def foreign_key_present?
|
59
|
-
if reflection.klass.primary_key
|
60
|
-
# CPK
|
61
|
-
# owner.attribute_present?(reflection.association_primary_key)
|
62
|
-
Array(reflection.klass.primary_key).all? {|key| owner.attribute_present?(key)}
|
63
|
-
else
|
64
|
-
false
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class HasManyAssociation
|
4
|
+
def delete_count(method, scope)
|
5
|
+
if method == :delete_all
|
6
|
+
scope.delete_all
|
7
|
+
else
|
8
|
+
# CPK
|
9
|
+
# scope.update_all(reflection.foreign_key => nil)
|
10
|
+
conds = Array(reflection.foreign_key).inject(Hash.new) do |mem, key|
|
11
|
+
mem[key] = nil
|
12
|
+
mem
|
13
|
+
end
|
14
|
+
scope.update_all(conds)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete_records(records, method)
|
19
|
+
if method == :destroy
|
20
|
+
records.each(&:destroy!)
|
21
|
+
update_counter(-records.length) unless inverse_updates_counter_cache?
|
22
|
+
else
|
23
|
+
if records == :all || !reflection.klass.primary_key
|
24
|
+
scope = self.scope
|
25
|
+
else
|
26
|
+
# CPK
|
27
|
+
# scope = self.scope.where(reflection.klass.primary_key => records)
|
28
|
+
table = Arel::Table.new(reflection.table_name)
|
29
|
+
and_conditions = records.map do |record|
|
30
|
+
eq_conditions = Array(reflection.association_primary_key).map do |name|
|
31
|
+
table[name].eq(record[name])
|
32
|
+
end
|
33
|
+
Arel::Nodes::And.new(eq_conditions)
|
34
|
+
end
|
35
|
+
|
36
|
+
condition = and_conditions.shift
|
37
|
+
and_conditions.each do |and_condition|
|
38
|
+
condition = condition.or(and_condition)
|
39
|
+
end
|
40
|
+
|
41
|
+
scope = self.scope.where(condition)
|
42
|
+
end
|
43
|
+
|
44
|
+
if method == :delete_all
|
45
|
+
update_counter(-scope.delete_all)
|
46
|
+
else
|
47
|
+
# CPK
|
48
|
+
# update_counter(-scope.update_all(reflection.foreign_key => nil))
|
49
|
+
update_hash = Array(reflection.foreign_key).inject(Hash.new) do |hash, key|
|
50
|
+
hash[key] = nil
|
51
|
+
hash
|
52
|
+
end
|
53
|
+
update_counter(-scope.update_all(update_hash))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def foreign_key_present?
|
59
|
+
if reflection.klass.primary_key
|
60
|
+
# CPK
|
61
|
+
# owner.attribute_present?(reflection.association_primary_key)
|
62
|
+
Array(reflection.klass.primary_key).all? {|key| owner.attribute_present?(key)}
|
63
|
+
else
|
64
|
+
false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -1,87 +1,87 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module Associations
|
3
|
-
class JoinDependency
|
4
|
-
class Aliases # :nodoc:
|
5
|
-
def column_alias(node, column)
|
6
|
-
# CPK
|
7
|
-
#@alias_cache[node][column]
|
8
|
-
if column.kind_of?(Array)
|
9
|
-
column.map do |a_column|
|
10
|
-
@alias_cache[node][a_column]
|
11
|
-
end
|
12
|
-
else
|
13
|
-
@alias_cache[node][column]
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def instantiate(result_set, aliases)
|
19
|
-
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
20
|
-
|
21
|
-
seen = Hash.new { |h,parent_klass|
|
22
|
-
h[parent_klass] = Hash.new { |i,parent_id|
|
23
|
-
i[parent_id] = Hash.new { |j,child_klass| j[child_klass] = {} }
|
24
|
-
}
|
25
|
-
}
|
26
|
-
|
27
|
-
model_cache = Hash.new { |h,klass| h[klass] = {} }
|
28
|
-
parents = model_cache[join_root]
|
29
|
-
column_aliases = aliases.column_aliases join_root
|
30
|
-
|
31
|
-
result_set.each { |row_hash|
|
32
|
-
# CPK
|
33
|
-
primary_id = if primary_key.kind_of?(Array)
|
34
|
-
primary_key.map {|key| row_hash[key]}
|
35
|
-
else
|
36
|
-
row_hash[primary_key]
|
37
|
-
end
|
38
|
-
parent = parents[primary_id] ||= join_root.instantiate(row_hash, column_aliases)
|
39
|
-
construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
|
40
|
-
}
|
41
|
-
|
42
|
-
parents.values
|
43
|
-
end
|
44
|
-
|
45
|
-
def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
|
46
|
-
primary_id = ar_parent.id
|
47
|
-
|
48
|
-
parent.children.each do |node|
|
49
|
-
if node.reflection.collection?
|
50
|
-
other = ar_parent.association(node.reflection.name)
|
51
|
-
other.loaded!
|
52
|
-
else
|
53
|
-
if ar_parent.association_cache.key?(node.reflection.name)
|
54
|
-
model = ar_parent.association(node.reflection.name).target
|
55
|
-
construct(model, node, row, rs, seen, model_cache, aliases)
|
56
|
-
next
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
key = aliases.column_alias(node, node.primary_key)
|
61
|
-
|
62
|
-
# CPK
|
63
|
-
if key.is_a?(Array)
|
64
|
-
id = Array(key).map do |column_alias|
|
65
|
-
value = row[column_alias]
|
66
|
-
end
|
67
|
-
# At least the first value in the key has to be set. Should we require all values to be set?
|
68
|
-
next if id.first.nil?
|
69
|
-
else
|
70
|
-
id = row[key]
|
71
|
-
next if id.nil?
|
72
|
-
end
|
73
|
-
|
74
|
-
model = seen[parent.base_klass][primary_id][node.base_klass][id]
|
75
|
-
|
76
|
-
if model
|
77
|
-
construct(model, node, row, rs, seen, model_cache, aliases)
|
78
|
-
else
|
79
|
-
model = construct_model(ar_parent, node, row, model_cache, id, aliases)
|
80
|
-
seen[parent.base_klass][primary_id][node.base_klass][id] = model
|
81
|
-
construct(model, node, row, rs, seen, model_cache, aliases)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class JoinDependency
|
4
|
+
class Aliases # :nodoc:
|
5
|
+
def column_alias(node, column)
|
6
|
+
# CPK
|
7
|
+
#@alias_cache[node][column]
|
8
|
+
if column.kind_of?(Array)
|
9
|
+
column.map do |a_column|
|
10
|
+
@alias_cache[node][a_column]
|
11
|
+
end
|
12
|
+
else
|
13
|
+
@alias_cache[node][column]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def instantiate(result_set, aliases)
|
19
|
+
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
20
|
+
|
21
|
+
seen = Hash.new { |h,parent_klass|
|
22
|
+
h[parent_klass] = Hash.new { |i,parent_id|
|
23
|
+
i[parent_id] = Hash.new { |j,child_klass| j[child_klass] = {} }
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
model_cache = Hash.new { |h,klass| h[klass] = {} }
|
28
|
+
parents = model_cache[join_root]
|
29
|
+
column_aliases = aliases.column_aliases join_root
|
30
|
+
|
31
|
+
result_set.each { |row_hash|
|
32
|
+
# CPK
|
33
|
+
primary_id = if primary_key.kind_of?(Array)
|
34
|
+
primary_key.map {|key| row_hash[key]}
|
35
|
+
else
|
36
|
+
row_hash[primary_key]
|
37
|
+
end
|
38
|
+
parent = parents[primary_id] ||= join_root.instantiate(row_hash, column_aliases)
|
39
|
+
construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
|
40
|
+
}
|
41
|
+
|
42
|
+
parents.values
|
43
|
+
end
|
44
|
+
|
45
|
+
def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
|
46
|
+
primary_id = ar_parent.id
|
47
|
+
|
48
|
+
parent.children.each do |node|
|
49
|
+
if node.reflection.collection?
|
50
|
+
other = ar_parent.association(node.reflection.name)
|
51
|
+
other.loaded!
|
52
|
+
else
|
53
|
+
if ar_parent.association_cache.key?(node.reflection.name)
|
54
|
+
model = ar_parent.association(node.reflection.name).target
|
55
|
+
construct(model, node, row, rs, seen, model_cache, aliases)
|
56
|
+
next
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
key = aliases.column_alias(node, node.primary_key)
|
61
|
+
|
62
|
+
# CPK
|
63
|
+
if key.is_a?(Array)
|
64
|
+
id = Array(key).map do |column_alias|
|
65
|
+
value = row[column_alias]
|
66
|
+
end
|
67
|
+
# At least the first value in the key has to be set. Should we require all values to be set?
|
68
|
+
next if id.first.nil?
|
69
|
+
else
|
70
|
+
id = row[key]
|
71
|
+
next if id.nil?
|
72
|
+
end
|
73
|
+
|
74
|
+
model = seen[parent.base_klass][primary_id][node.base_klass][id]
|
75
|
+
|
76
|
+
if model
|
77
|
+
construct(model, node, row, rs, seen, model_cache, aliases)
|
78
|
+
else
|
79
|
+
model = construct_model(ar_parent, node, row, model_cache, id, aliases)
|
80
|
+
seen[parent.base_klass][primary_id][node.base_klass][id] = model
|
81
|
+
construct(model, node, row, rs, seen, model_cache, aliases)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -1,90 +1,90 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module Associations
|
3
|
-
class Preloader
|
4
|
-
class Association
|
5
|
-
def query_scope(ids)
|
6
|
-
# CPK
|
7
|
-
# scope.where(association_key.in(ids))
|
8
|
-
|
9
|
-
if reflection.foreign_key.is_a?(Array)
|
10
|
-
predicate = cpk_in_predicate(table, reflection.foreign_key, ids)
|
11
|
-
scope.where(predicate)
|
12
|
-
else
|
13
|
-
scope.where(association_key.in(ids))
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def associated_records_by_owner(preloader)
|
18
|
-
owners_map = owners_by_key
|
19
|
-
# CPK
|
20
|
-
# owner_keys = owners_map.keys.compact
|
21
|
-
owner_keys = if reflection.foreign_key.is_a?(Array)
|
22
|
-
owners.map do |owner|
|
23
|
-
Array(owner_key_name).map do |owner_key|
|
24
|
-
owner[owner_key]
|
25
|
-
end
|
26
|
-
end.compact.uniq
|
27
|
-
else
|
28
|
-
owners_map.keys.compact
|
29
|
-
end
|
30
|
-
|
31
|
-
# Each record may have multiple owners, and vice-versa
|
32
|
-
records_by_owner = owners.each_with_object({}) do |owner,h|
|
33
|
-
h[owner] = []
|
34
|
-
end
|
35
|
-
|
36
|
-
if owner_keys.any?
|
37
|
-
# Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
|
38
|
-
# Make several smaller queries if necessary or make one query if the adapter supports it
|
39
|
-
sliced = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
|
40
|
-
|
41
|
-
records = load_slices sliced
|
42
|
-
records.each do |record, owner_key|
|
43
|
-
owners_map[owner_key].each do |owner|
|
44
|
-
records_by_owner[owner] << record
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
records_by_owner
|
50
|
-
end
|
51
|
-
|
52
|
-
def load_slices(slices)
|
53
|
-
@preloaded_records = slices.flat_map { |slice|
|
54
|
-
records_for(slice)
|
55
|
-
}
|
56
|
-
|
57
|
-
@preloaded_records.map { |record|
|
58
|
-
# CPK
|
59
|
-
#[record, record[association_key_name]]
|
60
|
-
owner_key = Array(association_key_name).map do |key_name|
|
61
|
-
record[key_name]
|
62
|
-
end.join(CompositePrimaryKeys::ID_SEP)
|
63
|
-
[record, owner_key]
|
64
|
-
}
|
65
|
-
end
|
66
|
-
|
67
|
-
def owners_by_key
|
68
|
-
@owners_by_key ||= if key_conversion_required?
|
69
|
-
owners.group_by do |owner|
|
70
|
-
# CPK
|
71
|
-
# owner[owner_key_name].to_s
|
72
|
-
key = Array(owner_key_name).map do |key_name|
|
73
|
-
owner[key_name]
|
74
|
-
end.join(CompositePrimaryKeys::ID_SEP)
|
75
|
-
end
|
76
|
-
else
|
77
|
-
owners.group_by do |owner|
|
78
|
-
# CPK
|
79
|
-
# owner[owner_key_name]
|
80
|
-
key = Array(owner_key_name).map do |key_name|
|
81
|
-
owner[key_name]
|
82
|
-
end.join(CompositePrimaryKeys::ID_SEP)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class Preloader
|
4
|
+
class Association
|
5
|
+
def query_scope(ids)
|
6
|
+
# CPK
|
7
|
+
# scope.where(association_key.in(ids))
|
8
|
+
|
9
|
+
if reflection.foreign_key.is_a?(Array)
|
10
|
+
predicate = cpk_in_predicate(table, reflection.foreign_key, ids)
|
11
|
+
scope.where(predicate)
|
12
|
+
else
|
13
|
+
scope.where(association_key.in(ids))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def associated_records_by_owner(preloader)
|
18
|
+
owners_map = owners_by_key
|
19
|
+
# CPK
|
20
|
+
# owner_keys = owners_map.keys.compact
|
21
|
+
owner_keys = if reflection.foreign_key.is_a?(Array)
|
22
|
+
owners.map do |owner|
|
23
|
+
Array(owner_key_name).map do |owner_key|
|
24
|
+
owner[owner_key]
|
25
|
+
end
|
26
|
+
end.compact.uniq
|
27
|
+
else
|
28
|
+
owners_map.keys.compact
|
29
|
+
end
|
30
|
+
|
31
|
+
# Each record may have multiple owners, and vice-versa
|
32
|
+
records_by_owner = owners.each_with_object({}) do |owner,h|
|
33
|
+
h[owner] = []
|
34
|
+
end
|
35
|
+
|
36
|
+
if owner_keys.any?
|
37
|
+
# Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
|
38
|
+
# Make several smaller queries if necessary or make one query if the adapter supports it
|
39
|
+
sliced = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
|
40
|
+
|
41
|
+
records = load_slices sliced
|
42
|
+
records.each do |record, owner_key|
|
43
|
+
owners_map[owner_key].each do |owner|
|
44
|
+
records_by_owner[owner] << record
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
records_by_owner
|
50
|
+
end
|
51
|
+
|
52
|
+
def load_slices(slices)
|
53
|
+
@preloaded_records = slices.flat_map { |slice|
|
54
|
+
records_for(slice)
|
55
|
+
}
|
56
|
+
|
57
|
+
@preloaded_records.map { |record|
|
58
|
+
# CPK
|
59
|
+
#[record, record[association_key_name]]
|
60
|
+
owner_key = Array(association_key_name).map do |key_name|
|
61
|
+
record[key_name]
|
62
|
+
end.join(CompositePrimaryKeys::ID_SEP)
|
63
|
+
[record, owner_key]
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def owners_by_key
|
68
|
+
@owners_by_key ||= if key_conversion_required?
|
69
|
+
owners.group_by do |owner|
|
70
|
+
# CPK
|
71
|
+
# owner[owner_key_name].to_s
|
72
|
+
key = Array(owner_key_name).map do |key_name|
|
73
|
+
owner[key_name]
|
74
|
+
end.join(CompositePrimaryKeys::ID_SEP)
|
75
|
+
end
|
76
|
+
else
|
77
|
+
owners.group_by do |owner|
|
78
|
+
# CPK
|
79
|
+
# owner[owner_key_name]
|
80
|
+
key = Array(owner_key_name).map do |key_name|
|
81
|
+
owner[key_name]
|
82
|
+
end.join(CompositePrimaryKeys::ID_SEP)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|