activerecord_where_assoc 1.2.0 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ef06d95cd1fa0b3de7d06e745fc1a527bdc86415adb2e6b36b86b1f1477e804
4
- data.tar.gz: 36a723c284fbb1e0b44ea9af5368995d504a53026817f545a14235212d4ae7bf
3
+ metadata.gz: ad309caebf6c729645f97a3c76959f0e61557b956fe86fe13dac641af06f87d4
4
+ data.tar.gz: 7f4967c6493d13550f31036753a50606f7e82f9fed1d5277b06db899517e89ec
5
5
  SHA512:
6
- metadata.gz: 8a26660a18b817b6a5cd106e71413e064f899e6fb9c73e9f502a14fc5aeb2896412368bc932028300611c94adac234ebbff85d0e2fd3ed8b900c01fa6c5e2670
7
- data.tar.gz: febe936611a53bc9f2f75aeb639fe8d8b905304b9b910e71f5cc9020ab1c0fae6803cd303f0cd7b830e29aa092778df21ce0303150f876930a02be2ed831821e
6
+ metadata.gz: 0efde09f38bd4c4913ac3b4cb36d114cb654c0c35bd05c07eb31559801d9febb87b479d9ae41b6bc07d9d77ddef775fd3b1e307ea20a3a10ccf2b829f03f27f1
7
+ data.tar.gz: ef3d2dbfef7f58f0221cdf5c32448410a1886fd1b8532bbe2d4a8643d5bde2dad5c2029eeddf5cb1b6620ab199b6ec2e3bd313ddd505d648a90d2d2f619b2747
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Unreleased
2
2
 
3
+ # 1.2.1 - 2024-12-05
4
+
5
+ * Optimize `has_one` handling for `#where_assoc_exists` with a `has_one` as last association + without any conditions or offset.
6
+ * Optimize `has_one` handling when the foreign_key has a unique index and there is no offset
7
+
3
8
  # 1.2.0 - 2024-08-31
4
9
 
5
10
  * Add support for composite primary keys in Rails 7.2
@@ -101,5 +101,28 @@ module ActiveRecordWhereAssoc
101
101
  reflection.is_a?(ActiveRecord::NullRelation)
102
102
  end
103
103
  end
104
+
105
+ if ActiveRecord.gem_version >= Gem::Version.new("6.0")
106
+ def self.indexes(model)
107
+ model.connection.schema_cache.indexes(model.table_name)
108
+ end
109
+ else
110
+ def self.indexes(model)
111
+ model.connection.indexes(model.table_name)
112
+ end
113
+ end
114
+
115
+ @unique_indexes_cache = {}
116
+ def self.has_unique_index?(model, column_names)
117
+ column_names = Array(column_names).map(&:to_s)
118
+ @unique_indexes_cache.fetch([model, column_names]) do |k|
119
+ unique_indexes = indexes(model).select(&:unique)
120
+ columns_names_set = Set.new(column_names)
121
+
122
+ # We check for an index whose columns are a subset of the columns we specify
123
+ # This way, a composite column_names will find uniqueness if just a single of the column is unique
124
+ @unique_indexes_cache[k] = unique_indexes.any? { |ui| Set.new(ui.columns) <= columns_names_set }
125
+ end
126
+ end
104
127
  end
105
128
  end
@@ -155,9 +155,17 @@ module ActiveRecordWhereAssoc
155
155
 
156
156
  init_scopes = initial_scopes_from_reflection(record_class, reflection_chain[i..-1], constraints_chain[i], options)
157
157
  current_scopes = init_scopes.map do |alias_scope, current_scope, klass_scope|
158
- current_scope = process_association_step_limits(current_scope, reflection, record_class, options)
159
-
160
158
  if i.zero?
159
+ if given_conditions || klass_scope || last_assoc_block || current_scope.offset_value || nest_assocs_block == NestWithSumBlock
160
+ # In the deepest layer, the limit & offset complexities only matter when:
161
+ # * There is a condition to apply
162
+ # * There is an offset (which is a form of filtering)
163
+ # * We are counting the total matches
164
+ # Since last_assoc_block is always set except for the deepest association, and is only unset for the deepest layer if
165
+ # there is no condition given, using it as part of the condition does a lot of work here.
166
+ current_scope = process_association_step_limits(current_scope, reflection, record_class, options)
167
+ end
168
+
161
169
  current_scope = current_scope.where(given_conditions) if given_conditions
162
170
  if klass_scope
163
171
  if klass_scope.respond_to?(:call)
@@ -167,6 +175,8 @@ module ActiveRecordWhereAssoc
167
175
  end
168
176
  end
169
177
  current_scope = apply_proc_scope(current_scope, last_assoc_block) if last_assoc_block
178
+ else
179
+ current_scope = process_association_step_limits(current_scope, reflection, record_class, options)
170
180
  end
171
181
 
172
182
  # Those make no sense since at this point, we are only limiting the value that would match using conditions
@@ -338,10 +348,18 @@ module ActiveRecordWhereAssoc
338
348
 
339
349
  current_scope = current_scope.limit(1) if reflection.macro == :has_one
340
350
 
341
- # Order is useless without either limit or offset
342
- current_scope = current_scope.unscope(:order) if !current_scope.limit_value && !current_scope.offset_value
351
+ if !current_scope.offset_value
352
+ if current_scope.limit_value
353
+ join_keys = ActiveRecordCompat.join_keys(reflection, nil)
354
+ # #join_keys is inverted... the foreign key is on the "source" table, and the key is on the "target" table...
355
+ # Everything is so complicated in ActiveRecord.
356
+ current_scope = current_scope.unscope(:limit) if ActiveRecordCompat.has_unique_index?(current_scope.model, join_keys.key)
357
+ end
358
+
359
+ # Order is useless without either limit or offset
360
+ return current_scope.unscope(:order) if !current_scope.limit_value
361
+ end
343
362
 
344
- return current_scope unless current_scope.limit_value || current_scope.offset_value
345
363
  if %w(mysql mysql2).include?(relation_klass.connection.adapter_name.downcase)
346
364
  msg = String.new
347
365
  msg << "Associations and default_scopes with a limit or offset are not supported for MySQL (this includes has_many). "
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecordWhereAssoc
4
- VERSION = "1.2.0".freeze
4
+ VERSION = "1.2.1".freeze
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord_where_assoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maxime Handfield Lapointe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-31 00:00:00.000000000 Z
11
+ date: 2024-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord