activerecord4-redshift-adapter 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|