lib/sequel/extensions/date_arithmetic.rb in sequel-5.43.0 vs lib/sequel/extensions/date_arithmetic.rb in sequel-5.44.0

- old
+ new

@@ -6,13 +6,14 @@ # First, you need to load the extension into the database: # # DB.extension :date_arithmetic # # Then you can use the Sequel.date_add and Sequel.date_sub methods -# to return Sequel expressions: +# to return Sequel expressions (this example shows the only supported +# keys for the second argument): # -# add = Sequel.date_add(:date_column, years: 1, months: 2, days: 3) +# add = Sequel.date_add(:date_column, years: 1, months: 2, weeks: 2, days: 1) # sub = Sequel.date_sub(:date_column, hours: 1, minutes: 2, seconds: 3) # # In addition to specifying the interval as a hash, there is also # support for specifying the interval as an ActiveSupport::Duration # object: @@ -182,25 +183,38 @@ # Supports two types of intervals: # Hash :: Used directly, but values cannot be plain strings. # ActiveSupport::Duration :: Converted to a hash using the interval's parts. def initialize(expr, interval, opts=OPTS) @expr = expr - @interval = if interval.is_a?(Hash) - interval.each_value do |v| - # Attempt to prevent SQL injection by users who pass untrusted strings - # as interval values. - if v.is_a?(String) && !v.is_a?(LiteralString) - raise Sequel::InvalidValue, "cannot provide String value as interval part: #{v.inspect}" - end + + h = Hash.new(0) + interval = interval.parts unless interval.is_a?(Hash) + interval.each do |unit, value| + # skip nil values + next unless value + + # Convert weeks to days, as ActiveSupport::Duration can use weeks, + # but the database-specific literalizers only support days. + if unit == :weeks + unit = :days + value *= 7 end - Hash[interval] - else - h = Hash.new(0) - interval.parts.each{|unit, value| h[unit] += value} - Hash[h] + + unless DatasetMethods::DURATION_UNITS.include?(unit) + raise Sequel::Error, "Invalid key used in DateAdd interval hash: #{unit.inspect}" + end + + # Attempt to prevent SQL injection by users who pass untrusted strings + # as interval values. It doesn't make sense to support literal strings, + # due to the numeric adding below. + if value.is_a?(String) + raise Sequel::InvalidValue, "cannot provide String value as interval part: #{value.inspect}" + end + + h[unit] += value end - @interval.freeze + @interval = Hash[h].freeze @cast_type = opts[:cast] if opts[:cast] freeze end to_s_method :date_add_sql