composite_primary_keys 12.0.5 → 13.0.0
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 +883 -862
- data/README.rdoc +181 -180
- data/lib/composite_primary_keys.rb +119 -118
- data/lib/composite_primary_keys/active_model/attribute_assignment.rb +19 -19
- data/lib/composite_primary_keys/associations/association_scope.rb +66 -68
- data/lib/composite_primary_keys/associations/join_dependency.rb +118 -103
- data/lib/composite_primary_keys/attribute_methods.rb +21 -9
- data/lib/composite_primary_keys/attribute_methods/primary_key.rb +0 -2
- data/lib/composite_primary_keys/attribute_methods/read.rb +30 -30
- data/lib/composite_primary_keys/attribute_methods/write.rb +35 -35
- data/lib/composite_primary_keys/base.rb +141 -141
- data/lib/composite_primary_keys/connection_adapters/abstract/database_statements.rb +37 -22
- data/lib/composite_primary_keys/connection_adapters/sqlserver/database_statements.rb +44 -44
- data/lib/composite_primary_keys/core.rb +48 -48
- data/lib/composite_primary_keys/nested_attributes.rb +1 -1
- data/lib/composite_primary_keys/persistence.rb +82 -81
- data/lib/composite_primary_keys/reflection.rb +91 -29
- data/lib/composite_primary_keys/relation.rb +197 -193
- data/lib/composite_primary_keys/relation/batches.rb +16 -8
- data/lib/composite_primary_keys/relation/calculations.rb +104 -81
- data/lib/composite_primary_keys/relation/finder_methods.rb +235 -235
- data/lib/composite_primary_keys/relation/predicate_builder/association_query_value.rb +39 -20
- data/lib/composite_primary_keys/relation/query_methods.rb +42 -42
- data/lib/composite_primary_keys/relation/where_clause.rb +18 -23
- data/lib/composite_primary_keys/table_metadata.rb +11 -0
- data/lib/composite_primary_keys/version.rb +8 -8
- data/test/abstract_unit.rb +114 -114
- data/test/connections/databases.ci.yml +22 -19
- data/test/fixtures/db_definitions/db2-create-tables.sql +112 -112
- data/test/fixtures/db_definitions/db2-drop-tables.sql +16 -16
- data/test/fixtures/db_definitions/mysql.sql +180 -180
- data/test/fixtures/db_definitions/oracle.drop.sql +41 -41
- data/test/fixtures/db_definitions/oracle.sql +199 -199
- data/test/fixtures/db_definitions/postgresql.sql +182 -182
- data/test/fixtures/db_definitions/sqlite.sql +169 -169
- data/test/fixtures/db_definitions/sqlserver.sql +176 -176
- data/test/fixtures/department.rb +16 -16
- data/test/fixtures/departments.yml +19 -15
- data/test/fixtures/employees.yml +33 -28
- data/test/fixtures/restaurants_suburbs.yml +10 -10
- data/test/fixtures/streets.yml +16 -16
- data/test/fixtures/suburbs.yml +14 -14
- data/test/fixtures/user.rb +11 -11
- data/test/test_associations.rb +364 -358
- data/test/test_attributes.rb +75 -60
- data/test/test_calculations.rb +49 -42
- data/test/test_create.rb +218 -180
- data/test/test_delete.rb +182 -179
- data/test/test_exists.rb +39 -39
- data/test/test_find.rb +170 -157
- data/test/test_ids.rb +112 -112
- data/test/test_nested_attributes.rb +67 -67
- data/test/test_update.rb +96 -96
- metadata +5 -5
- data/lib/composite_primary_keys/connection_adapters/mysql/database_statements.rb +0 -24
@@ -1,19 +1,19 @@
|
|
1
|
-
module ActiveModel
|
2
|
-
module AttributeAssignment
|
3
|
-
def _assign_attribute(k, v)
|
4
|
-
# CPK. This is super ugly, but if a table has a composite key where one of the fields is named :id we need
|
5
|
-
# to handle it as a single value. Otherwise, we would call the id=(value) method which is expecting
|
6
|
-
# and array of values.
|
7
|
-
if k == 'id' && self.kind_of?(ActiveRecord::Base) && self.composite? && !self.column_for_attribute(k).null
|
8
|
-
self._write_attribute(k, v)
|
9
|
-
else
|
10
|
-
setter = :"#{k}="
|
11
|
-
if respond_to?(setter)
|
12
|
-
public_send(setter, v)
|
13
|
-
else
|
14
|
-
raise UnknownAttributeError.new(self, k)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
1
|
+
module ActiveModel
|
2
|
+
module AttributeAssignment
|
3
|
+
def _assign_attribute(k, v)
|
4
|
+
# CPK. This is super ugly, but if a table has a composite key where one of the fields is named :id we need
|
5
|
+
# to handle it as a single value. Otherwise, we would call the id=(value) method which is expecting
|
6
|
+
# and array of values.
|
7
|
+
if k == 'id' && !v.kind_of?(Array) && self.kind_of?(ActiveRecord::Base) && self.composite? && !self.column_for_attribute(k).null
|
8
|
+
self._write_attribute(k, v)
|
9
|
+
else
|
10
|
+
setter = :"#{k}="
|
11
|
+
if respond_to?(setter)
|
12
|
+
public_send(setter, v)
|
13
|
+
else
|
14
|
+
raise UnknownAttributeError.new(self, k)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,69 +1,67 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module Associations
|
3
|
-
class AssociationScope
|
4
|
-
def self.get_bind_values(owner, chain)
|
5
|
-
binds = []
|
6
|
-
last_reflection = chain.last
|
7
|
-
|
8
|
-
# CPK
|
9
|
-
# binds << last_reflection.join_id_for(owner)
|
10
|
-
values = last_reflection.join_id_for(owner)
|
11
|
-
binds += Array(values)
|
12
|
-
|
13
|
-
if last_reflection.type
|
14
|
-
binds << owner.class.polymorphic_name
|
15
|
-
end
|
16
|
-
|
17
|
-
chain.each_cons(2).each do |reflection, next_reflection|
|
18
|
-
if reflection.type
|
19
|
-
binds << next_reflection.klass.polymorphic_name
|
20
|
-
end
|
21
|
-
end
|
22
|
-
binds
|
23
|
-
end
|
24
|
-
|
25
|
-
def last_chain_scope(scope, reflection, owner)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
#
|
33
|
-
#
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end
|
68
|
-
end
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class AssociationScope
|
4
|
+
def self.get_bind_values(owner, chain)
|
5
|
+
binds = []
|
6
|
+
last_reflection = chain.last
|
7
|
+
|
8
|
+
# CPK
|
9
|
+
# binds << last_reflection.join_id_for(owner)
|
10
|
+
values = last_reflection.join_id_for(owner)
|
11
|
+
binds += Array(values)
|
12
|
+
|
13
|
+
if last_reflection.type
|
14
|
+
binds << owner.class.polymorphic_name
|
15
|
+
end
|
16
|
+
|
17
|
+
chain.each_cons(2).each do |reflection, next_reflection|
|
18
|
+
if reflection.type
|
19
|
+
binds << next_reflection.klass.polymorphic_name
|
20
|
+
end
|
21
|
+
end
|
22
|
+
binds
|
23
|
+
end
|
24
|
+
|
25
|
+
def last_chain_scope(scope, reflection, owner)
|
26
|
+
key = reflection.join_primary_key
|
27
|
+
foreign_key = reflection.join_foreign_key
|
28
|
+
|
29
|
+
table = reflection.aliased_table
|
30
|
+
|
31
|
+
# CPK
|
32
|
+
# value = transform_value(owner[foreign_key])
|
33
|
+
# scope = apply_scope(scope, table, key, value)
|
34
|
+
Array(key).zip(Array(foreign_key)).each do |a_join_key, a_foreign_key|
|
35
|
+
value = transform_value(owner[a_foreign_key])
|
36
|
+
scope = apply_scope(scope, table, a_join_key, value)
|
37
|
+
end
|
38
|
+
|
39
|
+
if reflection.type
|
40
|
+
polymorphic_type = transform_value(owner.class.polymorphic_name)
|
41
|
+
scope = apply_scope(scope, table, reflection.type, polymorphic_type)
|
42
|
+
end
|
43
|
+
|
44
|
+
scope
|
45
|
+
end
|
46
|
+
|
47
|
+
def next_chain_scope(scope, reflection, next_reflection)
|
48
|
+
key = reflection.join_primary_key
|
49
|
+
foreign_key = reflection.join_foreign_key
|
50
|
+
|
51
|
+
table = reflection.aliased_table
|
52
|
+
foreign_table = next_reflection.aliased_table
|
53
|
+
|
54
|
+
# CPK
|
55
|
+
# constraint = table[key].eq(foreign_table[foreign_key])
|
56
|
+
constraint = cpk_join_predicate(table, key, foreign_table, foreign_key)
|
57
|
+
|
58
|
+
if reflection.type
|
59
|
+
value = transform_value(next_reflection.klass.polymorphic_name)
|
60
|
+
scope = apply_scope(scope, table, reflection.type, value)
|
61
|
+
end
|
62
|
+
|
63
|
+
scope.joins!(join(foreign_table, constraint))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
69
67
|
end
|
@@ -1,103 +1,118 @@
|
|
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, &block)
|
19
|
-
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
20
|
-
|
21
|
-
seen = Hash.new { |i,
|
22
|
-
i[
|
23
|
-
j[child_class] = {}
|
24
|
-
}
|
25
|
-
}
|
26
|
-
|
27
|
-
model_cache = Hash.new { |h, klass| h[klass] = {} }
|
28
|
-
parents = model_cache[join_root]
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
construct(
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
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, strict_loading_value, &block)
|
19
|
+
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
20
|
+
|
21
|
+
seen = Hash.new { |i, parent|
|
22
|
+
i[parent] = Hash.new { |j, child_class|
|
23
|
+
j[child_class] = {}
|
24
|
+
}
|
25
|
+
}.compare_by_identity
|
26
|
+
|
27
|
+
model_cache = Hash.new { |h, klass| h[klass] = {} }
|
28
|
+
parents = model_cache[join_root]
|
29
|
+
|
30
|
+
column_aliases = aliases.column_aliases(join_root)
|
31
|
+
column_names = []
|
32
|
+
|
33
|
+
result_set.columns.each do |name|
|
34
|
+
column_names << name unless /\At\d+_r\d+\z/.match?(name)
|
35
|
+
end
|
36
|
+
|
37
|
+
if column_names.empty?
|
38
|
+
column_types = {}
|
39
|
+
else
|
40
|
+
column_types = result_set.column_types
|
41
|
+
unless column_types.empty?
|
42
|
+
attribute_types = join_root.attribute_types
|
43
|
+
column_types = column_types.slice(*column_names).delete_if { |k, _| attribute_types.key?(k) }
|
44
|
+
end
|
45
|
+
column_aliases += column_names.map! { |name| Aliases::Column.new(name, name) }
|
46
|
+
end
|
47
|
+
|
48
|
+
message_bus = ActiveSupport::Notifications.instrumenter
|
49
|
+
|
50
|
+
payload = {
|
51
|
+
record_count: result_set.length,
|
52
|
+
class_name: join_root.base_klass.name
|
53
|
+
}
|
54
|
+
|
55
|
+
message_bus.instrument("instantiation.active_record", payload) do
|
56
|
+
result_set.each { |row_hash|
|
57
|
+
# CPK
|
58
|
+
# parent_key = primary_key ? row_hash[primary_key] : row_hash
|
59
|
+
parent_key = if primary_key.kind_of?(Array)
|
60
|
+
primary_key.map {|key| row_hash[key]}
|
61
|
+
else
|
62
|
+
primary_key ? row_hash[primary_key] : row_hash
|
63
|
+
end
|
64
|
+
|
65
|
+
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, column_types, &block)
|
66
|
+
construct(parent, join_root, row_hash, seen, model_cache, strict_loading_value)
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
parents.values
|
71
|
+
end
|
72
|
+
|
73
|
+
def construct(ar_parent, parent, row, seen, model_cache, strict_loading_value)
|
74
|
+
return if ar_parent.nil?
|
75
|
+
|
76
|
+
parent.children.each do |node|
|
77
|
+
if node.reflection.collection?
|
78
|
+
other = ar_parent.association(node.reflection.name)
|
79
|
+
other.loaded!
|
80
|
+
elsif ar_parent.association_cached?(node.reflection.name)
|
81
|
+
model = ar_parent.association(node.reflection.name).target
|
82
|
+
construct(model, node, row, seen, model_cache, strict_loading_value)
|
83
|
+
next
|
84
|
+
end
|
85
|
+
|
86
|
+
key = aliases.column_alias(node, node.primary_key)
|
87
|
+
# CPK
|
88
|
+
if key.is_a?(Array)
|
89
|
+
id = Array(key).map do |column_alias|
|
90
|
+
row[column_alias]
|
91
|
+
end
|
92
|
+
# At least the first value in the key has to be set. Should we require all values to be set?
|
93
|
+
id = nil if id.first.nil?
|
94
|
+
else # original
|
95
|
+
id = row[key]
|
96
|
+
end
|
97
|
+
|
98
|
+
if id.nil?
|
99
|
+
nil_association = ar_parent.association(node.reflection.name)
|
100
|
+
nil_association.loaded!
|
101
|
+
next
|
102
|
+
end
|
103
|
+
|
104
|
+
model = seen[ar_parent][node][id]
|
105
|
+
|
106
|
+
if model
|
107
|
+
construct(model, node, row, seen, model_cache, strict_loading_value)
|
108
|
+
else
|
109
|
+
model = construct_model(ar_parent, node, row, model_cache, id, strict_loading_value)
|
110
|
+
|
111
|
+
seen[ar_parent][node][id] = model
|
112
|
+
construct(model, node, row, seen, model_cache, strict_loading_value)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -1,9 +1,21 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module AttributeMethods
|
3
|
-
def has_attribute?(attr_name)
|
4
|
-
# CPK
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeMethods
|
3
|
+
def has_attribute?(attr_name)
|
4
|
+
# CPK
|
5
|
+
# attr_name = attr_name.to_s
|
6
|
+
# attr_name = self.class.attribute_aliases[attr_name] || attr_name
|
7
|
+
# @attributes.key?(attr_name)
|
8
|
+
Array(attr_name).all? do |attr|
|
9
|
+
attr = attr.to_s
|
10
|
+
attr = self.class.attribute_aliases[attr] || attr
|
11
|
+
@attributes.key?(attr)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def _has_attribute?(attr_name)
|
16
|
+
# CPK
|
17
|
+
# @attributes.key?(attr_name)
|
18
|
+
Array(attr_name).all? { |attr| @attributes.key?(attr) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -16,7 +16,6 @@ module ActiveRecord
|
|
16
16
|
|
17
17
|
# Returns the primary key previous value.
|
18
18
|
def id_was
|
19
|
-
sync_with_transaction_state
|
20
19
|
# CPK
|
21
20
|
# attribute_was(self.class.primary_key)
|
22
21
|
if self.composite?
|
@@ -29,7 +28,6 @@ module ActiveRecord
|
|
29
28
|
end
|
30
29
|
|
31
30
|
def id_in_database
|
32
|
-
sync_with_transaction_state
|
33
31
|
# CPK
|
34
32
|
# attribute_in_database(self.class.primary_key)
|
35
33
|
if self.composite?
|
@@ -1,30 +1,30 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module AttributeMethods
|
3
|
-
module Read
|
4
|
-
def read_attribute(attr_name, &block)
|
5
|
-
# CPK
|
6
|
-
# name = attr_name.to_s
|
7
|
-
name = attr_name
|
8
|
-
if self.class.attribute_alias?(name)
|
9
|
-
name = self.class.attribute_alias(name)
|
10
|
-
end
|
11
|
-
|
12
|
-
primary_key = self.class.primary_key
|
13
|
-
# CPK
|
14
|
-
# name = primary_key if name == "id" && primary_key
|
15
|
-
name = primary_key if name == "id" && primary_key && !composite?
|
16
|
-
|
17
|
-
_read_attribute(name, &block)
|
18
|
-
end
|
19
|
-
|
20
|
-
def _read_attribute(attr_name, &block) # :nodoc
|
21
|
-
# CPK
|
22
|
-
if attr_name.kind_of?(Array)
|
23
|
-
attr_name.map {|name| @attributes.fetch_value(name.to_s, &block)}
|
24
|
-
else
|
25
|
-
@attributes.fetch_value(attr_name.to_s, &block)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeMethods
|
3
|
+
module Read
|
4
|
+
def read_attribute(attr_name, &block)
|
5
|
+
# CPK
|
6
|
+
# name = attr_name.to_s
|
7
|
+
name = attr_name
|
8
|
+
if self.class.attribute_alias?(name)
|
9
|
+
name = self.class.attribute_alias(name)
|
10
|
+
end
|
11
|
+
|
12
|
+
primary_key = self.class.primary_key
|
13
|
+
# CPK
|
14
|
+
# name = primary_key if name == "id" && primary_key
|
15
|
+
name = primary_key if name == "id" && primary_key && !composite?
|
16
|
+
|
17
|
+
_read_attribute(name, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def _read_attribute(attr_name, &block) # :nodoc
|
21
|
+
# CPK
|
22
|
+
if attr_name.kind_of?(Array)
|
23
|
+
attr_name.map {|name| @attributes.fetch_value(name.to_s, &block)}
|
24
|
+
else
|
25
|
+
@attributes.fetch_value(attr_name.to_s, &block)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|