diff --git a/lib/sequel/extensions/default_order_by_id.rb b/lib/sequel/extensions/default_order_by_id.rb index 5ec36b3fd86..70b0f7bbaeb 100644 --- a/lib/sequel/extensions/default_order_by_id.rb +++ b/lib/sequel/extensions/default_order_by_id.rb @@ -2,7 +2,8 @@ # Sequel extension that adds default ORDER BY id to model queries. # -# Hooks into fetch methods (all, each, first) to add ORDER BY id just before +# Hooks into fetch methods (all, each, first) and placeholder_literalizer_loader +# (used for optimized association loading) to add ORDER BY id just before # execution. This ensures ordering is only added to the final query, not to # subqueries or compound query parts. # @@ -47,6 +48,13 @@ def first(*, &) ds.first(*, &) end + def placeholder_literalizer_loader(&block) + super do |pl, ds| + result_ds = block.call(pl, ds) + result_ds.send(:default_order_by_id) || result_ds + end + end + private def default_order_by_id diff --git a/spec/unit/lib/sequel/extensions/default_order_by_id_spec.rb b/spec/unit/lib/sequel/extensions/default_order_by_id_spec.rb index efcb6db28b0..dc52952e209 100644 --- a/spec/unit/lib/sequel/extensions/default_order_by_id_spec.rb +++ b/spec/unit/lib/sequel/extensions/default_order_by_id_spec.rb @@ -153,4 +153,32 @@ def capture_sql(&) end end end + + describe 'association loading (placeholder_literalizer_loader)' do + let(:child_class) { Class.new(Sequel::Model(db[:test_default_order_join])) } + + it 'adds ORDER BY id to association queries' do + child = child_class + parent_class = Class.new(Sequel::Model(db[:test_default_order_main])) do + def self.name; 'TestParent'; end # vcap_relations plugin derives reciprocal from self.name + one_to_many :children, class: child, key: :main_id + end + parent = parent_class.new + parent.values[:id] = 1 + sql = capture_sql { parent.children } + expect(sql).to match(/ORDER BY .id./) + end + + it 'does not override explicit association order' do + child = child_class + parent_class = Class.new(Sequel::Model(db[:test_default_order_main])) do + def self.name; 'TestParent'; end + one_to_many :children, class: child, key: :main_id, order: Sequel.desc(:id) + end + parent = parent_class.new + parent.values[:id] = 1 + sql = capture_sql { parent.children } + expect(sql).to match(/ORDER BY .id. DESC/) + end + end end