composite_primary_keys 12.0.9 → 13.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/History.rdoc +883 -877
  3. data/README.rdoc +181 -180
  4. data/lib/composite_primary_keys.rb +119 -117
  5. data/lib/composite_primary_keys/active_model/attribute_assignment.rb +19 -19
  6. data/lib/composite_primary_keys/associations/association_scope.rb +66 -68
  7. data/lib/composite_primary_keys/associations/join_dependency.rb +118 -103
  8. data/lib/composite_primary_keys/attribute_methods.rb +21 -9
  9. data/lib/composite_primary_keys/attribute_methods/primary_key.rb +0 -2
  10. data/lib/composite_primary_keys/attribute_methods/read.rb +30 -30
  11. data/lib/composite_primary_keys/attribute_methods/write.rb +35 -35
  12. data/lib/composite_primary_keys/base.rb +141 -141
  13. data/lib/composite_primary_keys/connection_adapters/abstract/database_statements.rb +37 -37
  14. data/lib/composite_primary_keys/connection_adapters/sqlserver/database_statements.rb +44 -44
  15. data/lib/composite_primary_keys/core.rb +48 -48
  16. data/lib/composite_primary_keys/nested_attributes.rb +1 -1
  17. data/lib/composite_primary_keys/persistence.rb +82 -81
  18. data/lib/composite_primary_keys/reflection.rb +91 -29
  19. data/lib/composite_primary_keys/relation.rb +197 -193
  20. data/lib/composite_primary_keys/relation/batches.rb +15 -7
  21. data/lib/composite_primary_keys/relation/calculations.rb +104 -81
  22. data/lib/composite_primary_keys/relation/finder_methods.rb +235 -235
  23. data/lib/composite_primary_keys/relation/predicate_builder/association_query_value.rb +39 -20
  24. data/lib/composite_primary_keys/relation/query_methods.rb +42 -42
  25. data/lib/composite_primary_keys/relation/where_clause.rb +18 -23
  26. data/lib/composite_primary_keys/table_metadata.rb +11 -0
  27. data/lib/composite_primary_keys/version.rb +8 -8
  28. data/test/abstract_unit.rb +114 -114
  29. data/test/connections/databases.ci.yml +22 -22
  30. data/test/fixtures/db_definitions/db2-create-tables.sql +112 -112
  31. data/test/fixtures/db_definitions/db2-drop-tables.sql +16 -16
  32. data/test/fixtures/db_definitions/mysql.sql +180 -180
  33. data/test/fixtures/db_definitions/oracle.drop.sql +41 -41
  34. data/test/fixtures/db_definitions/oracle.sql +199 -199
  35. data/test/fixtures/db_definitions/postgresql.sql +182 -182
  36. data/test/fixtures/db_definitions/sqlite.sql +169 -169
  37. data/test/fixtures/db_definitions/sqlserver.sql +176 -176
  38. data/test/fixtures/department.rb +16 -16
  39. data/test/fixtures/departments.yml +19 -15
  40. data/test/fixtures/employees.yml +33 -28
  41. data/test/fixtures/restaurants_suburbs.yml +10 -10
  42. data/test/fixtures/streets.yml +16 -16
  43. data/test/fixtures/suburbs.yml +14 -14
  44. data/test/fixtures/user.rb +11 -11
  45. data/test/test_associations.rb +364 -358
  46. data/test/test_attributes.rb +75 -60
  47. data/test/test_calculations.rb +49 -42
  48. data/test/test_create.rb +218 -206
  49. data/test/test_delete.rb +182 -179
  50. data/test/test_exists.rb +39 -39
  51. data/test/test_find.rb +170 -164
  52. data/test/test_ids.rb +112 -112
  53. data/test/test_nested_attributes.rb +67 -67
  54. data/test/test_update.rb +96 -96
  55. metadata +12 -11
@@ -1,12 +1,16 @@
1
1
  module CompositePrimaryKeys
2
2
  module ActiveRecord
3
3
  module Batches
4
- def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil)
4
+ def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil, order: :asc)
5
5
  relation = self
6
6
  unless block_given?
7
7
  return ::ActiveRecord::Batches::BatchEnumerator.new(of: of, start: start, finish: finish, relation: self)
