diff --git a/CHANGELOG.md b/CHANGELOG.md index 89a0608..1d96bcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +v1.3.4, 20265-01-20 + * [BUGFIX] Validate limit for days and weekdays v1.3.3, 2025-08-12 ------------------- diff --git a/VERSION b/VERSION index 31e5c84..d0149fe 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.3.3 +1.3.4 diff --git a/lib/sparkql/function_resolver.rb b/lib/sparkql/function_resolver.rb index 3857c63..c66d476 100644 --- a/lib/sparkql/function_resolver.rb +++ b/lib/sparkql/function_resolver.rb @@ -21,6 +21,7 @@ class FunctionResolver VALID_REGEX_FLAGS = ['', 'i'].freeze MIN_DATE_TIME = Time.new(1970, 1, 1, 0, 0, 0, '+00:00').iso8601 MAX_DATE_TIME = Time.new(9999, 12, 31, 23, 59, 59, '+00:00').iso8601 + MAX_DAYS = (365 * 1000).freeze # 1000 years ought to cover most cases VALID_CAST_TYPES = %i[field character decimal integer].freeze SUPPORTED_FUNCTIONS = { @@ -43,16 +44,16 @@ class FunctionResolver regex: { args: [:character], opt_args: [{ - type: :character, - default: '' - }], + type: :character, + default: '' + }], return_type: :character }, substring: { args: [%i[field character], :integer], opt_args: [{ - type: :integer - }], + type: :integer + }], resolve_for_type: true, return_type: :character }, @@ -526,6 +527,15 @@ def hours(num) # Offset the current timestamp by a number of days def days(number_of_days) + if number_of_days.abs > MAX_DAYS + @errors << Sparkql::ParserError.new(token: number_of_days, + message: "Function call 'days' max offset #{MAX_DAYS} days", + status: :fatal, + syntax: false, + constraint: true) + return + end + # date calculated as the offset from midnight tommorrow. Zero will provide values for all times # today. d = current_date + number_of_days @@ -536,6 +546,15 @@ def days(number_of_days) end def weekdays(number_of_days) + if number_of_days.abs > MAX_DAYS + @errors << Sparkql::ParserError.new(token: number_of_days, + message: "Function call 'weekdays' max offset #{MAX_DAYS} days", + status: :fatal, + syntax: false, + constraint: true) + return + end + today = current_date weekend_start = today.saturday? || today.sunday? direction = number_of_days.positive? ? 1 : -1 diff --git a/test/unit/function_resolver_test.rb b/test/unit/function_resolver_test.rb index c471b5f..ee92b28 100644 --- a/test/unit/function_resolver_test.rb +++ b/test/unit/function_resolver_test.rb @@ -445,6 +445,24 @@ def assert_times(call_value, expected_call_type = :datetime, overrides = {}) end end + test 'days - exceed max' do + f = FunctionResolver.new('days', + [{ type: :integer, value: 365_001 }], + current_timestamp: EXAMPLE_DATE) + f.validate + assert !f.errors? + assert_nil f.call + assert f.errors?, "function 'days' limit 365000" + + f = FunctionResolver.new('days', + [{ type: :integer, value: -365_001 }], + current_timestamp: EXAMPLE_DATE) + f.validate + assert !f.errors? + assert_nil f.call + assert f.errors?, "function 'days' limit 365000" + end + test 'weekdays()' do friday = Date.new(2012, 10, 19) saturday = Date.new(2012, 10, 20) @@ -514,6 +532,24 @@ def assert_times(call_value, expected_call_type = :datetime, overrides = {}) end end + test 'weekdays - exceed max' do + f = FunctionResolver.new('weekdays', + [{ type: :integer, value: 365_001 }], + current_timestamp: EXAMPLE_DATE) + f.validate + assert !f.errors? + assert_nil f.call + assert f.errors?, "function 'weekdays' limit 365000" + + f = FunctionResolver.new('weekdays', + [{ type: :integer, value: -365_001 }], + current_timestamp: EXAMPLE_DATE) + f.validate + assert !f.errors? + assert_nil f.call + assert f.errors?, "function 'weekdays' limit 365000" + end + test 'months()' do f = FunctionResolver.new('months', [{ type: :integer, value: 3 }],