groupdate 5.2.4 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b6c83f192d439e35025586a076762413d80daab15b85e81561404bdf078acad
4
- data.tar.gz: 382dd612bbd1db8e131345981576b7a6c61c525d8a991fb8a3fc60678ce8d6ef
3
+ metadata.gz: f0169fac01ba6b7b7a051ae37d2cc3d5b89b137b7bb3d609df7a633b534ac89b
4
+ data.tar.gz: ccdf270ed3726d0c0a52e9fafe0fb3b2281f6b31ac0a9a731eaedfbda8f42067
5
5
  SHA512:
6
- metadata.gz: 2d33022921907669f28eed522daec36e84552aabd41a411ff556923c0029cc72a003d30cd9cbeecba27e3d795fc279f71ca05a01cbc8c5cf6a0bd7256a478f72
7
- data.tar.gz: 87c778a1972590cc6609a3c5a096fef6dde88c70ab663b5ebac820de89ec356359f802ea1d8ba38280378e877caabb1974e1d6a81c689eef92e6d7accf2f356a
6
+ metadata.gz: aa3150d5c5edd803e225045d745faf191fef78f483fec13aac05f4fb7dc0ee04a1b7a9340f1f8cf35a8d1c79ed09a6e6e6c4432e094d54faf5c7af48385c5e68
7
+ data.tar.gz: 97137d089ec745cdbcf08d1c7999f3efefab9c8cb3dd39e4e033f28184b8e4d4955cb4f7777cd7e8f8d28a3dda8b3dce2826242322de1949c2f72ad11f24a32d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 6.0.0 (2022-01-15)
2
+
3
+ - Changed SQL to return dates instead of times for day, week, month, quarter, and year
4
+ - Added `n` option for Redshift
5
+ - Removed `dates` option
6
+ - Raise `ActiveRecord::UnknownAttributeReference` for non-attribute arguments
7
+ - Raise `ArgumentError` for ranges with string bounds
8
+ - Dropped support for Ruby < 2.6 and Rails < 5.2
9
+
1
10
  ## 5.2.4 (2021-12-15)
2
11
 
