activerecord4-redshift-adapter 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/active_record/connection_adapters/redshift/array_parser.rb +35 -39
- data/lib/active_record/connection_adapters/redshift/column.rb +10 -0
- data/lib/active_record/connection_adapters/redshift/database_statements.rb +37 -47
- data/lib/active_record/connection_adapters/redshift/oid.rb +14 -359
- data/lib/active_record/connection_adapters/redshift/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/redshift/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/redshift/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/redshift/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/redshift/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/redshift/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/redshift/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/redshift/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/redshift/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/redshift/oid/type_map_initializer.rb +63 -0
- data/lib/active_record/connection_adapters/redshift/quoting.rb +45 -119
- data/lib/active_record/connection_adapters/redshift/referential_integrity.rb +4 -19
- data/lib/active_record/connection_adapters/redshift/schema_definitions.rb +73 -0
- data/lib/active_record/connection_adapters/redshift/schema_statements.rb +141 -76
- data/lib/active_record/connection_adapters/redshift/utils.rb +77 -0
- data/lib/active_record/connection_adapters/redshift_adapter.rb +252 -496
- metadata +17 -11
- data/lib/active_record/connection_adapters/redshift/cast.rb +0 -156
@@ -0,0 +1,77 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module Redshift
|
4
|
+
# Value Object to hold a schema qualified name.
|
5
|
+
# This is usually the name of a PostgreSQL relation but it can also represent
|
6
|
+
# schema qualified type names. +schema+ and +identifier+ are unquoted to prevent
|
7
|
+
# double quoting.
|
8
|
+
class Name # :nodoc:
|
9
|
+
SEPARATOR = "."
|
10
|
+
attr_reader :schema, :identifier
|
11
|
+
|
12
|
+
def initialize(schema, identifier)
|
13
|
+
@schema, @identifier = unquote(schema), unquote(identifier)
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
parts.join SEPARATOR
|
18
|
+
end
|
19
|
+
|
20
|
+
def quoted
|
21
|
+
if schema
|
22
|
+
PGconn.quote_ident(schema) << SEPARATOR << PGconn.quote_ident(identifier)
|
23
|
+
else
|
24
|
+
PGconn.quote_ident(identifier)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def ==(o)
|
29
|
+
o.class == self.class && o.parts == parts
|
30
|
+
end
|
31
|
+
alias_method :eql?, :==
|
32
|
+
|
33
|
+
def hash
|
34
|
+
parts.hash
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
def unquote(part)
|
39
|
+
if part && part.start_with?('"')
|
40
|
+
part[1..-2]
|
41
|
+
else
|
42
|
+
part
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def parts
|
47
|
+
@parts ||= [@schema, @identifier].compact
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
module Utils # :nodoc:
|
52
|
+
extend self
|
53
|
+
|
54
|
+
# Returns an instance of <tt>ActiveRecord::ConnectionAdapters::PostgreSQL::Name</tt>
|
55
|
+
# extracted from +string+.
|
56
|
+
# +schema+ is nil if not specified in +string+.
|
57
|
+
# +schema+ and +identifier+ exclude surrounding quotes (regardless of whether provided in +string+)
|
58
|
+
# +string+ supports the range of schema/table references understood by PostgreSQL, for example:
|
59
|
+
#
|
60
|
+
# * <tt>table_name</tt>
|
61
|
+
# * <tt>"table.name"</tt>
|
62
|
+
# * <tt>schema_name.table_name</tt>
|
63
|
+
# * <tt>schema_name."table.name"</tt>
|
64
|
+
# * <tt>"schema_name".table_name</tt>
|
65
|
+
# * <tt>"schema.name"."table name"</tt>
|
66
|
+
def extract_schema_qualified_name(string)
|
67
|
+
schema, table = string.scan(/[^".\s]+|"[^"]*"/)
|
68
|
+
if table.nil?
|
69
|
+
table = schema
|
70
|
+
schema = nil
|
71
|
+
end
|
72
|
+
Redshift::Name.new(schema, table)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -1,16 +1,19 @@
|
|
1
1
|
require 'active_record/connection_adapters/abstract_adapter'
|
2
2
|
require 'active_record/connection_adapters/statement_pool'
|
3
|
+
|
4
|
+
require 'active_record/connection_adapters/redshift/utils'
|
5
|
+
require 'active_record/connection_adapters/redshift/column'
|
3
6
|
require 'active_record/connection_adapters/redshift/oid'
|
4
|
-
require 'active_record/connection_adapters/redshift/cast'
|
5
|
-
require 'active_record/connection_adapters/redshift/array_parser'
|
6
7
|
require 'active_record/connection_adapters/redshift/quoting'
|
8
|
+
require 'active_record/connection_adapters/redshift/referential_integrity'
|
9
|
+
require 'active_record/connection_adapters/redshift/schema_definitions'
|
7
10
|
require 'active_record/connection_adapters/redshift/schema_statements'
|
8
11
|
require 'active_record/connection_adapters/redshift/database_statements'
|
9
|
-
|
12
|
+
|
10
13
|
require 'arel/visitors/bind_visitor'
|
11
14
|
|
12
15
|
# Make sure we're using pg high enough for PGResult#values
|
13
|
-
gem 'pg', '
|
16
|
+
gem 'pg', '> 0.15'
|
14
17
|
require 'pg'
|
15
18
|
|
16
19
|
require 'ipaddr'
|
@@ -20,8 +23,8 @@ module ActiveRecord
|
|
20
23
|
RS_VALID_CONN_PARAMS = [:host, :hostaddr, :port, :dbname, :user, :password, :connect_timeout,
|
21
24
|
:client_encoding, :options, :application_name, :fallback_application_name,
|
22
25
|
:keepalives, :keepalives_idle, :keepalives_interval, :keepalives_count,
|
23
|
-
:tty, :sslmode, :requiressl, :
|
24
|
-
:requirepeer, :krbsrvname, :gsslib, :service]
|
26
|
+
:tty, :sslmode, :requiressl, :sslcompression, :sslcert, :sslkey,
|
27
|
+
:sslrootcert, :sslcrl, :requirepeer, :krbsrvname, :gsslib, :service]
|
25
28
|
|
26
29
|
# Establishes a connection to the database that's used by all Active Record objects
|
27
30
|
def redshift_connection(config)
|
@@ -43,194 +46,6 @@ module ActiveRecord
|
|
43
46
|
end
|
44
47
|
|
45
48
|
module ConnectionAdapters
|
46
|
-
# PostgreSQL-specific extensions to column definitions in a table.
|
47
|
-
class RedshiftColumn < Column #:nodoc:
|
48
|
-
attr_accessor :array
|
49
|
-
# Instantiates a new PostgreSQL column definition in a table.
|
50
|
-
def initialize(name, default, oid_type, sql_type = nil, null = true)
|
51
|
-
@oid_type = oid_type
|
52
|
-
if sql_type =~ /\[\]$/
|
53
|
-
@array = true
|
54
|
-
super(name, self.class.extract_value_from_default(default), sql_type[0..sql_type.length - 3], null)
|
55
|
-
else
|
56
|
-
@array = false
|
57
|
-
super(name, self.class.extract_value_from_default(default), sql_type, null)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
# :stopdoc:
|
62
|
-
class << self
|
63
|
-
include ConnectionAdapters::RedshiftColumn::Cast
|
64
|
-
include ConnectionAdapters::RedshiftColumn::ArrayParser
|
65
|
-
attr_accessor :money_precision
|
66
|
-
end
|
67
|
-
# :startdoc:
|
68
|
-
|
69
|
-
# Extracts the value from a PostgreSQL column default definition.
|
70
|
-
def self.extract_value_from_default(default)
|
71
|
-
# This is a performance optimization for Ruby 1.9.2 in development.
|
72
|
-
# If the value is nil, we return nil straight away without checking
|
73
|
-
# the regular expressions. If we check each regular expression,
|
74
|
-
# Regexp#=== will call NilClass#to_str, which will trigger
|
75
|
-
# method_missing (defined by whiny nil in ActiveSupport) which
|
76
|
-
# makes this method very very slow.
|
77
|
-
return default unless default
|
78
|
-
|
79
|
-
case default
|
80
|
-
when /\A'(.*)'::(num|date|tstz|ts|int4|int8)range\z/m
|
81
|
-
$1
|
82
|
-
# Numeric types
|
83
|
-
when /\A\(?(-?\d+(\.\d*)?\)?)\z/
|
84
|
-
$1
|
85
|
-
# Character types
|
86
|
-
when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
|
87
|
-
$1
|
88
|
-
# Binary data types
|
89
|
-
when /\A'(.*)'::bytea\z/m
|
90
|
-
$1
|
91
|
-
# Date/time types
|
92
|
-
when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
|
93
|
-
$1
|
94
|
-
when /\A'(.*)'::interval\z/
|
95
|
-
$1
|
96
|
-
# Boolean type
|
97
|
-
when 'true'
|
98
|
-
true
|
99
|
-
when 'false'
|
100
|
-
false
|
101
|
-
# Geometric types
|
102
|
-
when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
|
103
|
-
$1
|
104
|
-
# Network address types
|
105
|
-
when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
|
106
|
-
$1
|
107
|
-
# Bit string types
|
108
|
-
when /\AB'(.*)'::"?bit(?: varying)?"?\z/
|
109
|
-
$1
|
110
|
-
# XML type
|
111
|
-
when /\A'(.*)'::xml\z/m
|
112
|
-
$1
|
113
|
-
# Arrays
|
114
|
-
when /\A'(.*)'::"?\D+"?\[\]\z/
|
115
|
-
$1
|
116
|
-
# Hstore
|
117
|
-
when /\A'(.*)'::hstore\z/
|
118
|
-
$1
|
119
|
-
# JSON
|
120
|
-
when /\A'(.*)'::json\z/
|
121
|
-
$1
|
122
|
-
# Object identifier types
|
123
|
-
when /\A-?\d+\z/
|
124
|
-
$1
|
125
|
-
else
|
126
|
-
# Anything else is blank, some user type, or some function
|
127
|
-
# and we can't know the value of that, so return nil.
|
128
|
-
nil
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
def type_cast(value)
|
133
|
-
return if value.nil?
|
134
|
-
return super if encoded?
|
135
|
-
|
136
|
-
@oid_type.type_cast value
|
137
|
-
end
|
138
|
-
|
139
|
-
private
|
140
|
-
|
141
|
-
def extract_limit(sql_type)
|
142
|
-
case sql_type
|
143
|
-
when /^bigint/i; 8
|
144
|
-
when /^smallint/i; 2
|
145
|
-
when /^timestamp/i; nil
|
146
|
-
else super
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
# Extracts the scale from PostgreSQL-specific data types.
|
151
|
-
def extract_scale(sql_type)
|
152
|
-
# Money type has a fixed scale of 2.
|
153
|
-
sql_type =~ /^money/ ? 2 : super
|
154
|
-
end
|
155
|
-
|
156
|
-
# Extracts the precision from PostgreSQL-specific data types.
|
157
|
-
def extract_precision(sql_type)
|
158
|
-
if sql_type == 'money'
|
159
|
-
self.class.money_precision
|
160
|
-
elsif sql_type =~ /timestamp/i
|
161
|
-
$1.to_i if sql_type =~ /\((\d+)\)/
|
162
|
-
else
|
163
|
-
super
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
# Maps PostgreSQL-specific data types to logical Rails types.
|
168
|
-
def simplified_type(field_type)
|
169
|
-
case field_type
|
170
|
-
# Numeric and monetary types
|
171
|
-
when /^(?:real|double precision)$/
|
172
|
-
:float
|
173
|
-
# Monetary types
|
174
|
-
when 'money'
|
175
|
-
:decimal
|
176
|
-
when 'hstore'
|
177
|
-
:hstore
|
178
|
-
when 'ltree'
|
179
|
-
:ltree
|
180
|
-
# Network address types
|
181
|
-
when 'inet'
|
182
|
-
:inet
|
183
|
-
when 'cidr'
|
184
|
-
:cidr
|
185
|
-
when 'macaddr'
|
186
|
-
:macaddr
|
187
|
-
# Character types
|
188
|
-
when /^(?:character varying|bpchar)(?:\(\d+\))?$/
|
189
|
-
:string
|
190
|
-
# Binary data types
|
191
|
-
when 'bytea'
|
192
|
-
:binary
|
193
|
-
# Date/time types
|
194
|
-
when /^timestamp with(?:out)? time zone$/
|
195
|
-
:datetime
|
196
|
-
when /^interval(?:|\(\d+\))$/
|
197
|
-
:string
|
198
|
-
# Geometric types
|
199
|
-
when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/
|
200
|
-
:string
|
201
|
-
# Bit strings
|
202
|
-
when /^bit(?: varying)?(?:\(\d+\))?$/
|
203
|
-
:string
|
204
|
-
# XML type
|
205
|
-
when 'xml'
|
206
|
-
:xml
|
207
|
-
# tsvector type
|
208
|
-
when 'tsvector'
|
209
|
-
:tsvector
|
210
|
-
# Arrays
|
211
|
-
when /^\D+\[\]$/
|
212
|
-
:string
|
213
|
-
# Object identifier types
|
214
|
-
when 'oid'
|
215
|
-
:integer
|
216
|
-
# UUID type
|
217
|
-
when 'uuid'
|
218
|
-
:uuid
|
219
|
-
# JSON type
|
220
|
-
when 'json'
|
221
|
-
:json
|
222
|
-
# Small and big integer types
|
223
|
-
when /^(?:small|big)int$/
|
224
|
-
:integer
|
225
|
-
when /(num|date|tstz|ts|int4|int8)range$/
|
226
|
-
field_type.to_sym
|
227
|
-
# Pass through all types that are not specific to PostgreSQL.
|
228
|
-
else
|
229
|
-
super
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
49
|
# The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
|
235
50
|
#
|
236
51
|
# Options:
|
@@ -249,7 +64,7 @@ module ActiveRecord
|
|
249
64
|
# <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
|
250
65
|
# * <tt>:variables</tt> - An optional hash of additional parameters that
|
251
66
|
# will be used in <tt>SET SESSION key = val</tt> calls on the connection.
|
252
|
-
# * <tt>:insert_returning</tt> -
|
67
|
+
# * <tt>:insert_returning</tt> - Does nothing for Redshift.
|
253
68
|
#
|
254
69
|
# Any further options are used as connection parameters to libpq. See
|
255
70
|
# http://www.postgresql.org/docs/9.1/static/libpq-connect.html for the
|
@@ -258,187 +73,44 @@ module ActiveRecord
|
|
258
73
|
# In addition, default connection parameters of libpq can be set per environment variables.
|
259
74
|
# See http://www.postgresql.org/docs/9.1/static/libpq-envars.html .
|
260
75
|
class RedshiftAdapter < AbstractAdapter
|
261
|
-
|
262
|
-
attr_accessor :array
|
263
|
-
end
|
264
|
-
|
265
|
-
module ColumnMethods
|
266
|
-
def xml(*args)
|
267
|
-
options = args.extract_options!
|
268
|
-
column(args[0], 'xml', options)
|
269
|
-
end
|
270
|
-
|
271
|
-
def tsvector(*args)
|
272
|
-
options = args.extract_options!
|
273
|
-
column(args[0], 'tsvector', options)
|
274
|
-
end
|
275
|
-
|
276
|
-
def int4range(name, options = {})
|
277
|
-
column(name, 'int4range', options)
|
278
|
-
end
|
279
|
-
|
280
|
-
def int8range(name, options = {})
|
281
|
-
column(name, 'int8range', options)
|
282
|
-
end
|
283
|
-
|
284
|
-
def tsrange(name, options = {})
|
285
|
-
column(name, 'tsrange', options)
|
286
|
-
end
|
287
|
-
|
288
|
-
def tstzrange(name, options = {})
|
289
|
-
column(name, 'tstzrange', options)
|
290
|
-
end
|
291
|
-
|
292
|
-
def numrange(name, options = {})
|
293
|
-
column(name, 'numrange', options)
|
294
|
-
end
|
295
|
-
|
296
|
-
def daterange(name, options = {})
|
297
|
-
column(name, 'daterange', options)
|
298
|
-
end
|
299
|
-
|
300
|
-
def hstore(name, options = {})
|
301
|
-
column(name, 'hstore', options)
|
302
|
-
end
|
303
|
-
|
304
|
-
def ltree(name, options = {})
|
305
|
-
column(name, 'ltree', options)
|
306
|
-
end
|
307
|
-
|
308
|
-
def inet(name, options = {})
|
309
|
-
column(name, 'inet', options)
|
310
|
-
end
|
311
|
-
|
312
|
-
def cidr(name, options = {})
|
313
|
-
column(name, 'cidr', options)
|
314
|
-
end
|
315
|
-
|
316
|
-
def macaddr(name, options = {})
|
317
|
-
column(name, 'macaddr', options)
|
318
|
-
end
|
319
|
-
|
320
|
-
def uuid(name, options = {})
|
321
|
-
column(name, 'uuid', options)
|
322
|
-
end
|
323
|
-
|
324
|
-
def json(name, options = {})
|
325
|
-
column(name, 'json', options)
|
326
|
-
end
|
327
|
-
end
|
328
|
-
|
329
|
-
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
|
330
|
-
include ColumnMethods
|
331
|
-
|
332
|
-
# Defines the primary key field.
|
333
|
-
# Use of the native PostgreSQL UUID type is supported, and can be used
|
334
|
-
# by defining your tables as such:
|
335
|
-
#
|
336
|
-
# create_table :stuffs, id: :uuid do |t|
|
337
|
-
# t.string :content
|
338
|
-
# t.timestamps
|
339
|
-
# end
|
340
|
-
#
|
341
|
-
# By default, this will use the +uuid_generate_v4()+ function from the
|
342
|
-
# +uuid-ossp+ extension, which MUST be enabled on your databse. To enable
|
343
|
-
# the +uuid-ossp+ extension, you can use the +enable_extension+ method in your
|
344
|
-
# migrations To use a UUID primary key without +uuid-ossp+ enabled, you can
|
345
|
-
# set the +:default+ option to nil:
|
346
|
-
#
|
347
|
-
# create_table :stuffs, id: false do |t|
|
348
|
-
# t.primary_key :id, :uuid, default: nil
|
349
|
-
# t.uuid :foo_id
|
350
|
-
# t.timestamps
|
351
|
-
# end
|
352
|
-
#
|
353
|
-
# You may also pass a different UUID generation function from +uuid-ossp+
|
354
|
-
# or another library.
|
355
|
-
#
|
356
|
-
# Note that setting the UUID primary key default value to +nil+
|
357
|
-
# will require you to assure that you always provide a UUID value
|
358
|
-
# before saving a record (as primary keys cannot be nil). This might be
|
359
|
-
# done via the SecureRandom.uuid method and a +before_save+ callback,
|
360
|
-
# for instance.
|
361
|
-
def primary_key(name, type = :primary_key, options = {})
|
362
|
-
return super unless type == :uuid
|
363
|
-
options[:default] = options.fetch(:default, 'uuid_generate_v4()')
|
364
|
-
options[:primary_key] = true
|
365
|
-
column name, type, options
|
366
|
-
end
|
367
|
-
|
368
|
-
def column(name, type = nil, options = {})
|
369
|
-
super
|
370
|
-
column = self[name]
|
371
|
-
column.array = options[:array]
|
372
|
-
|
373
|
-
self
|
374
|
-
end
|
375
|
-
|
376
|
-
def xml(options = {})
|
377
|
-
column(args[0], :text, options)
|
378
|
-
end
|
379
|
-
|
380
|
-
private
|
381
|
-
|
382
|
-
def create_column_definition(name, type)
|
383
|
-
ColumnDefinition.new name, type
|
384
|
-
end
|
385
|
-
end
|
386
|
-
|
387
|
-
class Table < ActiveRecord::ConnectionAdapters::Table
|
388
|
-
include ColumnMethods
|
389
|
-
end
|
390
|
-
|
391
|
-
ADAPTER_NAME = 'Redshift'
|
76
|
+
ADAPTER_NAME = 'Redshift'.freeze
|
392
77
|
|
393
78
|
NATIVE_DATABASE_TYPES = {
|
394
|
-
primary_key: "
|
395
|
-
string: { name: "character varying"
|
79
|
+
primary_key: "integer primary key",
|
80
|
+
string: { name: "character varying" },
|
396
81
|
text: { name: "text" },
|
397
82
|
integer: { name: "integer" },
|
398
83
|
float: { name: "float" },
|
399
84
|
decimal: { name: "decimal" },
|
400
85
|
datetime: { name: "timestamp" },
|
401
|
-
timestamp: { name: "timestamp" },
|
402
86
|
time: { name: "time" },
|
403
87
|
date: { name: "date" },
|
404
|
-
|
405
|
-
numrange: { name: "numrange" },
|
406
|
-
tsrange: { name: "tsrange" },
|
407
|
-
tstzrange: { name: "tstzrange" },
|
408
|
-
int4range: { name: "int4range" },
|
409
|
-
int8range: { name: "int8range" },
|
410
|
-
binary: { name: "bytea" },
|
411
|
-
boolean: { name: "boolean" },
|
412
|
-
xml: { name: "xml" },
|
413
|
-
tsvector: { name: "tsvector" },
|
414
|
-
hstore: { name: "hstore" },
|
415
|
-
inet: { name: "inet" },
|
416
|
-
cidr: { name: "cidr" },
|
417
|
-
macaddr: { name: "macaddr" },
|
418
|
-
uuid: { name: "uuid" },
|
88
|
+
bigint: { name: "bigint" },
|
419
89
|
json: { name: "json" },
|
420
|
-
|
90
|
+
jsonb: { name: "jsonb" }
|
421
91
|
}
|
422
92
|
|
423
|
-
|
424
|
-
|
425
|
-
include
|
426
|
-
include
|
93
|
+
OID = Redshift::OID #:nodoc:
|
94
|
+
|
95
|
+
include Redshift::Quoting
|
96
|
+
include Redshift::ReferentialIntegrity
|
97
|
+
include Redshift::SchemaStatements
|
98
|
+
include Redshift::DatabaseStatements
|
427
99
|
|
428
|
-
|
429
|
-
|
430
|
-
ADAPTER_NAME
|
100
|
+
def schema_creation # :nodoc:
|
101
|
+
Redshift::SchemaCreation.new self
|
431
102
|
end
|
432
103
|
|
433
|
-
# Adds
|
104
|
+
# Adds +:array+ option to the default set provided by the
|
434
105
|
# AbstractAdapter
|
435
|
-
def prepare_column_options(column, types)
|
106
|
+
def prepare_column_options(column, types) # :nodoc:
|
436
107
|
spec = super
|
437
108
|
spec[:array] = 'true' if column.respond_to?(:array) && column.array
|
109
|
+
spec[:default] = "\"#{column.default_function}\"" if column.default_function
|
438
110
|
spec
|
439
111
|
end
|
440
112
|
|
441
|
-
# Adds
|
113
|
+
# Adds +:array+ as a valid migration key
|
442
114
|
def migration_keys
|
443
115
|
super + [:array]
|
444
116
|
end
|
@@ -450,14 +122,22 @@ module ActiveRecord
|
|
450
122
|
end
|
451
123
|
|
452
124
|
def supports_index_sort_order?
|
453
|
-
|
125
|
+
false
|
454
126
|
end
|
455
127
|
|
456
128
|
def supports_partial_index?
|
457
|
-
|
129
|
+
false
|
458
130
|
end
|
459
131
|
|
460
132
|
def supports_transaction_isolation?
|
133
|
+
false
|
134
|
+
end
|
135
|
+
|
136
|
+
def supports_foreign_keys?
|
137
|
+
true
|
138
|
+
end
|
139
|
+
|
140
|
+
def supports_views?
|
461
141
|
true
|
462
142
|
end
|
463
143
|
|
@@ -518,21 +198,13 @@ module ActiveRecord
|
|
518
198
|
end
|
519
199
|
end
|
520
200
|
|
521
|
-
class BindSubstitution < Arel::Visitors::PostgreSQL # :nodoc:
|
522
|
-
include Arel::Visitors::BindVisitor
|
523
|
-
end
|
524
|
-
|
525
201
|
# Initializes and connects a PostgreSQL adapter.
|
526
202
|
def initialize(connection, logger, connection_parameters, config)
|
527
203
|
super(connection, logger)
|
528
204
|
|
529
|
-
|
530
|
-
|
531
|
-
else
|
532
|
-
@visitor = unprepared_visitor
|
533
|
-
end
|
205
|
+
@visitor = Arel::Visitors::PostgreSQL.new self
|
206
|
+
@prepared_statements = false
|
534
207
|
|
535
|
-
connection_parameters.delete :prepared_statements
|
536
208
|
@connection_parameters, @config = connection_parameters, config
|
537
209
|
|
538
210
|
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
|
@@ -543,7 +215,8 @@ module ActiveRecord
|
|
543
215
|
@statements = StatementPool.new @connection,
|
544
216
|
self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 })
|
545
217
|
|
546
|
-
|
218
|
+
@type_map = Type::HashLookupTypeMap.new
|
219
|
+
initialize_type_map(type_map)
|
547
220
|
@local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
|
548
221
|
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : false
|
549
222
|
end
|
@@ -553,9 +226,14 @@ module ActiveRecord
|
|
553
226
|
@statements.clear
|
554
227
|
end
|
555
228
|
|
229
|
+
def truncate(table_name, name = nil)
|
230
|
+
exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
|
231
|
+
end
|
232
|
+
|
556
233
|
# Is this connection alive and ready for queries?
|
557
234
|
def active?
|
558
|
-
@connection.
|
235
|
+
@connection.query 'SELECT 1'
|
236
|
+
true
|
559
237
|
rescue PGError
|
560
238
|
false
|
561
239
|
end
|
@@ -569,7 +247,12 @@ module ActiveRecord
|
|
569
247
|
|
570
248
|
def reset!
|
571
249
|
clear_cache!
|
572
|
-
|
250
|
+
reset_transaction
|
251
|
+
unless @connection.transaction_status == ::PG::PQTRANS_IDLE
|
252
|
+
@connection.query 'ROLLBACK'
|
253
|
+
end
|
254
|
+
@connection.query 'DISCARD ALL'
|
255
|
+
configure_connection
|
573
256
|
end
|
574
257
|
|
575
258
|
# Disconnects from the database if already connected. Otherwise, this
|
@@ -593,38 +276,38 @@ module ActiveRecord
|
|
593
276
|
true
|
594
277
|
end
|
595
278
|
|
596
|
-
|
597
|
-
|
279
|
+
# Enable standard-conforming strings if available.
|
280
|
+
def set_standard_conforming_strings
|
281
|
+
old, self.client_min_messages = client_min_messages, 'panic'
|
282
|
+
execute('SET standard_conforming_strings = on', 'SCHEMA') rescue nil
|
283
|
+
ensure
|
284
|
+
self.client_min_messages = old
|
598
285
|
end
|
599
286
|
|
600
287
|
def supports_ddl_transactions?
|
601
288
|
true
|
602
289
|
end
|
603
290
|
|
604
|
-
# Returns true, since this connection adapter supports savepoints.
|
605
|
-
def supports_savepoints?
|
606
|
-
false
|
607
|
-
end
|
608
|
-
|
609
|
-
def supports_import?
|
610
|
-
true
|
611
|
-
end
|
612
|
-
|
613
|
-
# Returns true.
|
614
291
|
def supports_explain?
|
615
292
|
true
|
616
293
|
end
|
617
294
|
|
618
|
-
# Returns true if pg > 9.2
|
619
295
|
def supports_extensions?
|
620
296
|
false
|
621
297
|
end
|
622
298
|
|
623
|
-
# Range datatypes weren't introduced until PostgreSQL 9.2
|
624
299
|
def supports_ranges?
|
625
300
|
false
|
626
301
|
end
|
627
302
|
|
303
|
+
def supports_materialized_views?
|
304
|
+
false
|
305
|
+
end
|
306
|
+
|
307
|
+
def supports_import?
|
308
|
+
true
|
309
|
+
end
|
310
|
+
|
628
311
|
def enable_extension(name)
|
629
312
|
end
|
630
313
|
|
@@ -635,9 +318,6 @@ module ActiveRecord
|
|
635
318
|
false
|
636
319
|
end
|
637
320
|
|
638
|
-
#def extensions
|
639
|
-
#end
|
640
|
-
|
641
321
|
# Returns the configured supported identifier length supported by PostgreSQL
|
642
322
|
def table_alias_length
|
643
323
|
@table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
|
@@ -649,25 +329,6 @@ module ActiveRecord
|
|
649
329
|
exec_query "SET SESSION AUTHORIZATION #{user}"
|
650
330
|
end
|
651
331
|
|
652
|
-
module Utils
|
653
|
-
extend self
|
654
|
-
|
655
|
-
# Returns an array of <tt>[schema_name, table_name]</tt> extracted from +name+.
|
656
|
-
# +schema_name+ is nil if not specified in +name+.
|
657
|
-
# +schema_name+ and +table_name+ exclude surrounding quotes (regardless of whether provided in +name+)
|
658
|
-
# +name+ supports the range of schema/table references understood by PostgreSQL, for example:
|
659
|
-
#
|
660
|
-
# * <tt>table_name</tt>
|
661
|
-
# * <tt>"table.name"</tt>
|
662
|
-
# * <tt>schema_name.table_name</tt>
|
663
|
-
# * <tt>schema_name."table.name"</tt>
|
664
|
-
# * <tt>"schema.name"."table name"</tt>
|
665
|
-
def extract_schema_and_table(name)
|
666
|
-
table, schema = name.scan(/[^".\s]+|"[^"]*"/)[0..1].collect{|m| m.gsub(/(^"|"$)/,'') }.reverse
|
667
|
-
[schema, table]
|
668
|
-
end
|
669
|
-
end
|
670
|
-
|
671
332
|
def use_insert_returning?
|
672
333
|
false
|
673
334
|
end
|
@@ -676,17 +337,32 @@ module ActiveRecord
|
|
676
337
|
!native_database_types[type].nil?
|
677
338
|
end
|
678
339
|
|
340
|
+
def update_table_definition(table_name, base) #:nodoc:
|
341
|
+
Redshift::Table.new(table_name, base)
|
342
|
+
end
|
343
|
+
|
344
|
+
def lookup_cast_type(sql_type) # :nodoc:
|
345
|
+
oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").first['oid'].to_i
|
346
|
+
super(oid)
|
347
|
+
end
|
348
|
+
|
349
|
+
def column_name_for_operation(operation, node) # :nodoc:
|
350
|
+
OPERATION_ALIASES.fetch(operation) { operation.downcase }
|
351
|
+
end
|
352
|
+
|
353
|
+
OPERATION_ALIASES = { # :nodoc:
|
354
|
+
"maximum" => "max",
|
355
|
+
"minimum" => "min",
|
356
|
+
"average" => "avg",
|
357
|
+
}
|
358
|
+
|
679
359
|
protected
|
680
360
|
|
681
361
|
# Returns the version of the connected PostgreSQL server.
|
682
|
-
def
|
362
|
+
def redshift_version
|
683
363
|
@connection.server_version
|
684
364
|
end
|
685
365
|
|
686
|
-
# See http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html
|
687
|
-
FOREIGN_KEY_VIOLATION = "23503"
|
688
|
-
UNIQUE_VIOLATION = "23505"
|
689
|
-
|
690
366
|
def translate_exception(exception, message)
|
691
367
|
return exception unless exception.respond_to?(:result)
|
692
368
|
|
@@ -702,64 +378,170 @@ module ActiveRecord
|
|
702
378
|
|
703
379
|
private
|
704
380
|
|
705
|
-
def
|
706
|
-
|
707
|
-
|
708
|
-
|
381
|
+
def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
|
382
|
+
if !type_map.key?(oid)
|
383
|
+
load_additional_types(type_map, [oid])
|
384
|
+
end
|
709
385
|
|
710
|
-
|
711
|
-
|
712
|
-
|
386
|
+
type_map.fetch(oid, fmod, sql_type) {
|
387
|
+
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
388
|
+
Type::Value.new.tap do |cast_type|
|
389
|
+
type_map.register_type(oid, cast_type)
|
390
|
+
end
|
391
|
+
}
|
392
|
+
end
|
393
|
+
|
394
|
+
def initialize_type_map(m) # :nodoc:
|
395
|
+
register_class_with_limit m, 'int2', OID::Integer
|
396
|
+
register_class_with_limit m, 'int4', OID::Integer
|
397
|
+
register_class_with_limit m, 'int8', OID::Integer
|
398
|
+
m.alias_type 'oid', 'int2'
|
399
|
+
m.register_type 'float4', OID::Float.new
|
400
|
+
m.alias_type 'float8', 'float4'
|
401
|
+
m.register_type 'text', Type::Text.new
|
402
|
+
register_class_with_limit m, 'varchar', Type::String
|
403
|
+
m.alias_type 'char', 'varchar'
|
404
|
+
m.alias_type 'name', 'varchar'
|
405
|
+
m.alias_type 'bpchar', 'varchar'
|
406
|
+
m.alias_type 'timestamptz', 'timestamp'
|
407
|
+
m.register_type 'date', OID::Date.new
|
408
|
+
m.register_type 'time', OID::Time.new
|
409
|
+
|
410
|
+
m.register_type 'json', OID::Json.new
|
411
|
+
m.register_type 'jsonb', OID::Jsonb.new
|
412
|
+
|
413
|
+
# FIXME: why are we keeping these types as strings?
|
414
|
+
m.alias_type 'interval', 'varchar'
|
415
|
+
|
416
|
+
m.register_type 'timestamp' do |_, _, sql_type|
|
417
|
+
precision = extract_precision(sql_type)
|
418
|
+
OID::DateTime.new(precision: precision)
|
419
|
+
end
|
713
420
|
|
714
|
-
|
715
|
-
|
716
|
-
|
421
|
+
m.register_type 'numeric' do |_, fmod, sql_type|
|
422
|
+
precision = extract_precision(sql_type)
|
423
|
+
scale = extract_scale(sql_type)
|
424
|
+
|
425
|
+
# The type for the numeric depends on the width of the field,
|
426
|
+
# so we'll do something special here.
|
427
|
+
#
|
428
|
+
# When dealing with decimal columns:
|
429
|
+
#
|
430
|
+
# places after decimal = fmod - 4 & 0xffff
|
431
|
+
# places before decimal = (fmod - 4) >> 16 & 0xffff
|
432
|
+
if fmod && (fmod - 4 & 0xffff).zero?
|
433
|
+
# FIXME: Remove this class, and the second argument to
|
434
|
+
# lookups on PG
|
435
|
+
Type::DecimalWithoutScale.new(precision: precision)
|
436
|
+
else
|
437
|
+
OID::Decimal.new(precision: precision, scale: scale)
|
438
|
+
end
|
717
439
|
end
|
718
440
|
|
719
|
-
|
441
|
+
load_additional_types(m)
|
442
|
+
end
|
720
443
|
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
444
|
+
def extract_limit(sql_type) # :nodoc:
|
445
|
+
case sql_type
|
446
|
+
when /^bigint/i, /^int8/i
|
447
|
+
8
|
448
|
+
when /^smallint/i
|
449
|
+
2
|
450
|
+
else
|
451
|
+
super
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
# Extracts the value from a PostgreSQL column default definition.
|
456
|
+
def extract_value_from_default(oid, default) # :nodoc:
|
457
|
+
case default
|
458
|
+
# Quoted types
|
459
|
+
when /\A[\(B]?'(.*)'::/m
|
460
|
+
$1.gsub(/''/, "'")
|
461
|
+
# Boolean types
|
462
|
+
when 'true', 'false'
|
463
|
+
default
|
464
|
+
# Numeric types
|
465
|
+
when /\A\(?(-?\d+(\.\d*)?)\)?\z/
|
466
|
+
$1
|
467
|
+
# Object identifier types
|
468
|
+
when /\A-?\d+\z/
|
469
|
+
$1
|
726
470
|
else
|
727
|
-
#
|
728
|
-
|
729
|
-
|
471
|
+
# Anything else is blank, some user type, or some function
|
472
|
+
# and we can't know the value of that, so return nil.
|
473
|
+
nil
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
def extract_default_function(default_value, default) # :nodoc:
|
478
|
+
default if has_default_function?(default_value, default)
|
479
|
+
end
|
480
|
+
|
481
|
+
def has_default_function?(default_value, default) # :nodoc:
|
482
|
+
!default_value && (%r{\w+\(.*\)} === default)
|
483
|
+
end
|
484
|
+
|
485
|
+
def load_additional_types(type_map, oids = nil) # :nodoc:
|
486
|
+
initializer = OID::TypeMapInitializer.new(type_map)
|
487
|
+
|
488
|
+
if supports_ranges?
|
489
|
+
query = <<-SQL
|
490
|
+
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
|
491
|
+
FROM pg_type as t
|
492
|
+
LEFT JOIN pg_range as r ON oid = rngtypid
|
493
|
+
SQL
|
494
|
+
else
|
495
|
+
query = <<-SQL
|
496
|
+
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
|
497
|
+
FROM pg_type as t
|
498
|
+
SQL
|
499
|
+
end
|
730
500
|
|
731
|
-
|
501
|
+
if oids
|
502
|
+
query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
|
732
503
|
end
|
733
504
|
|
734
|
-
|
735
|
-
|
736
|
-
array = OID::Array.new OID::TYPE_MAP[row['typelem'].to_i]
|
737
|
-
OID::TYPE_MAP[row['oid'].to_i] = array
|
505
|
+
execute_and_clear(query, 'SCHEMA', []) do |records|
|
506
|
+
initializer.run(records)
|
738
507
|
end
|
739
508
|
end
|
740
509
|
|
741
|
-
FEATURE_NOT_SUPPORTED = "0A000"
|
510
|
+
FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
|
511
|
+
|
512
|
+
def execute_and_clear(sql, name, binds)
|
513
|
+
result = without_prepared_statement?(binds) ? exec_no_cache(sql, name, binds) :
|
514
|
+
exec_cache(sql, name, binds)
|
515
|
+
ret = yield result
|
516
|
+
result.clear
|
517
|
+
ret
|
518
|
+
end
|
742
519
|
|
743
|
-
def exec_no_cache(sql, binds)
|
744
|
-
@connection.async_exec(sql, [])
|
520
|
+
def exec_no_cache(sql, name, binds)
|
521
|
+
log(sql, name, binds) { @connection.async_exec(sql, []) }
|
745
522
|
end
|
746
523
|
|
747
|
-
def exec_cache(sql, binds)
|
748
|
-
stmt_key = prepare_statement
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
524
|
+
def exec_cache(sql, name, binds)
|
525
|
+
stmt_key = prepare_statement(sql)
|
526
|
+
type_casted_binds = binds.map { |col, val|
|
527
|
+
[col, type_cast(val, col)]
|
528
|
+
}
|
529
|
+
|
530
|
+
log(sql, name, type_casted_binds, stmt_key) do
|
531
|
+
@connection.exec_prepared(stmt_key, type_casted_binds.map { |_, val| val })
|
532
|
+
end
|
533
|
+
rescue ActiveRecord::StatementInvalid => e
|
534
|
+
pgerror = e.original_exception
|
535
|
+
|
758
536
|
# Get the PG code for the failure. Annoyingly, the code for
|
759
537
|
# prepared statements whose return value may have changed is
|
760
538
|
# FEATURE_NOT_SUPPORTED. Check here for more details:
|
761
539
|
# http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
|
762
|
-
|
540
|
+
begin
|
541
|
+
code = pgerror.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
|
542
|
+
rescue
|
543
|
+
raise e
|
544
|
+
end
|
763
545
|
if FEATURE_NOT_SUPPORTED == code
|
764
546
|
@statements.delete sql_key(sql)
|
765
547
|
retry
|
@@ -780,28 +562,30 @@ module ActiveRecord
|
|
780
562
|
sql_key = sql_key(sql)
|
781
563
|
unless @statements.key? sql_key
|
782
564
|
nextkey = @statements.next_key
|
783
|
-
|
565
|
+
begin
|
566
|
+
@connection.prepare nextkey, sql
|
567
|
+
rescue => e
|
568
|
+
raise translate_exception_class(e, sql)
|
569
|
+
end
|
570
|
+
# Clear the queue
|
571
|
+
@connection.get_last_result
|
784
572
|
@statements[sql_key] = nextkey
|
785
573
|
end
|
786
574
|
@statements[sql_key]
|
787
575
|
end
|
788
576
|
|
789
|
-
# The internal PostgreSQL identifier of the money data type.
|
790
|
-
MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
|
791
|
-
# The internal PostgreSQL identifier of the BYTEA data type.
|
792
|
-
BYTEA_COLUMN_TYPE_OID = 17 #:nodoc:
|
793
|
-
|
794
577
|
# Connects to a PostgreSQL server and sets up the adapter depending on the
|
795
578
|
# connected server's characteristics.
|
796
579
|
def connect
|
797
580
|
@connection = PGconn.connect(@connection_parameters)
|
798
581
|
|
799
|
-
# Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
|
800
|
-
# PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
|
801
|
-
# should know about this but can't detect it there, so deal with it here.
|
802
|
-
RedshiftColumn.money_precision = (postgresql_version >= 80300) ? 19 : 10
|
803
|
-
|
804
582
|
configure_connection
|
583
|
+
rescue ::PG::Error => error
|
584
|
+
if error.message.include?("does not exist")
|
585
|
+
raise ActiveRecord::NoDatabaseError.new(error.message, error)
|
586
|
+
else
|
587
|
+
raise
|
588
|
+
end
|
805
589
|
end
|
806
590
|
|
807
591
|
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
@@ -818,9 +602,9 @@ module ActiveRecord
|
|
818
602
|
variables.map do |k, v|
|
819
603
|
if v == ':default' || v == :default
|
820
604
|
# Sets the value to the global or compile default
|
821
|
-
execute("SET SESSION #{k
|
605
|
+
execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA')
|
822
606
|
elsif !v.nil?
|
823
|
-
execute("SET SESSION #{k
|
607
|
+
execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA')
|
824
608
|
end
|
825
609
|
end
|
826
610
|
end
|
@@ -838,20 +622,6 @@ module ActiveRecord
|
|
838
622
|
exec_query("SELECT currval('#{sequence_name}')", 'SQL')
|
839
623
|
end
|
840
624
|
|
841
|
-
# Executes a SELECT query and returns the results, performing any data type
|
842
|
-
# conversions that are required to be performed here instead of in PostgreSQLColumn.
|
843
|
-
def select(sql, name = nil, binds = [])
|
844
|
-
exec_query(sql, name, binds)
|
845
|
-
end
|
846
|
-
|
847
|
-
def select_raw(sql, name = nil)
|
848
|
-
res = execute(sql, name)
|
849
|
-
results = result_as_array(res)
|
850
|
-
fields = res.fields
|
851
|
-
res.clear
|
852
|
-
return fields, results
|
853
|
-
end
|
854
|
-
|
855
625
|
# Returns the list of a table's column names, data types, and default values.
|
856
626
|
#
|
857
627
|
# The underlying query is roughly:
|
@@ -870,7 +640,7 @@ module ActiveRecord
|
|
870
640
|
# Query implementation notes:
|
871
641
|
# - format_type includes the column size constraint, e.g. varchar(50)
|
872
642
|
# - ::regclass is a function that gives the id for a table name
|
873
|
-
def column_definitions(table_name)
|
643
|
+
def column_definitions(table_name) # :nodoc:
|
874
644
|
exec_query(<<-end_sql, 'SCHEMA').rows
|
875
645
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
876
646
|
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
|
@@ -882,27 +652,13 @@ module ActiveRecord
|
|
882
652
|
end_sql
|
883
653
|
end
|
884
654
|
|
885
|
-
def
|
886
|
-
|
887
|
-
|
888
|
-
if match_data
|
889
|
-
rest = name[match_data[0].length, name.length]
|
890
|
-
rest = rest[1, rest.length] if rest.start_with? "."
|
891
|
-
[match_data[1], (rest.length > 0 ? rest : nil)]
|
892
|
-
end
|
893
|
-
end
|
894
|
-
|
895
|
-
def extract_table_ref_from_insert_sql(sql)
|
896
|
-
sql[/into\s+([^\(]*).*values\s*\(/i]
|
655
|
+
def extract_table_ref_from_insert_sql(sql) # :nodoc:
|
656
|
+
sql[/into\s+([^\(]*).*values\s*\(/im]
|
897
657
|
$1.strip if $1
|
898
658
|
end
|
899
659
|
|
900
|
-
def create_table_definition(name, temporary, options, as = nil)
|
901
|
-
TableDefinition.new native_database_types, name, temporary, options
|
902
|
-
end
|
903
|
-
|
904
|
-
def update_table_definition(table_name, base)
|
905
|
-
Table.new(table_name, base)
|
660
|
+
def create_table_definition(name, temporary, options, as = nil) # :nodoc:
|
661
|
+
Redshift::TableDefinition.new native_database_types, name, temporary, options, as
|
906
662
|
end
|
907
663
|
end
|
908
664
|
end
|