8
8
  end
9
9
 
10
+ unless [:asc, :desc].include?(order)
11
+ raise ArgumentError, ":order must be :asc or :desc, got #{order.inspect}"
12
+ end
13
+
10
14
  if arel.orders.present?
11
15
  act_on_ignored_order(error_on_ignore)
12
16
  end
@@ -17,8 +21,8 @@ module CompositePrimaryKeys
17
21
  batch_limit = remaining if remaining < batch_limit
18
22
  end
19
23
 
20
- relation = relation.reorder(batch_order).limit(batch_limit)
21
- relation = apply_limits(relation, start, finish)
24
+ relation = relation.reorder(batch_order(order)).limit(batch_limit)
25
+ relation = apply_limits(relation, start, finish, order)
22
26
  relation.skip_query_cache! # Retaining the results in the query cache would undermine the point of batching
23
27
  batch_relation = relation
24
28
 
@@ -61,7 +65,9 @@ module CompositePrimaryKeys
61
65
  end
62
66
 
63
67
  # CPK
64
- # batch_relation = relation.where(arel_attribute(primary_key).gt(primary_key_offset))
68
+ #batch_relation = relation.where(
69
+ # predicate_builder[primary_key, primary_key_offset, order == :desc ? :lt : :gt]
70
+ #)
65
71
  batch_relation = if composite?
66
72
  # CPK
67
73
  # Lexicographically select records
@@ -81,7 +87,9 @@ module CompositePrimaryKeys
81
87
  end.reduce(:or)
82
88
  relation.where(query)
83
89
  else
84
- relation.where(arel_attribute(primary_key).gt(primary_key_offset))
90
+ batch_relation = relation.where(
91
+ predicate_builder[primary_key, primary_key_offset, order == :desc ? :lt : :gt]
92
+ )
85
93
  end
86
94
  end
87
95
  end
@@ -95,9 +103,9 @@ module CompositePrimaryKeys
95
103
  ary.length.times.reduce([]) { |results, i| results << ary[0..i] }
96
104
  end
97
105
 
98
- def batch_order
106
+ def batch_order(order)
99
107
  self.primary_key.map do |key|
100
- arel_attribute(key).asc
108
+ table[key].public_send(order)
101
109
  end
102
110
  end
103
111
  end
