From 78b9f61dcab9bd571952b8c9e8052b643588c52b Mon Sep 17 00:00:00 2001 From: Tom Clegg Date: Mon, 8 Feb 2016 14:28:02 -0500 Subject: [PATCH] 8289: Strip redundant orders, even when provided explicitly by client. --- services/api/lib/load_param.rb | 27 ++++++++++-- .../test/functional/arvados/v1/query_test.rb | 42 +++++++++++++++++++ 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/services/api/lib/load_param.rb b/services/api/lib/load_param.rb index c6c8ddfac4..d7b9bb7513 100644 --- a/services/api/lib/load_param.rb +++ b/services/api/lib/load_param.rb @@ -111,11 +111,30 @@ module LoadParam # (e.g., [] or ['owner_uuid desc']), fall back on the default # orders to ensure repeating the same request (possibly with # different limit/offset) will return records in the same order. - unless @orders.any? do |order| - otable, ocol = order.split(' ')[0].split('.') - otable == table_name and model_class.unique_columns.include?(ocol) + # + # Clean up the resulting list of orders such that no column + # uselessly appears twice (Postgres might not optimize this out + # for us) and no columns uselessly appear after a unique column + # (Postgres does not optimize this out for us; as of 9.2, "order + # by id, modified_at desc, uuid" is slow but "order by id" is + # fast). + orders_given_and_default = @orders + model_class.default_orders + order_cols_used = {} + @orders = [] + orders_given_and_default.each do |order| + otablecol = order.split(' ')[0] + + next if order_cols_used[otablecol] + order_cols_used[otablecol] = true + + @orders << order + + otable, ocol = otablecol.split('.') + if otable == table_name and model_class.unique_columns.include?(ocol) + # we already have a full ordering; subsequent entries would be + # superfluous + break end - @orders += model_class.default_orders end case params[:select] diff --git a/services/api/test/functional/arvados/v1/query_test.rb b/services/api/test/functional/arvados/v1/query_test.rb index 56db54c3a4..91fe077503 100644 --- a/services/api/test/functional/arvados/v1/query_test.rb +++ b/services/api/test/functional/arvados/v1/query_test.rb @@ -23,4 +23,46 @@ class Arvados::V1::QueryTest < ActionController::TestCase assert_equal('logs.event_type asc, logs.modified_at desc, logs.uuid', assigns(:objects).order_values.join(', ')) end + + test 'skip fallback orders already given by client' do + @controller = Arvados::V1::LogsController.new + authorize_with :active + get :index, { + order: ['modified_at asc'], + controller: 'logs', + } + assert_response :success + assert_equal('logs.modified_at asc, logs.uuid', + assigns(:objects).order_values.join(', ')) + end + + test 'eliminate superfluous orders' do + @controller = Arvados::V1::LogsController.new + authorize_with :active + get :index, { + order: ['logs.modified_at asc', + 'modified_at desc', + 'event_type desc', + 'logs.event_type asc'], + controller: 'logs', + } + assert_response :success + assert_equal('logs.modified_at asc, logs.event_type desc, logs.uuid', + assigns(:objects).order_values.join(', ')) + end + + test 'eliminate orders after the first unique column' do + @controller = Arvados::V1::LogsController.new + authorize_with :active + get :index, { + order: ['event_type asc', + 'id asc', + 'uuid asc', + 'modified_at desc'], + controller: 'logs', + } + assert_response :success + assert_equal('logs.event_type asc, logs.id asc', + assigns(:objects).order_values.join(', ')) + end end -- 2.30.2