3
12
  - Simplified queries for Active Record 7 and MySQL
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013-2021 Andrew Kane
1
+ Copyright (c) 2013-2022 Andrew Kane
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -22,7 +22,7 @@ Supports PostgreSQL, MySQL, and Redshift, plus arrays and hashes (and limited su
22
22
  Add this line to your application’s Gemfile:
23
23
 
24
24
  ```ruby
25
- gem 'groupdate'
25
+ gem "groupdate"
26
26
  ```
27
27
 
28
28
  For MySQL and SQLite, also follow [these instructions](#additional-instructions).
@@ -210,19 +210,6 @@ If grouping on date columns which don’t need time zone conversion, use:
210
210
  User.group_by_week(:created_on, time_zone: false).count
211
211
  ```
212
212
 
213
- ### User Input
214
-
215
- If passing user input as the column, be sure to sanitize it first [like you must](https://rails-sqli.org/) with `group`.
216
-
217
- ```ruby
218
- column = params[:column]
219
-
220
- # check against permitted columns
221
- raise "Unpermitted column" unless ["column_a", "column_b"].include?(column)
222
-
223
- User.group_by_day(column).count
224
- ```
225
-
226
213
  ### Default Scopes
227
214
 
228
215
  If you use Postgres and have a default scope that uses `order`, you may get a `column must appear in the GROUP BY clause` error (just like with Active Record’s `group` method). Remove the `order` scope with:
@@ -252,7 +239,7 @@ users.group_by_day(series: true) { |u| u.created_at }
252
239
  Count
253
240
 
254
241
  ```ruby
255
- users.group_by_day { |u| u.created_at }.map { |k, v| [k, v.count] }.to_h
242
+ users.group_by_day { |u| u.created_at }.to_h { |k, v| [k, v.count] }
256
243
  ```
257
244
 
258
245
  ## Additional Instructions
@@ -289,13 +276,15 @@ Groupdate.time_zone = false
289
276
 
290
277
  ## Upgrading
291
278
 
292
- ### 5.0
279
+ ### 6.0
280
+
281
+ Groupdate 6.0 protects against unsafe input by default. For non-attribute arguments, use:
293
282
 
294
- Groupdate 5.0 brings a number of improvements. Here are a few to be aware of:
283
+ ```ruby
284
+ User.group_by_day(Arel.sql(known_safe_value)).count
285
+ ```
295
286
 
296
- - The `week_start` option is now supported for SQLite
297
- - The `day_start` option is now consistent between Active Record and enumerable
298
- - Deprecated positional arguments for time zone and range have been removed
287
+ Also, the `dates` option has been removed.
299
288
 
300
289
  ## History
301
290
 
@@ -4,11 +4,8 @@ module Groupdate
4
4
  attr_reader :period, :column, :day_start, :week_start, :n_seconds
5
5
 
6
6
  def initialize(relation, column:, period:, time_zone:, time_range:, week_start:, day_start:, n_seconds:)
7
- # very important
8
- column = validate_column(column)
9
-
10
7
  @relation = relation
11
- @column = resolve_column(relation, column)
8
+ @column = column
12
9
  @period = period
13
10
  @time_zone = time_zone
14
11
  @time_range = time_range
@@ -49,28 +46,6 @@ module Groupdate
49
46
  ["#{column} IS NOT NULL"]
50
47
  end
51
48
  end
52
-
53
- # basic version of Active Record disallow_raw_sql!
54
- # symbol = column (safe), Arel node = SQL (safe), other = untrusted
55
- # matches table.column and column
56
- def validate_column(column)
57
- unless column.is_a?(Symbol) || column.is_a?(Arel::Nodes::SqlLiteral)
58
- column = column.to_s
59
- unless /\A\w+(\.\w+)?\z/i.match(column)
60
- warn "[groupdate] Non-attribute argument: #{column}. Use Arel.sql() for known-safe values. This will raise an error in Groupdate 6"
61
- end
62
- end
63
- column
64
- end
65
-
66
- # resolves eagerly
67
- # need to convert both where_clause (easy)
68
- # and group_clause (not easy) if want to avoid this
69
- def resolve_column(relation, column)
70
- node = relation.send(:relation).send(:arel_columns, [column]).first
71
- node = Arel::Nodes::SqlLiteral.new(node) if node.is_a?(String)
72
- relation.connection.visitor.accept(node, Arel::Collectors::SQLString.new).value
73
- end
74
49
  end
75
50
  end
76
51
  end
@@ -20,9 +20,21 @@ module Groupdate
20
20
  when :month_of_year
21
21
  ["MONTH(#{day_start_column})", time_zone, day_start]
22
22
  when :week
23
- ["CONVERT_TZ(DATE_FORMAT(#{day_start_column} - INTERVAL ((? + DAYOFWEEK(#{day_start_column})) % 7) DAY, '%Y-%m-%d 00:00:00') + INTERVAL ? second, ?, '+00:00')", time_zone, day_start, 12 - week_start, time_zone, day_start, day_start, time_zone]
23
+ ["CAST(DATE_FORMAT(#{day_start_column} - INTERVAL ((? + DAYOFWEEK(#{day_start_column})) % 7) DAY, '%Y-%m-%d') AS DATE)", time_zone, day_start, 12 - week_start, time_zone, day_start]
24
24
  when :quarter
25
- ["CONVERT_TZ(DATE_FORMAT(DATE(CONCAT(YEAR(#{day_start_column}), '-', LPAD(1 + 3 * (QUARTER(#{day_start_column}) - 1), 2, '00'), '-01')), '%Y-%m-%d %H:%i:%S') + INTERVAL ? second, ?, '+00:00')", time_zone, day_start, time_zone, day_start, day_start, time_zone]
25
+ ["CAST(CONCAT(YEAR(#{day_start_column}), '-', LPAD(1 + 3 * (QUARTER(#{day_start_column}) - 1), 2, '00'), '-01') AS DATE)", time_zone, day_start, time_zone, day_start]
26
+ when :day, :month, :year
27
+ format =
28
+ case period
29
+ when :day
30
+ "%Y-%m-%d"
31
+ when :month
32
+ "%Y-%m-01"
33
+ else # year
34
+ "%Y-01-01"
35
+ end
36
+
37
+ ["CAST(DATE_FORMAT(#{day_start_column}, ?) AS DATE)", time_zone, day_start, format]
26
38
  when :custom
27
39
  ["FROM_UNIXTIME((UNIX_TIMESTAMP(#{column}) DIV ?) * ?)", n_seconds, n_seconds]
28
40
  else
@@ -32,14 +44,8 @@ module Groupdate
32
44
  "%Y-%m-%d %H:%i:%S"
33
45
  when :minute
34
46
  "%Y-%m-%d %H:%i:00"
35
- when :hour
47
+ else # hour
36
48
  "%Y-%m-%d %H:00:00"
37
- when :day
38
- "%Y-%m-%d 00:00:00"
39
- when :month
40
- "%Y-%m-01 00:00:00"
41
- else # year
42
- "%Y-01-01 00:00:00"
43
49
  end
44
50
 
45
51
  ["CONVERT_TZ(DATE_FORMAT(#{day_start_column}, ?) + INTERVAL ? second, ?, '+00:00')", time_zone, day_start, format, day_start, time_zone]
@@ -21,16 +21,18 @@ module Groupdate
21
21
  when :month_of_year
22
22
  ["EXTRACT(MONTH FROM #{day_start_column})::integer", time_zone, day_start_interval]
23
23
  when :week
24
- ["(DATE_TRUNC('day', #{day_start_column} - INTERVAL '1 day' * ((? + EXTRACT(DOW FROM #{day_start_column})::integer) % 7)) + INTERVAL ?) AT TIME ZONE ?", time_zone, day_start_interval, 13 - week_start, time_zone, day_start_interval, day_start_interval, time_zone]
24
+ ["(DATE_TRUNC('day', #{day_start_column} - INTERVAL '1 day' * ((? + EXTRACT(DOW FROM #{day_start_column})::integer) % 7)) + INTERVAL ?)::date", time_zone, day_start_interval, 13 - week_start, time_zone, day_start_interval, day_start_interval]
25
25
  when :custom
26
- ["TO_TIMESTAMP(FLOOR(EXTRACT(EPOCH FROM #{column}::timestamptz) / ?) * ?)", n_seconds, n_seconds]
27
- else
28
- if day_start == 0
29
- # prettier
30
- ["DATE_TRUNC(?, #{day_start_column}) AT TIME ZONE ?", period, time_zone, day_start_interval, time_zone]
26
+ if @relation.connection.adapter_name == "Redshift"
27
+ ["TIMESTAMP 'epoch' + (FLOOR(EXTRACT(EPOCH FROM #{column}::timestamp) / ?) * ?) * INTERVAL '1 second'", n_seconds, n_seconds]
31
28
  else
32
- ["(DATE_TRUNC(?, #{day_start_column}) + INTERVAL ?) AT TIME ZONE ?", period, time_zone, day_start_interval, day_start_interval, time_zone]
29
+ ["TO_TIMESTAMP(FLOOR(EXTRACT(EPOCH FROM #{column}::timestamptz) / ?) * ?)", n_seconds, n_seconds]
33
30
  end
31
+ when :day, :month, :quarter, :year
32
+ ["DATE_TRUNC(?, #{day_start_column})::date", period, time_zone, day_start_interval]
33
+ else
34
+ # day start is always 0 for seconds, minute, hour
35
+ ["DATE_TRUNC(?, #{day_start_column}) AT TIME ZONE ?", period, time_zone, day_start_interval, time_zone]
34
36
  end
35
37
 
36
38
  clean_group_clause(@relation.send(:sanitize_sql_array, query))
@@ -7,7 +7,7 @@ module Groupdate
7
7
 
8
8
  query =
9
9
  if period == :week
10
- ["strftime('%Y-%m-%d 00:00:00 UTC', #{column}, '-6 days', ?)", "weekday #{(week_start + 1) % 7}"]
10
+ ["strftime('%Y-%m-%d', #{column}, '-6 days', ?)", "weekday #{(week_start + 1) % 7}"]
11
11
  elsif period == :custom
12
12
  ["datetime((strftime('%s', #{column}) / ?) * ?, 'unixepoch')", n_seconds, n_seconds]
13
13
  else
@@ -32,13 +32,13 @@ module Groupdate
32
32
  when :hour
33
33
  "%Y-%m-%d %H:00:00 UTC"
34
34
  when :day
35
- "%Y-%m-%d 00:00:00 UTC"
35
+ "%Y-%m-%d"
36
36
  when :month
37
- "%Y-%m-01 00:00:00 UTC"
37
+ "%Y-%m-01"
38
38
  when :quarter
39
39
  raise Groupdate::Error, "Quarter not supported for SQLite"
40
40
  else # year
41
- "%Y-01-01 00:00:00 UTC"
41
+ "%Y-01-01"
42
42
  end
43
43
 
44
44
  ["strftime(?, #{column})", format]
@@ -22,7 +22,7 @@ module Groupdate
22
22
  end
23
23
 
24
24
  def validate_keywords
25
- known_keywords = [:time_zone, :dates, :series, :format, :locale, :range, :reverse]
25
+ known_keywords = [:time_zone, :series, :format, :locale, :range, :reverse]
26
26
 
27
27
  if %i[week day_of_week].include?(period)
28
28
  known_keywords << :week_start
@@ -145,6 +145,15 @@ module Groupdate
145
145
  lambda { |k| k.to_i }
146
146
  when :day_of_week
147
147
  lambda { |k| (k.to_i - 1 - week_start) % 7 }
148
+ when :day, :week, :month, :quarter, :year
149
+ if day_start != 0
150
+ day_start_hour = day_start / 3600
151
+ day_start_min = (day_start % 3600) / 60
152
+ day_start_sec = (day_start % 3600) % 60
153
+ lambda { |k| k.in_time_zone(time_zone).change(hour: day_start_hour, min: day_start_min, sec: day_start_sec) }
154
+ else
155
+ lambda { |k| k.in_time_zone(time_zone) }
156
+ end
148
157
  else
149
158
  utc = ActiveSupport::TimeZone["UTC"]
150
159
  lambda { |k| (k.is_a?(String) || !k.respond_to?(:to_time) ? utc.parse(k.to_s) : k.to_time).in_time_zone(time_zone) }
@@ -193,11 +202,15 @@ module Groupdate
193
202
  adapter = Groupdate.adapters[adapter_name]
194
203
  raise Groupdate::Error, "Connection adapter not supported: #{adapter_name}" unless adapter
195
204
 
205
+ # very important
206
+ column = validate_column(field)
207
+ column = resolve_column(relation, column)
208
+
196
209
  # generate ActiveRecord relation
197
210
  relation =
198
211
  adapter.new(
199
212
  relation,
200
- column: field,
213
+ column: column,
201
214
  period: magic.period,
202
215
  time_zone: magic.time_zone,
203
216
  time_range: magic.time_range,
@@ -213,6 +226,30 @@ module Groupdate
213
226
  relation
214
227
  end
215
228
 
229
+ class << self
230
+ # basic version of Active Record disallow_raw_sql!
231
+ # symbol = column (safe), Arel node = SQL (safe), other = untrusted
232
+ # matches table.column and column
233
+ def validate_column(column)
234
+ unless column.is_a?(Symbol) || column.is_a?(Arel::Nodes::SqlLiteral)
235
+ column = column.to_s
236
+ unless /\A\w+(\.\w+)?\z/i.match(column)
237
+ raise ActiveRecord::UnknownAttributeReference, "Query method called with non-attribute argument(s): #{column.inspect}. Use Arel.sql() for known-safe values."
238
+ end
239
+ end
240
+ column
241
+ end
242
+
243
+ # resolves eagerly
244
+ # need to convert both where_clause (easy)
245
+ # and group_clause (not easy) if want to avoid this
246
+ def resolve_column(relation, column)
247
+ node = relation.send(:relation).send(:arel_columns, [column]).first
248
+ node = Arel::Nodes::SqlLiteral.new(node) if node.is_a?(String)
249
+ relation.connection.visitor.accept(node, Arel::Collectors::SQLString.new).value
250
+ end
251
+ end
252
+
216
253
  # allow any options to keep flexible for future
217
254
  def self.process_result(relation, result, **options)
218
255
  relation.groupdate_values.reverse.each do |gv|
@@ -2,8 +2,6 @@ module Groupdate
2
2
  class SeriesBuilder
3
3
  attr_reader :period, :time_zone, :day_start, :week_start, :n_seconds, :options
4
4
 
5
- CHECK_PERIODS = [:day, :week, :month, :quarter, :year]
6
-
7
5
  def initialize(period:, time_zone:, day_start:, week_start:, n_seconds:, **options)
8
6
  @period = period
9
7
  @time_zone = time_zone
@@ -23,41 +21,12 @@ module Groupdate
23
21
  verified_data[k] = data.delete(k)
24
22
  end
25
23
 
26
- # this is a fun one
27
- # PostgreSQL and Ruby both return the 2nd hour when converting/parsing a backward DST change
28
- # Other databases and Active Support return the 1st hour (as expected)
29
- # Active Support good: ActiveSupport::TimeZone["America/Los_Angeles"].parse("2013-11-03 01:00:00")
30
- # MySQL good: SELECT CONVERT_TZ('2013-11-03 01:00:00', 'America/Los_Angeles', 'Etc/UTC');
31
- # Ruby not good: Time.parse("2013-11-03 01:00:00")
32
- # PostgreSQL not good: SELECT '2013-11-03 01:00:00'::timestamp AT TIME ZONE 'America/Los_Angeles';
33
- # we need to account for this here
34
- if series_default && CHECK_PERIODS.include?(period)
35
- data.each do |k, v|
36
- key = multiple_groups ? k[group_index] : k
37
- # TODO only do this for PostgreSQL
38
- # this may mask some inconsistent time zone errors
39
- # but not sure there's a better approach
40
- if key.hour == (key - 1.hour).hour && series.include?(key - 1.hour)
41
- key -= 1.hour
42
- if multiple_groups
43
- k[group_index] = key
44
- else
45
- k = key
46
- end
47
- verified_data[k] = v
48
- elsif key != round_time(key)
49
- # only need to show what database returned since it will cast in Ruby time zone
50
- raise Groupdate::Error, "Database and Ruby have inconsistent time zone info. Database returned #{key}"
51
- end
52
- end
53
- end
54
-
55
24
  unless entire_series?(series_default)
56
25
  series = series.select { |k| verified_data[k] }
57
26
  end
58
27
 
59
28
  value = 0
60
- result = Hash[series.map do |k|
29
+ result = series.to_h do |k|
61
30
  value = verified_data[k] || (@options[:carry_forward] && value) || default_value
62
31
  key =
63
32
  if multiple_groups
@@ -67,7 +36,7 @@ module Groupdate
67
36
  end
68
37
 
69
38
  [key, value]
70
- end]
39
+ end
71
40
 
72
41
  result
73
42
  end
@@ -81,7 +50,7 @@ module Groupdate
81
50
 
82
51
  if day_start != 0
83
52
  # apply day_start to a time object that's not affected by DST
84
- time = change_zone.call(time, utc)
53
+ time = time.change(zone: utc)
85
54
  time -= day_start.seconds
86
55
  end
87
56
 
@@ -121,23 +90,12 @@ module Groupdate
121
90
 
122
91
  if day_start != 0 && time.is_a?(Time)
123
92
  time += day_start.seconds
124
- time = change_zone.call(time, time_zone)
93
+ time = time.change(zone: time_zone)
125
94
  end
126
95
 
127
96
  time
128
97
  end
129
98
 
130
- def change_zone
131
- @change_zone ||= begin
132
- if ActiveSupport::VERSION::STRING >= "5.2"
133
- ->(time, zone) { time.change(zone: zone) }
134
- else
135
- # TODO make more efficient
136
- ->(time, zone) { zone.parse(time.strftime("%Y-%m-%d %H:%M:%S")) }
137
- end
138
- end
139
- end
140
-
141
99
  def time_range
142
100
  @time_range ||= begin
143
101
  time_range = options[:range]
@@ -148,10 +106,6 @@ module Groupdate
148
106
  case v
149
107
  when nil, Date, Time
150
108
  # good
151
- when String
152
- # TODO raise error in Groupdate 6
153
- warn "[groupdate] Range bounds should be Date or Time, not #{v.class.name}. This will raise an error in Groupdate 6"
154
- break
155
109
  else
156
110
  raise ArgumentError, "Range bounds should be Date or Time, not #{v.class.name}"
157
111
  end
@@ -289,7 +243,6 @@ module Groupdate
289
243
  def key_format
290
244
  @key_format ||= begin
291
245
  locale = options[:locale] || I18n.locale
292
- use_dates = options.key?(:dates) ? options[:dates] : Groupdate.dates
293
246
 
294
247
  if options[:format]
295
248
  if options[:format].respond_to?(:call)
@@ -312,7 +265,7 @@ module Groupdate
312
265
  I18n.localize(key, format: options[:format], locale: locale)
313
266
  end
314
267
  end
315
- elsif [:day, :week, :month, :quarter, :year].include?(period) && use_dates
268
+ elsif [:day, :week, :month, :quarter, :year].include?(period)
316
269
  lambda { |k| k.to_date }
317
270
  else
318
271
  lambda { |k| k }
@@ -1,3 +1,3 @@
1
1
  module Groupdate
2
- VERSION = "5.2.4"
2
+ VERSION = "6.0.0"
3
3
  end
data/lib/groupdate.rb CHANGED
@@ -12,7 +12,6 @@ require "groupdate/version"
12
12
  require "groupdate/adapters/base_adapter"
13
13
  require "groupdate/adapters/mysql_adapter"
14
14
  require "groupdate/adapters/postgresql_adapter"
15
- require "groupdate/adapters/redshift_adapter"
16
15
  require "groupdate/adapters/sqlite_adapter"
17
16
 
18
17
  module Groupdate
@@ -21,10 +20,9 @@ module Groupdate
21
20
  PERIODS = [:second, :minute, :hour, :day, :week, :month, :quarter, :year, :day_of_week, :hour_of_day, :minute_of_hour, :day_of_month, :day_of_year, :month_of_year]
22
21
  METHODS = PERIODS.map { |v| :"group_by_#{v}" } + [:group_by_period]
23
22
 
24
- mattr_accessor :week_start, :day_start, :time_zone, :dates
23
+ mattr_accessor :week_start, :day_start, :time_zone
25
24
  self.week_start = :sunday
26
25
  self.day_start = 0
27
- self.dates = true
28
26
 
29
27
  # api for gems like ActiveMedian
30
28
  def self.process_result(relation, result, **options)
@@ -46,8 +44,7 @@ module Groupdate
46
44
  end
47
45
 
48
46
  Groupdate.register_adapter ["Mysql2", "Mysql2Spatial", "Mysql2Rgeo"], Groupdate::Adapters::MySQLAdapter
49
- Groupdate.register_adapter ["PostgreSQL", "PostGIS"], Groupdate::Adapters::PostgreSQLAdapter
50
- Groupdate.register_adapter "Redshift", Groupdate::Adapters::RedshiftAdapter
47
+ Groupdate.register_adapter ["PostgreSQL", "PostGIS", "Redshift"], Groupdate::Adapters::PostgreSQLAdapter
51
48
  Groupdate.register_adapter "SQLite", Groupdate::Adapters::SQLiteAdapter
52
49
 
53
50
  require "groupdate/enumerable"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: groupdate
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.4
4
+ version: 6.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-16 00:00:00.000000000 Z
11
+ date: 2022-01-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '5'
19
+ version: '5.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '5'
26
+ version: '5.2'
27
27
  description:
28
28
  email: andrew@ankane.org
29
29
  executables: []
@@ -39,7 +39,6 @@ files:
39
39
  - lib/groupdate/adapters/base_adapter.rb
40
40
  - lib/groupdate/adapters/mysql_adapter.rb
41
41
  - lib/groupdate/adapters/postgresql_adapter.rb
42
- - lib/groupdate/adapters/redshift_adapter.rb
43
42
  - lib/groupdate/adapters/sqlite_adapter.rb
44
43
  - lib/groupdate/enumerable.rb
45
44
  - lib/groupdate/magic.rb
@@ -59,14 +58,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
59
58
  requirements:
60
59
  - - ">="
61
60
  - !ruby/object:Gem::Version
62
- version: '2.4'
61
+ version: '2.6'
63
62
  required_rubygems_version: !ruby/object:Gem::Requirement
64
63
  requirements:
65
64
  - - ">="
66
65
  - !ruby/object:Gem::Version
67
66
  version: '0'
68
67
  requirements: []
69
- rubygems_version: 3.2.32
68
+ rubygems_version: 3.3.3
70
69
  signing_key:
71
70
  specification_version: 4
72
71
  summary: The simplest way to group temporal data
@@ -1,39 +0,0 @@
1
- module Groupdate
2
- module Adapters
3
- class RedshiftAdapter < BaseAdapter
4
- def group_clause
5
- time_zone = @time_zone.tzinfo.name
6
- day_start_column = "CONVERT_TIMEZONE(?, #{column}::timestamp) - INTERVAL ?"
7
- day_start_interval = "#{day_start} second"
8
-
9
- query =
10
- case period
11
- when :minute_of_hour
12
- ["EXTRACT(MINUTE from #{day_start_column})::integer", time_zone, day_start_interval]
13
- when :hour_of_day
14
- ["EXTRACT(HOUR from #{day_start_column})::integer", time_zone, day_start_interval]
15
- when :day_of_week
16
- ["EXTRACT(DOW from #{day_start_column})::integer", time_zone, day_start_interval]
17
- when :day_of_month
18
- ["EXTRACT(DAY from #{day_start_column})::integer", time_zone, day_start_interval]
19
- when :day_of_year
20
- ["EXTRACT(DOY from #{day_start_column})::integer", time_zone, day_start_interval]
21
- when :month_of_year
22
- ["EXTRACT(MONTH from #{day_start_column})::integer", time_zone, day_start_interval]
23
- when :week # start on Sunday, not Redshift default Monday
24
- # Redshift does not return timezone information; it
25
- # always says it is in UTC time, so we must convert
26
- # back to UTC to play properly with the rest of Groupdate.
27
- week_start_interval = "#{week_start} day"
28
- ["CONVERT_TIMEZONE(?, 'Etc/UTC', DATE_TRUNC('week', #{day_start_column} - INTERVAL ?) + INTERVAL ? + INTERVAL ?)::timestamp", time_zone, time_zone, day_start_interval, week_start_interval, week_start_interval, day_start_interval]
29
- when :custom
30
- raise Groupdate::Error, "Not implemented yet"
31
- else
32
- ["CONVERT_TIMEZONE(?, 'Etc/UTC', DATE_TRUNC(?, #{day_start_column}) + INTERVAL ?)::timestamp", time_zone, period, time_zone, day_start_interval, day_start_interval]
33
- end
34
-
35
- @relation.send(:sanitize_sql_array, query)
36
- end
37
- end
38
- end
39
- end