sequel 4.8.0 → 4.9.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/CHANGELOG +48 -0
- data/doc/association_basics.rdoc +1 -1
- data/doc/opening_databases.rdoc +4 -0
- data/doc/postgresql.rdoc +27 -3
- data/doc/release_notes/4.9.0.txt +190 -0
- data/doc/security.rdoc +1 -1
- data/doc/testing.rdoc +2 -2
- data/doc/validations.rdoc +8 -0
- data/lib/sequel/adapters/jdbc.rb +5 -3
- data/lib/sequel/adapters/jdbc/derby.rb +2 -8
- data/lib/sequel/adapters/jdbc/h2.rb +2 -13
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -16
- data/lib/sequel/adapters/mysql2.rb +11 -1
- data/lib/sequel/adapters/postgres.rb +33 -10
- data/lib/sequel/adapters/shared/db2.rb +2 -10
- data/lib/sequel/adapters/shared/mssql.rb +10 -8
- data/lib/sequel/adapters/shared/oracle.rb +9 -24
- data/lib/sequel/adapters/shared/postgres.rb +32 -9
- data/lib/sequel/adapters/shared/sqlanywhere.rb +2 -4
- data/lib/sequel/adapters/shared/sqlite.rb +4 -7
- data/lib/sequel/database/schema_methods.rb +15 -0
- data/lib/sequel/dataset.rb +1 -1
- data/lib/sequel/dataset/actions.rb +159 -27
- data/lib/sequel/dataset/graph.rb +29 -7
- data/lib/sequel/dataset/misc.rb +6 -0
- data/lib/sequel/dataset/placeholder_literalizer.rb +164 -0
- data/lib/sequel/dataset/query.rb +2 -0
- data/lib/sequel/dataset/sql.rb +103 -91
- data/lib/sequel/extensions/current_datetime_timestamp.rb +57 -0
- data/lib/sequel/extensions/pg_array.rb +68 -106
- data/lib/sequel/extensions/pg_hstore.rb +5 -5
- data/lib/sequel/extensions/schema_dumper.rb +49 -49
- data/lib/sequel/model.rb +4 -2
- data/lib/sequel/model/associations.rb +1 -1
- data/lib/sequel/model/base.rb +136 -3
- data/lib/sequel/model/errors.rb +6 -0
- data/lib/sequel/plugins/defaults_setter.rb +1 -1
- data/lib/sequel/plugins/eager_each.rb +9 -0
- data/lib/sequel/plugins/nested_attributes.rb +2 -2
- data/lib/sequel/plugins/timestamps.rb +2 -2
- data/lib/sequel/plugins/touch.rb +2 -2
- data/lib/sequel/sql.rb +20 -15
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +70 -8
- data/spec/core/dataset_spec.rb +172 -27
- data/spec/core/expression_filters_spec.rb +3 -3
- data/spec/core/object_graph_spec.rb +17 -1
- data/spec/core/placeholder_literalizer_spec.rb +128 -0
- data/spec/core/schema_spec.rb +54 -0
- data/spec/extensions/current_datetime_timestamp_spec.rb +27 -0
- data/spec/extensions/defaults_setter_spec.rb +12 -0
- data/spec/extensions/eager_each_spec.rb +6 -0
- data/spec/extensions/nested_attributes_spec.rb +14 -2
- data/spec/extensions/pg_array_spec.rb +15 -7
- data/spec/extensions/shared_caching_spec.rb +5 -5
- data/spec/extensions/timestamps_spec.rb +9 -0
- data/spec/extensions/touch_spec.rb +9 -0
- data/spec/integration/database_test.rb +1 -1
- data/spec/integration/dataset_test.rb +27 -5
- data/spec/model/eager_loading_spec.rb +32 -0
- data/spec/model/model_spec.rb +119 -9
- metadata +8 -2
@@ -0,0 +1,57 @@
|
|
1
|
+
# The current_datetime_timestamp extension makes Dataset#current_datetime
|
2
|
+
# return an object that operates like Sequel.datetime_class.now, but will
|
3
|
+
# be literalized as CURRENT_TIMESTAMP.
|
4
|
+
#
|
5
|
+
# This allows you to use the defaults_setter, timestamps, and touch
|
6
|
+
# model plugins and make sure that CURRENT_TIMESTAMP is used instead of
|
7
|
+
# a literalized timestamp value.
|
8
|
+
#
|
9
|
+
# The reason that CURRENT_TIMESTAMP is better than a literalized version
|
10
|
+
# of the timestamp is that it obeys correct transactional semantics
|
11
|
+
# (all calls to CURRENT_TIMESTAMP in the same transaction return the
|
12
|
+
# same timestamp, at least on some databases).
|
13
|
+
#
|
14
|
+
# To have current_datetime be literalized as CURRENT_TIMESTAMP for
|
15
|
+
# a single dataset:
|
16
|
+
#
|
17
|
+
# ds = ds.extension(:current_datetime_timestamp)
|
18
|
+
#
|
19
|
+
# To have current_datetime be literalized as CURRENT_TIMESTAMP for all
|
20
|
+
# datasets of a given database.
|
21
|
+
#
|
22
|
+
# DB.extension(:current_datetime_timestamp)
|
23
|
+
|
24
|
+
module Sequel
|
25
|
+
module CurrentDateTimeTimestamp
|
26
|
+
module DatasetMethods
|
27
|
+
# Return an instance of Sequel.datetime_class that will be literalized
|
28
|
+
# as CURRENT_TIMESTAMP.
|
29
|
+
def current_datetime
|
30
|
+
MAP.fetch(Sequel.datetime_class).now
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# Literalize custom DateTime subclass objects as CURRENT_TIMESTAMP.
|
36
|
+
def literal_datetime_append(sql, v)
|
37
|
+
v.is_a?(DateTime) ? literal_append(sql, Sequel::CURRENT_TIMESTAMP) : super
|
38
|
+
end
|
39
|
+
|
40
|
+
# Literalize custom Time subclass objects as CURRENT_TIMESTAMP.
|
41
|
+
def literal_time_append(sql, v)
|
42
|
+
v.is_a?(Time) ? literal_append(sql, Sequel::CURRENT_TIMESTAMP) : super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Time subclass literalized as CURRENT_TIMESTAMP
|
47
|
+
class Time < ::Time; end
|
48
|
+
|
49
|
+
# DateTime subclass literalized as CURRENT_TIMESTAMP
|
50
|
+
class DateTime < ::DateTime; end
|
51
|
+
|
52
|
+
# Mapping of Time/DateTime classes to subclasses literalized as CURRENT_TIMESTAMP
|
53
|
+
MAP = {::Time=>Time, ::DateTime=>DateTime}
|
54
|
+
end
|
55
|
+
|
56
|
+
Dataset.register_extension(:current_datetime_timestamp, CurrentDateTimeTimestamp::DatasetMethods)
|
57
|
+
end
|
@@ -23,7 +23,7 @@
|
|
23
23
|
#
|
24
24
|
# array.pg_array
|
25
25
|
#
|
26
|
-
# You can also provide a type, though
|
26
|
+
# You can also provide a type, though in many cases it isn't necessary:
|
27
27
|
#
|
28
28
|
# Sequel.pg_array(array, :varchar) # or :integer, :"double precision", etc.
|
29
29
|
# array.pg_array(:varchar) # or :integer, :"double precision", etc.
|
@@ -73,35 +73,10 @@
|
|
73
73
|
# If you want an easy way to call PostgreSQL array functions and
|
74
74
|
# operators, look into the pg_array_ops extension.
|
75
75
|
#
|
76
|
-
# This extension requires
|
77
|
-
#
|
78
|
-
# == Additional License
|
79
|
-
#
|
80
|
-
# PGArray::Parser code was translated from Javascript code in the
|
81
|
-
# node-postgres project and has the following additional license:
|
82
|
-
#
|
83
|
-
# Copyright (c) 2010 Brian Carlson (brian.m.carlson@gmail.com)
|
84
|
-
#
|
85
|
-
# Permission is hereby granted, free of charge, to any person obtaining
|
86
|
-
# a copy of this software and associated documentation files (the
|
87
|
-
# "Software"), to deal in the Software without restriction, including
|
88
|
-
# without limitation the rights to use, copy, modify, merge, publish,
|
89
|
-
# distribute, sublicense, and/or sell copies of the Software, and to
|
90
|
-
# permit persons to whom the Software is furnished to do so, subject
|
91
|
-
# to the following conditions:
|
92
|
-
#
|
93
|
-
# The above copyright notice and this permission notice shall be included
|
94
|
-
# in all copies or substantial portions of the Software.
|
95
|
-
#
|
96
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
97
|
-
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
98
|
-
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
99
|
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
100
|
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
101
|
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
102
|
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
76
|
+
# This extension requires the json, strscan, and delegate libraries.
|
103
77
|
|
104
78
|
require 'delegate'
|
79
|
+
require 'strscan'
|
105
80
|
require 'json'
|
106
81
|
Sequel.require 'adapters/utils/pg_types'
|
107
82
|
|
@@ -159,10 +134,6 @@ module Sequel
|
|
159
134
|
# :type_symbol :: The base of the schema type symbol for this type. For example, if you provide
|
160
135
|
# :integer, Sequel will recognize this type as :integer_array during schema parsing.
|
161
136
|
# Defaults to the db_type argument.
|
162
|
-
# :typecast_method :: If given, specifies the :type_symbol option, but additionally causes no
|
163
|
-
# typecasting method to be created in the database. This should only be used
|
164
|
-
# to alias existing array types. For example, if there is an array type that can be
|
165
|
-
# treated just like an integer array, you can do :typecast_method=>:integer.
|
166
137
|
# :typecast_method_map :: The map in which to place the database type string to type symbol mapping.
|
167
138
|
# Defaults to ARRAY_TYPES.
|
168
139
|
# :typecast_methods_module :: If given, a module object to add the typecasting method to. Defaults
|
@@ -171,8 +142,7 @@ module Sequel
|
|
171
142
|
# If a block is given, it is treated as the :converter option.
|
172
143
|
def self.register(db_type, opts=OPTS, &block)
|
173
144
|
db_type = db_type.to_s
|
174
|
-
|
175
|
-
type = (typecast_method || opts[:type_symbol] || db_type).to_sym
|
145
|
+
type = (opts[:type_symbol] || db_type).to_sym
|
176
146
|
type_procs = opts[:type_procs] || PG_TYPES
|
177
147
|
mod = opts[:typecast_methods_module] || DatabaseMethods
|
178
148
|
typecast_method_map = opts[:typecast_method_map] || ARRAY_TYPES
|
@@ -193,7 +163,7 @@ module Sequel
|
|
193
163
|
|
194
164
|
typecast_method_map[db_type] = :"#{type}_array"
|
195
165
|
|
196
|
-
define_array_typecast_method(mod, type, creator, opts.fetch(:scalar_typecast, type))
|
166
|
+
define_array_typecast_method(mod, type, creator, opts.fetch(:scalar_typecast, type))
|
197
167
|
|
198
168
|
if oid = opts[:oid]
|
199
169
|
type_procs[oid] = creator
|
@@ -261,7 +231,7 @@ module Sequel
|
|
261
231
|
opts[:oid] = array_oid unless opts.has_key?(:oid)
|
262
232
|
end
|
263
233
|
PGArray.register(db_type, opts, &block)
|
264
|
-
@schema_type_classes[:"#{opts[:
|
234
|
+
@schema_type_classes[:"#{opts[:type_symbol] || db_type}_array"] = PGArray
|
265
235
|
end
|
266
236
|
|
267
237
|
# Return PGArray if this type matches any supported array type.
|
@@ -334,7 +304,6 @@ module Sequel
|
|
334
304
|
# typecast all members of the array in ruby for performance reasons, but
|
335
305
|
# it will cast the array the appropriate database type when the array is
|
336
306
|
# literalized.
|
337
|
-
# * If given a String, call the parser for the subclass with it.
|
338
307
|
def typecast_value_pg_array(value, creator, scalar_typecast_method=nil)
|
339
308
|
case value
|
340
309
|
when PGArray
|
@@ -354,45 +323,26 @@ module Sequel
|
|
354
323
|
end
|
355
324
|
end
|
356
325
|
|
357
|
-
# PostgreSQL array parser that handles
|
358
|
-
#
|
359
|
-
#
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
326
|
+
# PostgreSQL array parser that handles PostgreSQL array output format.
|
327
|
+
# Note that does not handle all forms out input that PostgreSQL will
|
328
|
+
# accept, and it will not raise an error for all forms of invalid input.
|
329
|
+
class Parser < StringScanner
|
330
|
+
UNQUOTED_RE = /[{}",]|[^{}",]+/
|
331
|
+
QUOTED_RE = /["\\]|[^"\\]+/
|
332
|
+
NULL_RE = /NULL",/
|
333
|
+
OPEN_RE = /\{/
|
364
334
|
|
365
335
|
# Set the source for the input, and any converter callable
|
366
336
|
# to call with objects to be created. For nested parsers
|
367
337
|
# the source may contain text after the end current parse,
|
368
338
|
# which will be ignored.
|
369
339
|
def initialize(source, converter=nil)
|
370
|
-
|
371
|
-
@source_length = source.length
|
340
|
+
super(source)
|
372
341
|
@converter = converter
|
373
|
-
@
|
374
|
-
@entries = []
|
342
|
+
@stack = [[]]
|
375
343
|
@recorded = ""
|
376
|
-
@dimension = 0
|
377
|
-
end
|
378
|
-
|
379
|
-
# Return 2 objects, whether the next character in the input
|
380
|
-
# was escaped with a backslash, and what the next character is.
|
381
|
-
def next_char
|
382
|
-
@pos += 1
|
383
|
-
if (c = @source[@pos..@pos]) == BACKSLASH
|
384
|
-
@pos += 1
|
385
|
-
[true, @source[@pos..@pos]]
|
386
|
-
else
|
387
|
-
[false, c]
|
388
|
-
end
|
389
344
|
end
|
390
345
|
|
391
|
-
# Add a new character to the buffer of recorded characters.
|
392
|
-
def record(c)
|
393
|
-
@recorded << c
|
394
|
-
end
|
395
|
-
|
396
346
|
# Take the buffer of recorded characters and add it to the array
|
397
347
|
# of entries, and use a new buffer for recorded characters.
|
398
348
|
def new_entry(include_empty=false)
|
@@ -403,53 +353,65 @@ module Sequel
|
|
403
353
|
elsif @converter
|
404
354
|
entry = @converter.call(entry)
|
405
355
|
end
|
406
|
-
@
|
356
|
+
@stack.last.push(entry)
|
407
357
|
@recorded = ""
|
408
358
|
end
|
409
359
|
end
|
410
360
|
|
411
361
|
# Parse the input character by character, returning an array
|
412
362
|
# of parsed (and potentially converted) objects.
|
413
|
-
def parse
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
363
|
+
def parse
|
364
|
+
raise Sequel::Error, "invalid array, empty string" if eos?
|
365
|
+
raise Sequel::Error, "invalid array, doesn't start with {" unless scan(OPEN_RE)
|
366
|
+
|
367
|
+
while !eos?
|
368
|
+
char = scan(UNQUOTED_RE)
|
369
|
+
if char == COMMA
|
370
|
+
# Comma outside quoted string indicates end of current entry
|
371
|
+
new_entry
|
372
|
+
elsif char == QUOTE
|
373
|
+
raise Sequel::Error, "invalid array, opening quote with existing recorded data" unless @recorded.empty?
|
374
|
+
while true
|
375
|
+
char = scan(QUOTED_RE)
|
376
|
+
if char == BACKSLASH
|
377
|
+
@recorded << getch
|
378
|
+
elsif char == QUOTE
|
379
|
+
n = peek(1)
|
380
|
+
raise Sequel::Error, "invalid array, closing quote not followed by comma or closing brace" unless n == COMMA || n == CLOSE_BRACE
|
381
|
+
break
|
382
|
+
else
|
383
|
+
@recorded << char
|
384
|
+
end
|
435
385
|
end
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
386
|
+
new_entry(true)
|
387
|
+
elsif char == OPEN_BRACE
|
388
|
+
raise Sequel::Error, "invalid array, opening brace with existing recorded data" unless @recorded.empty?
|
389
|
+
|
390
|
+
# Start of new array, add it to the stack
|
391
|
+
new = []
|
392
|
+
@stack.last << new
|
393
|
+
@stack << new
|
394
|
+
elsif char == CLOSE_BRACE
|
395
|
+
# End of current array, add current entry to the current array
|
445
396
|
new_entry
|
397
|
+
|
398
|
+
if @stack.length == 1
|
399
|
+
raise Sequel::Error, "array parsing finished without parsing entire string" unless eos?
|
400
|
+
|
401
|
+
# Top level of array, parsing should be over.
|
402
|
+
# Pop current array off stack and return it as result
|
403
|
+
return @stack.pop
|
404
|
+
else
|
405
|
+
# Nested array, pop current array off stack
|
406
|
+
@stack.pop
|
407
|
+
end
|
446
408
|
else
|
447
409
|
# Add the character to the recorded character buffer.
|
448
|
-
|
410
|
+
@recorded << char
|
449
411
|
end
|
450
412
|
end
|
451
|
-
|
452
|
-
|
413
|
+
|
414
|
+
raise Sequel::Error, "array parsing finished with array unclosed"
|
453
415
|
end
|
454
416
|
end unless Sequel::Postgres.respond_to?(:parse_pg_array)
|
455
417
|
|
@@ -571,11 +533,11 @@ module Sequel
|
|
571
533
|
register('time with time zone', :oid=>1270, :scalar_oid=>1083, :type_symbol=>:time_timezone, :scalar_typecast=>:time)
|
572
534
|
register('timestamp with time zone', :oid=>1185, :scalar_oid=>1184, :type_symbol=>:datetime_timezone, :scalar_typecast=>:datetime)
|
573
535
|
|
574
|
-
register('smallint', :oid=>1005, :parser=>:json, :
|
575
|
-
register('oid', :oid=>1028, :parser=>:json, :
|
576
|
-
register('real', :oid=>1021, :scalar_oid=>
|
577
|
-
register('character', :oid=>1014, :array_type=>:text, :
|
578
|
-
register('character varying', :oid=>1015, :
|
536
|
+
register('smallint', :oid=>1005, :parser=>:json, :scalar_typecast=>:integer)
|
537
|
+
register('oid', :oid=>1028, :parser=>:json, :scalar_typecast=>:integer)
|
538
|
+
register('real', :oid=>1021, :scalar_oid=>700, :scalar_typecast=>:float)
|
539
|
+
register('character', :oid=>1014, :array_type=>:text, :scalar_typecast=>:string)
|
540
|
+
register('character varying', :oid=>1015, :scalar_typecast=>:string, :type_symbol=>:varchar)
|
579
541
|
end
|
580
542
|
end
|
581
543
|
|
@@ -28,16 +28,16 @@
|
|
28
28
|
# Since the hstore type only supports strings, non string keys and
|
29
29
|
# values are converted to strings
|
30
30
|
#
|
31
|
-
#
|
32
|
-
# v = {}
|
31
|
+
# Sequel.hstore(:foo=>1).to_hash # {'foo'=>'1'}
|
32
|
+
# v = Sequel.hstore({})
|
33
33
|
# v[:foo] = 1
|
34
34
|
# v # {'foo'=>'1'}
|
35
35
|
#
|
36
36
|
# However, to make life easier, lookups by key are converted to
|
37
37
|
# strings (even when accessing the underlying hash directly):
|
38
38
|
#
|
39
|
-
#
|
40
|
-
#
|
39
|
+
# Sequel.hstore('foo'=>'bar')[:foo] # 'bar'
|
40
|
+
# Sequel.hstore('foo'=>'bar').to_hash[:foo] # 'bar'
|
41
41
|
#
|
42
42
|
# HStore instances mostly just delegate to the underlying hash
|
43
43
|
# instance, so Hash methods that modify the receiver or returned
|
@@ -66,7 +66,7 @@
|
|
66
66
|
#
|
67
67
|
# If you want to insert a hash into an hstore database column:
|
68
68
|
#
|
69
|
-
# DB[:table].insert(:column=>
|
69
|
+
# DB[:table].insert(:column=>Sequel.hstore('foo'=>'bar'))
|
70
70
|
#
|
71
71
|
# If you would like to use hstore columns in your model objects, you
|
72
72
|
# probably want to modify the schema parsing/typecasting so that it
|
@@ -12,6 +12,55 @@ Sequel.extension :eval_inspect
|
|
12
12
|
|
13
13
|
module Sequel
|
14
14
|
module SchemaDumper
|
15
|
+
# Convert the column schema information to a hash of column options, one of which must
|
16
|
+
# be :type. The other options added should modify that type (e.g. :size). If a
|
17
|
+
# database type is not recognized, return it as a String type.
|
18
|
+
def column_schema_to_ruby_type(schema)
|
19
|
+
case schema[:db_type].downcase
|
20
|
+
when /\A(medium|small)?int(?:eger)?(?:\((\d+)\))?( unsigned)?\z/o
|
21
|
+
if !$1 && $2 && $2.to_i >= 10 && $3
|
22
|
+
# Unsigned integer type with 10 digits can potentially contain values which
|
23
|
+
# don't fit signed integer type, so use bigint type in target database.
|
24
|
+
{:type=>Bignum}
|
25
|
+
else
|
26
|
+
{:type=>Integer}
|
27
|
+
end
|
28
|
+
when /\Atinyint(?:\((\d+)\))?(?: unsigned)?\z/o
|
29
|
+
{:type =>schema[:type] == :boolean ? TrueClass : Integer}
|
30
|
+
when /\Abigint(?:\((?:\d+)\))?(?: unsigned)?\z/o
|
31
|
+
{:type=>Bignum}
|
32
|
+
when /\A(?:real|float|double(?: precision)?|double\(\d+,\d+\)(?: unsigned)?)\z/o
|
33
|
+
{:type=>Float}
|
34
|
+
when 'boolean'
|
35
|
+
{:type=>TrueClass}
|
36
|
+
when /\A(?:(?:tiny|medium|long|n)?text|clob)\z/o
|
37
|
+
{:type=>String, :text=>true}
|
38
|
+
when 'date'
|
39
|
+
{:type=>Date}
|
40
|
+
when /\A(?:small)?datetime\z/o
|
41
|
+
{:type=>DateTime}
|
42
|
+
when /\Atimestamp(?:\((\d+)\))?(?: with(?:out)? time zone)?\z/o
|
43
|
+
{:type=>DateTime, :size=>($1.to_i if $1)}
|
44
|
+
when /\Atime(?: with(?:out)? time zone)?\z/o
|
45
|
+
{:type=>Time, :only_time=>true}
|
46
|
+
when /\An?char(?:acter)?(?:\((\d+)\))?\z/o
|
47
|
+
{:type=>String, :size=>($1.to_i if $1), :fixed=>true}
|
48
|
+
when /\A(?:n?varchar|character varying|bpchar|string)(?:\((\d+)\))?\z/o
|
49
|
+
{:type=>String, :size=>($1.to_i if $1)}
|
50
|
+
when /\A(?:small)?money\z/o
|
51
|
+
{:type=>BigDecimal, :size=>[19,2]}
|
52
|
+
when /\A(?:decimal|numeric|number)(?:\((\d+)(?:,\s*(\d+))?\))?\z/o
|
53
|
+
s = [($1.to_i if $1), ($2.to_i if $2)].compact
|
54
|
+
{:type=>BigDecimal, :size=>(s.empty? ? nil : s)}
|
55
|
+
when /\A(?:bytea|(?:tiny|medium|long)?blob|(?:var)?binary)(?:\((\d+)\))?\z/o
|
56
|
+
{:type=>File, :size=>($1.to_i if $1)}
|
57
|
+
when /\A(?:year|(?:int )?identity)\z/o
|
58
|
+
{:type=>Integer}
|
59
|
+
else
|
60
|
+
{:type=>String}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
15
64
|
# Dump foreign key constraints for all tables as a migration. This complements
|
16
65
|
# the :foreign_keys=>false option to dump_schema_migration. This only dumps
|
17
66
|
# the constraints (not the columns) using alter_table/add_foreign_key with an
|
@@ -145,55 +194,6 @@ END_MIG
|
|
145
194
|
end
|
146
195
|
end
|
147
196
|
|
148
|
-
# Convert the column schema information to a hash of column options, one of which must
|
149
|
-
# be :type. The other options added should modify that type (e.g. :size). If a
|
150
|
-
# database type is not recognized, return it as a String type.
|
151
|
-
def column_schema_to_ruby_type(schema)
|
152
|
-
case schema[:db_type].downcase
|
153
|
-
when /\A(medium|small)?int(?:eger)?(?:\((\d+)\))?( unsigned)?\z/o
|
154
|
-
if !$1 && $2 && $2.to_i >= 10 && $3
|
155
|
-
# Unsigned integer type with 10 digits can potentially contain values which
|
156
|
-
# don't fit signed integer type, so use bigint type in target database.
|
157
|
-
{:type=>Bignum}
|
158
|
-
else
|
159
|
-
{:type=>Integer}
|
160
|
-
end
|
161
|
-
when /\Atinyint(?:\((\d+)\))?(?: unsigned)?\z/o
|
162
|
-
{:type =>schema[:type] == :boolean ? TrueClass : Integer}
|
163
|
-
when /\Abigint(?:\((?:\d+)\))?(?: unsigned)?\z/o
|
164
|
-
{:type=>Bignum}
|
165
|
-
when /\A(?:real|float|double(?: precision)?|double\(\d+,\d+\)(?: unsigned)?)\z/o
|
166
|
-
{:type=>Float}
|
167
|
-
when 'boolean'
|
168
|
-
{:type=>TrueClass}
|
169
|
-
when /\A(?:(?:tiny|medium|long|n)?text|clob)\z/o
|
170
|
-
{:type=>String, :text=>true}
|
171
|
-
when 'date'
|
172
|
-
{:type=>Date}
|
173
|
-
when /\A(?:small)?datetime\z/o
|
174
|
-
{:type=>DateTime}
|
175
|
-
when /\Atimestamp(?:\((\d+)\))?(?: with(?:out)? time zone)?\z/o
|
176
|
-
{:type=>DateTime, :size=>($1.to_i if $1)}
|
177
|
-
when /\Atime(?: with(?:out)? time zone)?\z/o
|
178
|
-
{:type=>Time, :only_time=>true}
|
179
|
-
when /\An?char(?:acter)?(?:\((\d+)\))?\z/o
|
180
|
-
{:type=>String, :size=>($1.to_i if $1), :fixed=>true}
|
181
|
-
when /\A(?:n?varchar|character varying|bpchar|string)(?:\((\d+)\))?\z/o
|
182
|
-
{:type=>String, :size=>($1.to_i if $1)}
|
183
|
-
when /\A(?:small)?money\z/o
|
184
|
-
{:type=>BigDecimal, :size=>[19,2]}
|
185
|
-
when /\A(?:decimal|numeric|number)(?:\((\d+)(?:,\s*(\d+))?\))?\z/o
|
186
|
-
s = [($1.to_i if $1), ($2.to_i if $2)].compact
|
187
|
-
{:type=>BigDecimal, :size=>(s.empty? ? nil : s)}
|
188
|
-
when /\A(?:bytea|(?:tiny|medium|long)?blob|(?:var)?binary)(?:\((\d+)\))?\z/o
|
189
|
-
{:type=>File, :size=>($1.to_i if $1)}
|
190
|
-
when /\A(?:year|(?:int )?identity)\z/o
|
191
|
-
{:type=>Integer}
|
192
|
-
else
|
193
|
-
{:type=>String}
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
197
|
# For the table and foreign key metadata array, return an alter_table
|
198
198
|
# string that would add the foreign keys if run in a migration.
|
199
199
|
def dump_add_fk_constraints(table, fks)
|