# @where, @filters, @limit, @offset, @orders
module LoadParam
- # Default limit on number of rows to return in a single query.
+ # Default number of rows to return in a single query.
DEFAULT_LIMIT = 100
# Load params[:where] into @where
end
end
- def default_orders
- ["#{table_name}.modified_at desc"]
- end
-
# Load params[:limit], params[:offset] and params[:order]
# into @limit, @offset, @orders
def load_limit_offset_order_params
unless params[:limit].to_s.match(/^\d+$/)
raise ArgumentError.new("Invalid value for limit parameter")
end
- @limit = params[:limit].to_i
+ @limit = [params[:limit].to_i,
+ Rails.configuration.max_items_per_response].min
else
@limit = DEFAULT_LIMIT
end
end
end
- if @orders.empty?
- @orders = default_orders
+ # If the client-specified orders don't amount to a full ordering
+ # (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.
+ #
+ # 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
end
case params[:select]