@@ -1,81 +1,104 @@
1
- module CompositePrimaryKeys
2
- module ActiveRecord
3
- module Calculations
4
- def aggregate_column(column_name)
5
- # CPK
6
- if column_name.kind_of?(Array)
7
- column_name.map do |column|
8
- @klass.arel_attribute(column_name)
9
- end
10
- elsif @klass.has_attribute?(column_name) || @klass.attribute_alias?(column_name)
11
- @klass.arel_attribute(column_name)
12
- else
13
- Arel.sql(column_name == :all ? "*" : column_name.to_s)
14
- end
15
- end
16
-
17
- def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
18
- column_alias = column_name
19
-
20
- # CPK
21
- # if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
22
- # # Shortcut when limit is zero.
23
- # return 0 if limit_value == 0
24
- #
25
- # query_builder = build_count_subquery(spawn, column_name, distinct)
26
- if operation == "count"
27
- relation = unscope(:order)
28
- query_builder = build_count_subquery(spawn, column_name, distinct)
29
- else
30
- # PostgreSQL doesn't like ORDER BY when there are no GROUP BY
31
- relation = unscope(:order).distinct!(false)
32
-
33
- column = aggregate_column(column_name)
34
-
35
- select_value = operation_over_aggregate_column(column, operation, distinct)
36
- if operation == "sum" && distinct
37
- select_value.distinct = true
38
- end
39
-
40
- column_alias = select_value.alias
41
- column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
42
- relation.select_values = [select_value]
43
-
44
- query_builder = relation.arel
45
- end
46
-
47
- result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, nil) }
48
- row = result.first
49
- value = row && row.values.first
50
- type = result.column_types.fetch(column_alias) do
51
- type_for(column_name)
52
- end
53
-
54
- type_cast_calculated_value(value, type, operation)
55
- end
56
-
57
- def build_count_subquery(relation, column_name, distinct)
58
- if column_name == :all
59
- relation.select_values = [ Arel.sql(::ActiveRecord::FinderMethods::ONE_AS_ONE) ] unless distinct
60
- if relation.select_values.first.is_a?(Array)
61
- relation.select_values = relation.select_values.first.map do |column|
62
- Arel::Attribute.new(@klass.unscoped.table, column)
63
- end
64
- end
65
- elsif column_name.is_a?(Array)
66
- relation.select_values = column_name.map do |column|
67
- Arel::Attribute.new(@klass.unscoped.table, column)
68
- end
69
- else
70
- column_alias = Arel.sql("count_column")
71
- relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
72
- end
73
-
74
- subquery = relation.arel.as(Arel.sql("subquery_for_count"))
75
- select_value = operation_over_aggregate_column(column_alias || Arel.star, "count", false)
76
-
77
- Arel::SelectManager.new(subquery).project(select_value)
78
- end
79
- end
80
- end
81
- end
1
+ module CompositePrimaryKeys
2
+ module ActiveRecord
3
+ module Calculations
4
+ def aggregate_column(column_name)
5
+ # CPK
6
+ if column_name.kind_of?(Array)
7
+ # Note: Test don't seem to run this code?
8
+ column_name.map do |column|
9
+ @klass.arel_table[column]
10
+ end
11
+ elsif @klass.has_attribute?(column_name) || @klass.attribute_alias?(column_name)
12
+ @klass.arel_table[column_name]
13
+ else
14
+ Arel.sql(column_name == :all ? "*" : column_name.to_s)
15
+ end
16
+ end
17
+
18
+ def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
19
+ column_alias = column_name
20
+
21
+ # CPK
22
+ # if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
23
+ # # Shortcut when limit is zero.
24
+ # return 0 if limit_value == 0
25
+ #
26
+ # query_builder = build_count_subquery(spawn, column_name, distinct)
27
+ if operation == "count"
28
+ relation = unscope(:order)
29
+ query_builder = build_count_subquery(spawn, column_name, distinct)
30
+ else
31
+ # PostgreSQL doesn't like ORDER BY when there are no GROUP BY
32
+ relation = unscope(:order).distinct!(false)
33
+
34
+ column = aggregate_column(column_name)
35
+ select_value = operation_over_aggregate_column(column, operation, distinct)
36
+ select_value.distinct = true if operation == "sum" && distinct
37
+
38
+ relation.select_values = [select_value]
39
+
40
+ query_builder = relation.arel
41
+ end
42
+
43
+ result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder) }
44
+
45
+ type_cast_calculated_value(result.cast_values.first, operation) do |value|
46
+ type = column.try(:type_caster) ||
47
+ # CPK
48
+ # lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
49
+ lookup_cast_type_from_join_dependencies(column_name.to_s) || ::ActiveRecord::Type.default_value
50
+ type.deserialize(value)
51
+ end
52
+ end
53
+
54
+ def build_count_subquery(relation, column_name, distinct)
55
+ if column_name == :all
56
+ column_alias = Arel.star
57
+ # CPK
58
+ # relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
59
+ relation.select_values = [ Arel.sql(::ActiveRecord::FinderMethods::ONE_AS_ONE) ] unless distinct
60
+ elsif column_name.is_a?(Array)
61
+ column_alias = Arel.star
62
+ relation.select_values = column_name.map do |column|
63
+ Arel::Attribute.new(@klass.unscoped.table, column)
64
+ end
65
+ else
66
+ column_alias = Arel.sql("count_column")
67
+ relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
68
+ end
69
+
70
+ subquery_alias = Arel.sql("subquery_for_count")
71
+ select_value = operation_over_aggregate_column(column_alias, "count", false)
72
+
73
+ relation.build_subquery(subquery_alias, select_value)
74
+ end
75
+
76
+ def calculate(operation, column_name)
77
+ if has_include?(column_name)
78
+ relation = apply_join_dependency
79
+
80
+ if operation.to_s.downcase == "count"
81
+ unless distinct_value || distinct_select?(column_name || select_for_count)
82
+ relation.distinct!
83
+ # CPK
84
+ # relation.select_values = [ klass.primary_key || table[Arel.star] ]
85
+ if klass.primary_key.present? && klass.primary_key.is_a?(Array)
86
+ relation.select_values = klass.primary_key.map do |k|
87
+ "#{connection.quote_table_name(klass.table_name)}.#{connection.quote_column_name(k)}"
88
+ end
89
+ else
90
+ relation.select_values = [ klass.primary_key || table[Arel.star] ]
91
+ end
92
+ end
93
+ # PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
94
+ relation.order_values = [] if group_values.empty?
95
+ end
96
+
97
+ relation.calculate(operation, column_name)
98
+ else
99
+ perform_calculation(operation, column_name)
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -1,235 +1,235 @@
1
- module CompositePrimaryKeys
2
- module ActiveRecord
3
- module FinderMethods
4
- def apply_join_dependency(eager_loading: group_values.empty?)
5
- join_dependency = construct_join_dependency(eager_load_values + includes_values, Arel::Nodes::OuterJoin)
6
- relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
7
-
8
- if eager_loading && !using_limitable_reflections?(join_dependency.reflections)
9
- if has_limit_or_offset?
10
- limited_ids = limited_ids_for(relation)
11
-
12
- # CPK
13
- # limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
14
- limited_ids.empty? ? relation.none! : relation.where!(cpk_in_predicate(table, self.primary_keys, limited_ids))
15
- end
16
- relation.limit_value = relation.offset_value = nil
17
- end
18
-
19
- if block_given?
20
- yield relation, join_dependency
21
- else
22
- relation
23
- end
24
- end
25
-
26
- def limited_ids_for(relation)
27
- # CPK
28
- # values = @klass.connection.columns_for_distinct(
29
- # connection.column_name_from_arel_node(arel_attribute(primary_key)),
30
- # relation.order_values
31
- # )
32
-
33
- columns = @klass.primary_keys.map do |key|
34
- connection.visitor.compile(arel_attribute(key))
35
- end
36
- values = @klass.connection.columns_for_distinct(columns, relation.order_values)
37
-
38
- relation = relation.except(:select).select(values).distinct!
39
-
40
- id_rows = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "SQL") }
41
- # CPK
42
- #id_rows.map { |row| row[primary_key] }
43
- id_rows.map do |row|
44
- @klass.primary_keys.map do |key|
45
- row[key]
46
- end
47
- end
48
- end
49
-
50
- def construct_relation_for_exists(conditions)
51
- conditions = sanitize_forbidden_attributes(conditions)
52
-
53
- if distinct_value && offset_value
54
- relation = except(:order).limit!(1)
55
- else
56
- relation = except(:select, :distinct, :order)._select!(::ActiveRecord::FinderMethods::ONE_AS_ONE).limit!(1)
57
- end
58
-
59
- case conditions
60
- # CPK
61
- when CompositePrimaryKeys::CompositeKeys
62
- relation = relation.where(cpk_id_predicate(table, primary_key, conditions))
63
- # CPK
64
- when Array
65
- pk_length = @klass.primary_keys.length
66
-
67
- if conditions.length == pk_length # E.g. conditions = ['France', 'Paris']
68
- return self.construct_relation_for_exists(conditions.to_composite_keys)
69
- else # Assume that conditions contains where relation
70
- relation = relation.where(conditions)
71
- end
72
- when Array, Hash
73
- relation.where!(conditions) unless conditions.empty?
74
- else
75
- relation.where!(primary_key => conditions) unless conditions == :none
76
- end
77
-
78
- relation
79
- end
80
-
81
- def find_with_ids(*ids)
82
- raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
83
-
84
- # CPK
85
- # expects_array = ids.first.kind_of?(Array)
86
- ids = CompositePrimaryKeys.normalize(ids, @klass.primary_keys.length)
87
- expects_array = ids.flatten != ids.flatten(1)
88
- return ids.first if expects_array && ids.first.empty?
89
-
90
- # CPK
91
- # ids = ids.flatten.compact.uniq
92
- ids = expects_array ? ids.first : ids
93
-
94
- model_name = @klass.name
95
-
96
- case ids.size
97
- when 0
98
- error_message = "Couldn't find #{model_name} without an ID"
99
- raise ::ActiveRecord::RecordNotFound.new(error_message, model_name, primary_key)
100
- when 1
101
- result = find_one(ids.first)
102
- expects_array ? [ result ] : result
103
- else
104
- find_some(ids)
105
- end
106
- end
107
-
108
- # def last(limit = nil)
109
- # return find_last(limit) if loaded? || has_limit_or_offset?
110
- #
111
- # result = limit(limit || 1)
112
- # # CPK
113
- # # result.order!(arel_attribute(primary_key)) if order_values.empty? && primary_key
114
- # if order_values.empty? && primary_key
115
- # if composite?
116
- # result.order!(primary_keys.map { |pk| arel_attribute(pk).asc })
117
- # elsif
118
- # result.order!(arel_attribute(primary_key))
119
- # end
120
- # end
121
- #
122
- # result = result.reverse_order!
123
- #
124
- # limit ? result.reverse : result.first
125
- # rescue ::ActiveRecord::IrreversibleOrderError
126
- # ActiveSupport::Deprecation.warn(<<-WARNING.squish)
127
- # Finding a last element by loading the relation when SQL ORDER
128
- # can not be reversed is deprecated.
129
- # Rails 5.1 will raise ActiveRecord::IrreversibleOrderError in this case.
130
- # Please call `to_a.last` if you still want to load the relation.
131
- # WARNING
132
- # find_last(limit)
133
- # end
134
-
135
- def find_one(id)
136
- if ::ActiveRecord::Base === id
137
- raise ArgumentError, <<-MSG.squish
138
- You are passing an instance of ActiveRecord::Base to `find`.
139
- Please pass the id of the object by calling `.id`.
140
- MSG
141
- end
142
-
143
- # CPK
144
- #relation = where(primary_key => id)
145
- relation = where(cpk_id_predicate(table, primary_keys, id))
146
- record = relation.take
147
-
148
- raise_record_not_found_exception!(id, 0, 1) unless record
149
-
150
- record
151
- end
152
-
153
- def find_some(ids)
154
- # CPK
155
- if composite?
156
- ids = if ids.length == 1
157
- CompositePrimaryKeys::CompositeKeys.parse(ids.first)
158
- else
159
- ids.to_composite_keys
160
- end
161
- end
162
-
163
- return find_some_ordered(ids) unless order_values.present?
164
-
165
- # CPK
166
- # result = where(primary_key => ids).to_a
167
- result = if composite?
168
- result = where(cpk_in_predicate(table, primary_keys, ids)).to_a
169
- else
170
- result = where(primary_key => ids).to_a
171
- end
172
-
173
- expected_size =
174
- if limit_value && ids.size > limit_value
175
- limit_value
176
- else
177
- ids.size
178
- end
179
-
180
- # 11 ids with limit 3, offset 9 should give 2 results.
181
- if offset_value && (ids.size - offset_value < expected_size)
182
- expected_size = ids.size - offset_value
183
- end
184
-
185
- if result.size == expected_size
186
- result
187
- else
188
- raise_record_not_found_exception!(ids, result.size, expected_size)
189
- end
190
- end
191
-
192
- def find_some_ordered(ids)
193
- ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
194
-
195
- # CPK
196
- # result = except(:limit, :offset).where(primary_key => ids).records
197
- result = if composite?
198
- except(:limit, :offset).where(cpk_in_predicate(table, primary_keys, ids)).records
199
- else
200
- except(:limit, :offset).where(primary_key => ids).records
201
- end
202
-
203
- if result.size == ids.size
204
- pk_type = @klass.type_for_attribute(primary_key)
205
-
206
- records_by_id = result.index_by(&:id)
207
- # CPK
208
- # ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
209
- if composite?
210
- ids.map do |id|
211
- typecasted_id = primary_keys.zip(id).map do |col, val|
212
- @klass.type_for_attribute(col).cast(val)
213
- end
214
- records_by_id.fetch(typecasted_id)
215
- end
216
- else
217
- ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
218
- end
219
- else
220
- raise_record_not_found_exception!(ids, result.size, ids.size)
221
- end
222
- end
223
-
224
- def ordered_relation
225
- if order_values.empty? && (implicit_order_column || primary_key)
226
- # CPK
227
- # order(arel_attribute(implicit_order_column || primary_key).asc)
228
- order(Array(implicit_order_column || primary_key).map {|key| arel_attribute(key).asc})
229
- else
230
- self
231
- end
232
- end
233
- end
234
- end
235
- end
1
+ module CompositePrimaryKeys
2
+ module ActiveRecord
3
+ module FinderMethods
4
+ def apply_join_dependency(eager_loading: group_values.empty?)
5
+ join_dependency = construct_join_dependency(eager_load_values + includes_values, Arel::Nodes::OuterJoin)
6
+ relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
7
+
8
+ if eager_loading && !using_limitable_reflections?(join_dependency.reflections)
9
+ if has_limit_or_offset?
10
+ limited_ids = limited_ids_for(relation)
11
+
12
+ # CPK
13
+ # limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
14
+ limited_ids.empty? ? relation.none! : relation.where!(cpk_in_predicate(table, self.primary_keys, limited_ids))
15
+ end
16
+ relation.limit_value = relation.offset_value = nil
17
+ end
18
+
19
+ if block_given?
20
+ yield relation, join_dependency
21
+ else
22
+ relation
23
+ end
24
+ end
25
+
26
+ def limited_ids_for(relation)
27
+ # CPK
28
+ # values = @klass.connection.columns_for_distinct(
29
+ # connection.column_name_from_arel_node(table[primary_key]),
30
+ # relation.order_values
31
+ # )
32
+
33
+ columns = @klass.primary_keys.map do |key|
34
+ connection.visitor.compile(table[key])
35
+ end
36
+ values = @klass.connection.columns_for_distinct(columns, relation.order_values)
37
+
38
+ relation = relation.except(:select).select(values).distinct!
39
+
40
+ id_rows = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "SQL") }
41
+ # CPK
42
+ #id_rows.map { |row| row[primary_key] }
43
+ id_rows.map do |row|
44
+ @klass.primary_keys.map do |key|
45
+ row[key]
46
+ end
47
+ end
48
+ end
49
+
50
+ def construct_relation_for_exists(conditions)
51
+ conditions = sanitize_forbidden_attributes(conditions)
52
+
53
+ if distinct_value && offset_value
54
+ relation = except(:order).limit!(1)
55
+ else
56
+ relation = except(:select, :distinct, :order)._select!(::ActiveRecord::FinderMethods::ONE_AS_ONE).limit!(1)
57
+ end
58
+
59
+ case conditions
60
+ # CPK
61
+ when CompositePrimaryKeys::CompositeKeys
62
+ relation = relation.where(cpk_id_predicate(table, primary_key, conditions))
63
+ # CPK
64
+ when Array
65
+ pk_length = @klass.primary_keys.length
66
+
67
+ if conditions.length == pk_length # E.g. conditions = ['France', 'Paris']
68
+ return self.construct_relation_for_exists(conditions.to_composite_keys)
69
+ else # Assume that conditions contains where relation
70
+ relation = relation.where(conditions)
71
+ end
72
+ when Array, Hash
73
+ relation.where!(conditions) unless conditions.empty?
74
+ else
75
+ relation.where!(primary_key => conditions) unless conditions == :none
76
+ end
77
+
78
+ relation
79
+ end
80
+
81
+ def find_with_ids(*ids)
82
+ raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
83
+
84
+ # CPK
85
+ # expects_array = ids.first.kind_of?(Array)
86
+ ids = CompositePrimaryKeys.normalize(ids, @klass.primary_keys.length)
87
+ expects_array = ids.flatten != ids.flatten(1)
88
+ return ids.first if expects_array && ids.first.empty?
89
+
90
+ # CPK
91
+ # ids = ids.flatten.compact.uniq
92
+ ids = expects_array ? ids.first : ids
93
+
94
+ model_name = @klass.name
95
+
96
+ case ids.size
97
+ when 0
98
+ error_message = "Couldn't find #{model_name} without an ID"
99
+ raise ::ActiveRecord::RecordNotFound.new(error_message, model_name, primary_key)
100
+ when 1
101
+ result = find_one(ids.first)
102
+ expects_array ? [ result ] : result
103
+ else
104
+ find_some(ids)
105
+ end
106
+ end
107
+
108
+ # def last(limit = nil)
109
+ # return find_last(limit) if loaded? || has_limit_or_offset?
110
+ #
111
+ # result = limit(limit || 1)
112
+ # # CPK
113
+ # # result.order!(table[primary_key]) if order_values.empty? && primary_key
114
+ # if order_values.empty? && primary_key
115
+ # if composite?
116
+ # result.order!(primary_keys.map { |pk| table[pk].asc })
117
+ # elsif
118
+ # result.order!(table[primary_key])
119
+ # end
120
+ # end
121
+ #
122
+ # result = result.reverse_order!
123
+ #
124
+ # limit ? result.reverse : result.first
125
+ # rescue ::ActiveRecord::IrreversibleOrderError
126
+ # ActiveSupport::Deprecation.warn(<<-WARNING.squish)
127
+ # Finding a last element by loading the relation when SQL ORDER
128
+ # can not be reversed is deprecated.
129
+ # Rails 5.1 will raise ActiveRecord::IrreversibleOrderError in this case.
130
+ # Please call `to_a.last` if you still want to load the relation.
131
+ # WARNING
132
+ # find_last(limit)
133
+ # end
134
+
135
+ def find_one(id)
136
+ if ::ActiveRecord::Base === id
137
+ raise ArgumentError, <<-MSG.squish
138
+ You are passing an instance of ActiveRecord::Base to `find`.
139
+ Please pass the id of the object by calling `.id`.
140
+ MSG
141
+ end
142
+
143
+ # CPK
144
+ #relation = where(primary_key => id)
145
+ relation = where(cpk_id_predicate(table, primary_keys, id))
146
+ record = relation.take
147
+
148
+ raise_record_not_found_exception!(id, 0, 1) unless record
149
+
150
+ record
151
+ end
152
+
153
+ def find_some(ids)
154
+ # CPK
155
+ if composite?
156
+ ids = if ids.length == 1
157
+ CompositePrimaryKeys::CompositeKeys.parse(ids.first)
158
+ else
159
+ ids.to_composite_keys
160
+ end
161
+ end
162
+
163
+ return find_some_ordered(ids) unless order_values.present?
164
+
165
+ # CPK
166
+ # result = where(primary_key => ids).to_a
167
+ result = if composite?
168
+ result = where(cpk_in_predicate(table, primary_keys, ids)).to_a
169
+ else
170
+ result = where(primary_key => ids).to_a
171
+ end
172
+
173
+ expected_size =
174
+ if limit_value && ids.size > limit_value
175
+ limit_value
176
+ else
177
+ ids.size
178
+ end
179
+
180
+ # 11 ids with limit 3, offset 9 should give 2 results.
181
+ if offset_value && (ids.size - offset_value < expected_size)
182
+ expected_size = ids.size - offset_value
183
+ end
184
+
185
+ if result.size == expected_size
186
+ result
187
+ else
188
+ raise_record_not_found_exception!(ids, result.size, expected_size)
189
+ end
190
+ end
191
+
192
+ def find_some_ordered(ids)
193
+ ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
194
+
195
+ # CPK
196
+ # result = except(:limit, :offset).where(primary_key => ids).records
197
+ result = if composite?
198
+ except(:limit, :offset).where(cpk_in_predicate(table, primary_keys, ids)).records
199
+ else
200
+ except(:limit, :offset).where(primary_key => ids).records
201
+ end
202
+
203
+ if result.size == ids.size
204
+ pk_type = @klass.type_for_attribute(primary_key)
205
+
206
+ records_by_id = result.index_by(&:id)
207
+ # CPK
208
+ # ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
209
+ if composite?
210
+ ids.map do |id|
211
+ typecasted_id = primary_keys.zip(id).map do |col, val|
212
+ @klass.type_for_attribute(col).cast(val)
213
+ end
214
+ records_by_id.fetch(typecasted_id)
215
+ end
216
+ else
217
+ ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
218
+ end
219
+ else
220
+ raise_record_not_found_exception!(ids, result.size, ids.size)
221
+ end
222
+ end
223
+
224
+ def ordered_relation
225
+ if order_values.empty? && (implicit_order_column || primary_key)
226
+ # CPK
227
+ # order(table[implicit_order_column || primary_key].asc)
228
+ order(Array(implicit_order_column || primary_key).map {|key| table[key].asc})
229
+ else
230
+ self
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end