Commit 7fbc1c1b52f5e212f3980269fe9e5bfab1bb8e8e
Committed by
Andrew Kane
1 parent
5f8d5fba
Exists in
master
and in
6 other branches
Add Redshift adapter (#133)
Add Redshift adapter
Showing
6 changed files
with
65 additions
and
4 deletions
Show diff stats
Gemfile
README.md
... | ... | @@ -13,7 +13,7 @@ The simplest way to group by: |
13 | 13 | |
14 | 14 | Works with Rails 3.1+ |
15 | 15 | |
16 | -Supports PostgreSQL and MySQL, plus arrays and hashes | |
16 | +Supports PostgreSQL, MySQL, and Redshift, plus arrays and hashes | |
17 | 17 | |
18 | 18 | [](https://travis-ci.org/ankane/groupdate) |
19 | 19 | ... | ... |
Rakefile
... | ... | @@ -4,7 +4,7 @@ require "rake/testtask" |
4 | 4 | task default: :test |
5 | 5 | Rake::TestTask.new do |t| |
6 | 6 | t.libs << "test" |
7 | - t.pattern = "test/**/*_test.rb" | |
7 | + t.test_files = FileList["test/**/*_test.rb"].exclude(/redshift/) | |
8 | 8 | end |
9 | 9 | |
10 | 10 | namespace :test do |
... | ... | @@ -16,4 +16,8 @@ namespace :test do |
16 | 16 | t.libs << "test" |
17 | 17 | t.pattern = "test/mysql_test.rb" |
18 | 18 | end |
19 | + Rake::TestTask.new(:redshift) do |t| | |
20 | + t.libs << "test" | |
21 | + t.pattern = "test/redshift_test.rb" | |
22 | + end | |
19 | 23 | end | ... | ... |
lib/groupdate/magic.rb
... | ... | @@ -77,6 +77,25 @@ module Groupdate |
77 | 77 | else |
78 | 78 | ["(DATE_TRUNC('#{field}', (#{column}::timestamptz - INTERVAL '#{day_start} second') AT TIME ZONE ?) + INTERVAL '#{day_start} second') AT TIME ZONE ?", time_zone, time_zone] |
79 | 79 | end |
80 | + when "Redshift" | |
81 | + case field | |
82 | + when :day_of_week # Sunday = 0, Monday = 1, etc. | |
83 | + ["EXTRACT(DOW from CONVERT_TIMEZONE(?, #{column}::timestamp) - INTERVAL '#{day_start} second')::integer", time_zone] | |
84 | + when :hour_of_day | |
85 | + ["EXTRACT(HOUR from CONVERT_TIMEZONE(?, #{column}::timestamp) - INTERVAL '#{day_start} second')::integer", time_zone] | |
86 | + when :day_of_month | |
87 | + ["EXTRACT(DAY from CONVERT_TIMEZONE(?, #{column}::timestamp) - INTERVAL '#{day_start} second')::integer", time_zone] | |
88 | + when :month_of_year | |
89 | + ["EXTRACT(MONTH from CONVERT_TIMEZONE(?, #{column}::timestamp) - INTERVAL '#{day_start} second')::integer", time_zone] | |
90 | + when :week # start on Sunday, not Redshift default Monday | |
91 | + # Redshift does not return timezone information; it | |
92 | + # always says it is in UTC time, so we must convert | |
93 | + # back to UTC to play properly with the rest of Groupdate. | |
94 | + # | |
95 | + ["CONVERT_TIMEZONE(?, 'Etc/UTC', DATE_TRUNC(?, CONVERT_TIMEZONE(?, #{column}) - INTERVAL '#{week_start} day' - INTERVAL '#{day_start} second'))::timestamp + INTERVAL '#{week_start} day' + INTERVAL '#{day_start} second'", time_zone, field, time_zone] | |
96 | + else | |
97 | + ["CONVERT_TIMEZONE(?, 'Etc/UTC', DATE_TRUNC(?, CONVERT_TIMEZONE(?, #{column}) - INTERVAL '#{day_start} second'))::timestamp + INTERVAL '#{day_start} second'", time_zone, field, time_zone] | |
98 | + end | |
80 | 99 | else |
81 | 100 | raise "Connection adapter not supported: #{adapter_name}" |
82 | 101 | end | ... | ... |
... | ... | @@ -0,0 +1,18 @@ |
1 | +require_relative "test_helper" | |
2 | + | |
3 | +class TestRedshift < Minitest::Test | |
4 | + include TestGroupdate | |
5 | + include TestDatabase | |
6 | + | |
7 | + def setup | |
8 | + super | |
9 | + @@setup ||= begin | |
10 | + abort("REDSHIFT_URL environment variable must be set in order to run tests") unless ENV["REDSHIFT_URL"].present? | |
11 | + | |
12 | + ActiveRecord::Base.establish_connection(ENV["REDSHIFT_URL"]) | |
13 | + | |
14 | + create_redshift_tables | |
15 | + true | |
16 | + end | |
17 | + end | |
18 | +end | ... | ... |
test/test_helper.rb
... | ... | @@ -49,6 +49,22 @@ def create_tables |
49 | 49 | end |
50 | 50 | end |
51 | 51 | |
52 | +def create_redshift_tables | |
53 | + ActiveRecord::Migration.verbose = false | |
54 | + | |
55 | + if ActiveRecord::Migration.table_exists?(:users) | |
56 | + ActiveRecord::Migration.drop_table(:users, force: :cascade) | |
57 | + end | |
58 | + | |
59 | + if ActiveRecord::Migration.table_exists?(:posts) | |
60 | + ActiveRecord::Migration.drop_table(:posts, force: :cascade) | |
61 | + end | |
62 | + | |
63 | + ActiveRecord::Migration.execute "CREATE TABLE users (id INT IDENTITY(1,1) PRIMARY KEY, name VARCHAR(255), score INT, created_at DATETIME, created_on DATE);" | |
64 | + | |
65 | + ActiveRecord::Migration.execute "CREATE TABLE posts (id INT IDENTITY(1,1) PRIMARY KEY, user_id INT REFERENCES users, created_at DATETIME);" | |
66 | +end | |
67 | + | |
52 | 68 | module TestDatabase |
53 | 69 | def test_zeros_previous_scope |
54 | 70 | create_user "2013-05-01" |
... | ... | @@ -332,8 +348,11 @@ module TestDatabase |
332 | 348 | created_on: created_at ? Date.parse(created_at) : nil |
333 | 349 | ) |
334 | 350 | |
335 | - # hack for MySQL adapter | |
336 | - # user.update_attributes(created_at: nil, created_on: nil) if created_at.nil? | |
351 | + # hack for Redshift adapter, which doesn't return id on creation... | |
352 | + user = User.last if user.id.nil? | |
353 | + | |
354 | + # hack for MySQL & Redshift adapters | |
355 | + user.update_attributes(created_at: nil, created_on: nil) if created_at.nil? | |
337 | 356 | |
338 | 357 | user |
339 | 358 | end | ... | ... |