activerecord-redshift-adapter 0.9.1 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -33,6 +33,7 @@ redshift_development:
33
33
  ```
34
34
 
35
35
  ## options
36
+ ```html
36
37
  <table>
37
38
  <tr>
38
39
  <th>option</th>
@@ -47,3 +48,54 @@ redshift_development:
47
48
  <td>force timezone for datetime when select values. ActiveRecord default timezone will set if not given.</td>
48
49
  </tr>
49
50
  </table>
51
+ ```
52
+
53
+ ## TableManager
54
+
55
+ Helpful code to clone redshift tables
56
+
57
+ ```sql
58
+ create table foos
59
+ (
60
+ id int not null primary key distkey,
61
+ name varchar(255) unique sortkey
62
+ );
63
+ ```
64
+
65
+ ```ruby
66
+ class Foo < ActiveRecord::Base
67
+ end
68
+
69
+ require 'activerecord_redshift_adapter'
70
+
71
+ table_manager = ActiverecordRedshift::TableManager.new(Foo.connection, :exemplar_table_name => Foo.table_name)
72
+ table_manager.duplicate_table
73
+ ```
74
+
75
+ yields:
76
+
77
+ ```sql
78
+ select oid from pg_namespace where nspname = 'public' limit 1;
79
+
80
+ select oid,reldiststyle from pg_class where relnamespace = 2200 and relname = 'foos' limit 1;
81
+
82
+ select contype,conkey from pg_constraint where connamespace = 2200 and conrelid = 212591;
83
+
84
+ select attname,attnum from pg_attribute where attrelid = 212591 and attnum in (2,1);
85
+
86
+ show search_path;
87
+
88
+ set search_path = 'public';
89
+
90
+ select * from pg_table_def where tablename = 'foos' and schemaname = 'public';
91
+
92
+ create temporary table temporary_events_25343
93
+ (
94
+ id integer not null distkey,
95
+ name character varying(255),
96
+ primary key (id),
97
+ unique (name)
98
+ ) sortkey (name);
99
+
100
+ set search_path = '$user','public';
101
+ ```
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
7
7
  s.name = 'activerecord-redshift-adapter'
8
8
  s.version = ActiverecordRedshiftAdapter::VERSION
9
9
  s.license = 'New BSD License'
10
- s.date = '2013-03-23'
10
+ s.date = '2013-09-17'
11
11
  s.summary = "Rails 3 database adapter support for AWS RedShift."
12
12
  s.description = "This gem provides the Rails 3 with database adapter for AWS RedShift."
13
13
  s.authors = ["Keith Gabryelski"]
@@ -249,7 +249,7 @@ module ActiveRecord
249
249
  ADAPTER_NAME = 'Redshift'
250
250
 
251
251
  NATIVE_DATABASE_TYPES = {
252
- :primary_key => "serial primary key",
252
+ :primary_key => "bigint primary key",
253
253
  :string => { :name => "character varying", :limit => 255 },
254
254
  :text => { :name => "text" },
255
255
  :integer => { :name => "integer" },
@@ -413,7 +413,7 @@ module ActiveRecord
413
413
  end
414
414
 
415
415
  def supports_insert_with_returning?
416
- true
416
+ false
417
417
  end
418
418
 
419
419
  def supports_ddl_transactions?
@@ -538,7 +538,7 @@ module ActiveRecord
538
538
  # REFERENTIAL INTEGRITY ====================================
539
539
 
540
540
  def supports_disable_referential_integrity? #:nodoc:
541
- true
541
+ false
542
542
  end
543
543
 
544
544
  def disable_referential_integrity #:nodoc:
@@ -609,9 +609,12 @@ module ActiveRecord
609
609
  table_ref = extract_table_ref_from_insert_sql(sql)
610
610
  pk = primary_key(table_ref) if table_ref
611
611
  end
612
-
613
- if pk
612
+
613
+ if pk && use_insert_returning?
614
614
  select_value("#{sql} RETURNING #{quote_column_name(pk)}")
615
+ elsif pk
616
+ super
617
+ last_insert_id_value(sequence_name || default_sequence_name(table_ref, pk))
615
618
  else
616
619
  super
617
620
  end
@@ -709,7 +712,7 @@ module ActiveRecord
709
712
  pk = primary_key(table_ref) if table_ref
710
713
  end
711
714
 
712
- sql = "#{sql} RETURNING #{quote_column_name(pk)}" if pk
715
+ sql = "#{sql} RETURNING #{quote_column_name(pk)}" if pk && use_insert_returning?
713
716
 
714
717
  [sql, binds]
715
718
  end
@@ -0,0 +1,218 @@
1
+ module ActiverecordRedshift
2
+ class TableManager
3
+ attr_reader :default_options
4
+
5
+ DEFAULT_OPTIONS = { :exemplar_table_name => nil, :add_identity => false, :temporary => true}
6
+
7
+ def initialize(connection, default_options = {})
8
+ @connection = connection
9
+ table_name_options = {}
10
+ if default_options[:partitioned_model]
11
+ model = default_options[:partitioned_model]
12
+ default_options[:exemplar_table_name] = model.table_name
13
+ default_options[:schema_name] = model.configurator.schema_name
14
+ end
15
+
16
+ if default_options[:table_name].blank?
17
+ connection_pid = @connection.execute("select pg_backend_pid() as pid").first['pid'].to_i
18
+ table_name_options[:table_name] = "temporary_events_#{connection_pid}"
19
+ end
20
+ @default_options = DEFAULT_OPTIONS.merge(table_name_options).merge(default_options)
21
+ end
22
+
23
+ def partitioned_model
24
+ return @default_options[:partitioned_model]
25
+ end
26
+
27
+ def schema_name
28
+ return @default_options[:schema_name]
29
+ end
30
+
31
+ def exemplar_table_name
32
+ return @default_options[:exemplar_table_name]
33
+ end
34
+
35
+ def add_identity
36
+ return @default_options[:add_identity]
37
+ end
38
+
39
+ def temporary
40
+ return @default_options[:temporary]
41
+ end
42
+
43
+ def base_table_name
44
+ return @default_options[:table_name]
45
+ end
46
+
47
+ def table_name
48
+ if schema_name.blank?
49
+ return base_table_name
50
+ end
51
+ return "#{schema_name}.#{base_table_name}"
52
+ end
53
+
54
+ def drop_table
55
+ @connection.execute("drop table #{table_name}")
56
+ end
57
+
58
+ def duplicate_table(options = {})
59
+ current_options = @default_options.merge(options)
60
+ target_table_name = current_options[:table_name]
61
+ raise "target_table_name not set" if target_table_name.blank?
62
+ exemplar_table_name = current_options[:exemplar_table_name]
63
+ raise "exemplar_table_name not set" if exemplar_table_name.blank?
64
+ table_name_elements = exemplar_table_name.split('.');
65
+ if table_name_elements.length == 1
66
+ table_name_elements.unshift("public")
67
+ end
68
+ schema_name = table_name_elements[0]
69
+ parent_table_name = table_name_elements[1]
70
+
71
+ # first find the diststyle
72
+ ## namespace first
73
+ sql = "select oid from pg_namespace where nspname = '#{schema_name}' limit 1"
74
+ schema_oid = @connection.execute(sql).first['oid'].to_i
75
+
76
+ ## now the diststyle 0 = even, 1 = some column
77
+ sql = "select oid,reldiststyle from pg_class where relnamespace = #{schema_oid} and relname = '#{parent_table_name}' limit 1"
78
+ pg_class_row = @connection.execute(sql).first
79
+ reldiststyle = pg_class_row['reldiststyle'].to_i
80
+ even_diststyle = (reldiststyle == 0)
81
+ table_oid = pg_class_row['oid'].to_i
82
+
83
+ ## get unique and primary key constraints (pg_constraints)
84
+ sql = "select contype,conkey from pg_constraint where connamespace = #{schema_oid} and conrelid = #{table_oid}"
85
+ primary_key = nil
86
+ uniques = []
87
+ @connection.execute(sql).each do |row|
88
+ if row['contype'] == 'p'
89
+ # primary key
90
+ primary_key = row['conkey'][1..-2].split(',')
91
+ elsif row['contype'] == 'u'
92
+ # unique
93
+ uniques << row['conkey'][1..-2].split(',')
94
+ end
95
+ end
96
+
97
+ attnums = uniques.clone
98
+ unless primary_key.blank?
99
+ attnums << primary_key
100
+ end
101
+ attnums = attnums.flatten.uniq
102
+
103
+ sql = "select attname,attnum from pg_attribute where attrelid = #{table_oid} and attnum in (#{attnums.join(',')})"
104
+ column_names = {}
105
+ @connection.execute(sql).each do |row|
106
+ column_names[row['attnum']] = row['attname']
107
+ end
108
+
109
+ with_search_path([schema_name]) do
110
+ # select * from pg_table_def where tablename = 'bids' and schemaname = 'public';
111
+ ## column, type, encoding, distkey, sortkey, not null
112
+ sortkeys = []
113
+ sql_columns = []
114
+
115
+ if current_options[:add_identity]
116
+ sql_columns << "_identity bigint identity"
117
+ end
118
+
119
+ sql = "select * from pg_table_def where tablename = '#{parent_table_name}' and schemaname = '#{schema_name}'"
120
+ sql_column_rows = @connection.execute(sql)
121
+ sql_column_rows.each do |row|
122
+ column_info = []
123
+ column_name = row['column']
124
+ column_info << column_name
125
+ column_info << row['type']
126
+ if row['notnull'] == "t"
127
+ column_info << "not null"
128
+ end
129
+ if row['distkey'] == "t"
130
+ column_info << "distkey"
131
+ end
132
+ if row['encoding'] != 'none'
133
+ column_info << "encode #{row['encoding']}"
134
+ end
135
+ if row['sortkey'] != "0"
136
+ sortkeys[row['sortkey'].to_i - 1] = column_name
137
+ end
138
+ sql_columns << column_info.join(" ")
139
+ end
140
+
141
+ unless primary_key.blank?
142
+ sql_columns << "primary key (#{primary_key.map{|pk| column_names[pk]}.join(',')})"
143
+ end
144
+
145
+ uniques.each do |unique|
146
+ sql_columns << "unique (#{unique.map{|uk| column_names[uk]}.join(',')})"
147
+ end
148
+
149
+ if sortkeys.blank?
150
+ sql_sortkeys = ""
151
+ else
152
+ sql_sortkeys = " sortkey (#{sortkeys.join(',')})"
153
+ end
154
+ sql = <<-SQL
155
+ create #{"temporary " if current_options[:temporary]}table #{table_name}
156
+ (
157
+ #{sql_columns.join(', ')}
158
+ ) #{"diststyle even " if even_diststyle}#{sql_sortkeys}
159
+ SQL
160
+ @connection.execute(sql)
161
+ end
162
+ end
163
+
164
+ def table_def(table_name)
165
+ table_parts = table_name.split('.')
166
+ if table_parts.length == 1
167
+ name = table_parts.first
168
+ search_path = ["public"]
169
+ else
170
+ name = table_parts.last
171
+ search_path = [table_parts.first]
172
+ end
173
+
174
+ with_search_path(search_path) do
175
+ return @connection.execute("select * from pg_table_def where tablename = '#{name}'").to_a
176
+ end
177
+ end
178
+
179
+ # search_path = array
180
+ # modes: :prefix, :suffix, :replace
181
+ def with_search_path(search_path, mode = :replace, &block)
182
+ unless search_path.is_a? Array
183
+ raise "search_path must be an Array"
184
+ end
185
+
186
+ old_search_path = get_search_path
187
+ if mode == :prefix
188
+ new_search_path = search_path + old_search_path
189
+ elsif mode == :suffix
190
+ new_search_path = old_search_path + search_path
191
+ elsif mode == :replace
192
+ new_search_path = search_path
193
+ else
194
+ raise "mode must be :prefix, :suffix, :replace"
195
+ end
196
+
197
+ set_search_path(new_search_path)
198
+ begin
199
+ yield
200
+ ensure
201
+ set_search_path(old_search_path)
202
+ end
203
+ end
204
+
205
+ def get_search_path
206
+ return @connection.execute("show search_path").to_a.first["search_path"].split(',').map{|p| p.delete('" ')}
207
+ end
208
+
209
+ def set_search_path(search_path)
210
+ unless search_path.is_a? Array
211
+ raise "search_path must be an Array"
212
+ end
213
+ quoted_search_path = search_path.map{|sp| "'#{sp}'"}.join(',')
214
+ @connection.execute("set search_path = #{quoted_search_path}")
215
+ end
216
+
217
+ end
218
+ end
@@ -1 +1,4 @@
1
1
  require 'activerecord_redshift_adapter/version'
2
+ require 'activerecord_redshift/table_manager'
3
+ require 'monkeypatch_activerecord'
4
+ require 'monkeypatch_arel'
@@ -1,4 +1,4 @@
1
1
  module ActiverecordRedshiftAdapter
2
2
  # the current version of this gem
3
- VERSION = "0.9.1"
3
+ VERSION = "0.9.3"
4
4
  end
@@ -0,0 +1,195 @@
1
+ module ActiveRecord
2
+ module Querying
3
+ delegate :unload, :copy, :to => :scoped
4
+ end
5
+ end
6
+
7
+ module ActiveRecord::QueryMethods
8
+ module CopyUnloadParser
9
+ def self.parse_options(options, options_hash, valid_switches, valid_options, valid_unquoted_options, valid_special_options)
10
+ # credentials first
11
+ credentials = nil
12
+ if options_hash.has_key?(:credentials)
13
+ credentials = options_hash[:credentials]
14
+ else
15
+ creds = {}
16
+ creds[:aws_access_key_id] = options_hash[:aws_access_key_id] if options_hash.has_key?(:aws_access_key_id)
17
+ creds[:aws_secret_access_key] = options_hash[:aws_secret_access_key] if options_hash.has_key?(:aws_secret_access_key)
18
+ creds[:token] = options_hash[:token] if options_hash.has_key?(:token)
19
+ creds[:master_symmetric_key] = options_hash[:master_symmetric_key] if options_hash.has_key?(:master_symmetric_key)
20
+ credentials = creds.map{|k,v| "#{k}=#{v}"}.join(';')
21
+ end
22
+
23
+ option_list = []
24
+ option_list << "WITH CREDENTIALS AS #{connection.quote_value(credentials)}" unless credentials.blank?
25
+
26
+ valid_switches.each do |switch_name|
27
+ if options.include? switch_name
28
+ option_list << switch_name.to_s.upcase
29
+ end
30
+ end
31
+
32
+ valid_options.each do |option_name|
33
+ if options_hash.has_key? option_name
34
+ option_list << "#{option_name.to_s.upcase} AS #{connection.quote_value(options_hash[option_name])}"
35
+ end
36
+ end
37
+
38
+ valid_unquoted_options.each do |option_name|
39
+ if options_hash.has_key? option_name
40
+ option_list << "#{option_name.to_s.upcase} #{options_hash[option_name]}"
41
+ end
42
+ end
43
+
44
+ return credentials, option_list
45
+ end
46
+ end
47
+ end
48
+
49
+ module ActiveRecord
50
+ module QueryMethods
51
+ # UNLOAD ('select_statement')
52
+ # TO 's3_path'
53
+ # [ WITH ] CREDENTIALS [AS] 'aws_access_credentials'
54
+ # [ option [ ... ] ]
55
+ #
56
+ # where option is
57
+ #
58
+ # { DELIMITER [ AS ] 'delimiter_char'
59
+ # | FIXEDWIDTH [ AS ] 'fixedwidth_spec' }
60
+ # | ENCRYPTED
61
+ # | GZIP
62
+ # | ADDQUOTES
63
+ # | NULL [ AS ] 'null_string'
64
+ # | ESCAPE
65
+ # | ALLOWOVERWRITE
66
+ VALID_UNLOAD_SWITCHES = [
67
+ :gzip,
68
+ :addquotes,
69
+ :escape,
70
+ :allowoverwrite
71
+ ]
72
+ VALID_UNLOAD_OPTIONS = [
73
+ :delimiter,
74
+ :fixedwidth,
75
+ :null
76
+ ]
77
+ VALID_UNQUOTED_UNLOAD_OPTIONS = [ ]
78
+ VALID_SPECIAL_UNLOAD_OPTIONS = [
79
+ :credentials,
80
+ :aws_access_key_id,
81
+ :aws_secret_access_key,
82
+ :master_symmetric_key,
83
+ :token
84
+ ]
85
+
86
+ def unload(to_s3_filename, *options)
87
+ if options.last.is_a? Hash
88
+ options_hash = options.last
89
+ else
90
+ options_hash = {}
91
+ end
92
+
93
+ credentials, unload_options =
94
+ ActiveRecord::QueryMethods::CopyUnloadParser.parse_options(options, options_hash,
95
+ VALID_UNLOAD_SWITCHES, VALID_UNLOAD_OPTIONS, VALID_UNQUOTED_UNLOAD_OPTIONS, VALID_SPECIAL_UNLOAD_OPTIONS)
96
+
97
+
98
+ relation = Arel::Nodes::UnloadStatement.new(Arel::Nodes::Unload.new(Arel::Nodes::Relation.new(clone), to_s3_filename), unload_options.join(" "))
99
+ relation
100
+ end
101
+
102
+ VALID_COPY_SWITCHES = [
103
+ :encrypted,
104
+ :gzip,
105
+ :removequotes,
106
+ :explicit_ids,
107
+ :escape,
108
+ :acceptanydate,
109
+ :ignoreblanklines,
110
+ :truncatecolumns,
111
+ :fillrecord,
112
+ :trimblanks,
113
+ :noload,
114
+ :emptyasnull,
115
+ :blanksasnull,
116
+ :escape,
117
+ :roundec
118
+ ]
119
+ VALID_COPY_OPTIONS = [
120
+ :delimiter,
121
+ :fixedwidth,
122
+ :csv,
123
+ :acceptinvchars,
124
+ :dateformat,
125
+ :timeformat,
126
+ :null
127
+ ]
128
+
129
+ VALID_UNQUOTED_COPY_OPTIONS = [
130
+ :maxerror,
131
+ :ignoreheader,
132
+ :comprows,
133
+ :compupdate,
134
+ :statupdate
135
+ ]
136
+
137
+ VALID_SPECIAL_COPY_OPTIONS = [
138
+ :credentials,
139
+ :aws_access_key_id,
140
+ :aws_secret_access_key,
141
+ :master_symmetric_key,
142
+ :token
143
+ ]
144
+
145
+ # COPY table_name [ (column1 [,column2, ...]) ]
146
+ # FROM 's3://objectpath'
147
+ # [ WITH ] CREDENTIALS [AS] 'aws_access_credentials'
148
+ # [ option [ ... ] ]
149
+
150
+ # where option is
151
+
152
+ # { FIXEDWIDTH 'fixedwidth_spec'
153
+ # | [DELIMITER [ AS ] 'delimiter_char']
154
+ # [CSV [QUOTE [ AS ] 'quote_character']}
155
+
156
+ # | ENCRYPTED
157
+ # | GZIP
158
+ # | REMOVEQUOTES
159
+ # | EXPLICIT_IDS
160
+
161
+ # | ACCEPTINVCHARS [ AS ] ['replacement_char']
162
+ # | MAXERROR [ AS ] error_count
163
+ # | DATEFORMAT [ AS ] { 'dateformat_string' | 'auto' }
164
+ # | TIMEFORMAT [ AS ] { 'timeformat_string' | 'auto' | 'epochsecs' | 'epochmillisecs' }
165
+ # | IGNOREHEADER [ AS ] number_rows
166
+ # | ACCEPTANYDATE
167
+ # | IGNOREBLANKLINES
168
+ # | TRUNCATECOLUMNS
169
+ # | FILLRECORD
170
+ # | TRIMBLANKS
171
+ # | NOLOAD
172
+ # | NULL [ AS ] 'null_string'
173
+ # | EMPTYASNULL
174
+ # | BLANKSASNULL
175
+ # | COMPROWS numrows
176
+ # | COMPUPDATE [ { ON | TRUE } | { OFF | FALSE } ]
177
+ # | STATUPDATE [ { ON | TRUE } | { OFF | FALSE } ]
178
+ # | ESCAPE
179
+ # | ROUNDEC
180
+ def copy(to_s3_filename, *options)
181
+ if options.last.is_a? Hash
182
+ options_hash = options.last
183
+ else
184
+ options_hash = {}
185
+ end
186
+
187
+ credentials, copy_options =
188
+ ::ActiveRecord::QueryMethods::CopyUnloadParser.parse_options(options, options_hash,
189
+ VALID_COPY_SWITCHES, VALID_COPY_OPTIONS, VALID_UNQUOTED_COPY_OPTIONS, VALID_SPECIAL_COPY_OPTIONS)
190
+
191
+
192
+ conncection.execute(Arel::Nodes::CopyStatement.new(Arel::Nodes::Copy.new(table_name, to_s3_filename), copy_options.join(" ")).to_sql)
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,96 @@
1
+ module Arel
2
+ module Nodes
3
+ class Relation < Arel::Nodes::Unary
4
+ end
5
+
6
+ class Unload < Arel::Nodes::Binary
7
+ alias :statement :left
8
+ alias :statement= :left=
9
+ alias :to :right
10
+ alias :to= :right=
11
+ def initialize statement = nil, to = nil
12
+ super
13
+ end
14
+
15
+ def initialize_copy other
16
+ super
17
+ @right = @right.clone
18
+ end
19
+ end
20
+
21
+ class UnloadStatement < Arel::Nodes::Binary
22
+ alias :relation :left
23
+ alias :relation= :left=
24
+ alias :options :right
25
+ alias :options= :right=
26
+
27
+ def initialize relation = nil, options = []
28
+ super
29
+ end
30
+
31
+ def initialize_copy other
32
+ super
33
+ @right = @right.clone
34
+ end
35
+ end
36
+
37
+ class Copy < Arel::Nodes::Binary
38
+ alias :statement :left
39
+ alias :statement= :left=
40
+ alias :from :right
41
+ alias :from= :right=
42
+ def initialize statement = nil, from = nil
43
+ super
44
+ end
45
+
46
+ def initialize_copy other
47
+ super
48
+ @right = @right.clone
49
+ end
50
+ end
51
+
52
+ class CopyStatement < Arel::Nodes::Binary
53
+ alias :relation :left
54
+ alias :relation= :left=
55
+ alias :options :right
56
+ alias :options= :right=
57
+
58
+ def initialize relation = nil, options = []
59
+ super
60
+ end
61
+
62
+ def initialize_copy other
63
+ super
64
+ @right = @right.clone
65
+ end
66
+ end
67
+
68
+ end
69
+ end
70
+
71
+ module Arel
72
+ module Visitors
73
+ class ToSql < Arel::Visitors::Visitor
74
+
75
+ def visit_Arel_Nodes_UnloadStatement o
76
+ "#{visit o.relation} #{o.options}"
77
+ end
78
+
79
+ def visit_Arel_Nodes_Unload o
80
+ "UNLOAD (#{visit o.statement}) TO #{visit o.to}"
81
+ end
82
+
83
+ def visit_Arel_Nodes_CopyStatement o
84
+ "#{visit o.relation} #{o.options}"
85
+ end
86
+
87
+ def visit_Arel_Nodes_Copy o
88
+ "COPY #{o.statement} FROM #{visit o.from}"
89
+ end
90
+
91
+ def visit_Arel_Nodes_Relation o
92
+ visit o.expr.to_sql
93
+ end
94
+ end
95
+ end
96
+ end
@@ -5,6 +5,8 @@ port: 5432
5
5
  username: test_user
6
6
  password: test_password
7
7
  database: dev
8
+ #As RedShift does not support RETURNING keyword yet
9
+ use_insert_returning: false
8
10
  # ssl: true
9
11
  # search_path:
10
12
  # role:
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-redshift-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.9.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-23 00:00:00.000000000 Z
12
+ date: 2013-09-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pg
@@ -72,8 +72,11 @@ files:
72
72
  - Rakefile
73
73
  - activerecord-redshift-adapter.gemspec
74
74
  - lib/active_record/connection_adapters/redshift_adapter.rb
75
+ - lib/activerecord_redshift/table_manager.rb
75
76
  - lib/activerecord_redshift_adapter.rb
76
77
  - lib/activerecord_redshift_adapter/version.rb
78
+ - lib/monkeypatch_activerecord.rb
79
+ - lib/monkeypatch_arel.rb
77
80
  - spec/active_record/base_spec.rb
78
81
  - spec/active_record/connection_adapters/redshift_adapter_spec.rb
79
82
  - spec/dummy/config/database.example.yml
@@ -99,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
99
102
  version: '0'
100
103
  requirements: []
101
104
  rubyforge_project:
102
- rubygems_version: 1.8.25
105
+ rubygems_version: 1.8.24
103
106
  signing_key:
104
107
  specification_version: 3
105
108
  summary: Rails 3 database adapter support for AWS RedShift.