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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +48 -0
  3. data/doc/association_basics.rdoc +1 -1
  4. data/doc/opening_databases.rdoc +4 -0
  5. data/doc/postgresql.rdoc +27 -3
  6. data/doc/release_notes/4.9.0.txt +190 -0
  7. data/doc/security.rdoc +1 -1
  8. data/doc/testing.rdoc +2 -2
  9. data/doc/validations.rdoc +8 -0
  10. data/lib/sequel/adapters/jdbc.rb +5 -3
  11. data/lib/sequel/adapters/jdbc/derby.rb +2 -8
  12. data/lib/sequel/adapters/jdbc/h2.rb +2 -13
  13. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -16
  14. data/lib/sequel/adapters/mysql2.rb +11 -1
  15. data/lib/sequel/adapters/postgres.rb +33 -10
  16. data/lib/sequel/adapters/shared/db2.rb +2 -10
  17. data/lib/sequel/adapters/shared/mssql.rb +10 -8
  18. data/lib/sequel/adapters/shared/oracle.rb +9 -24
  19. data/lib/sequel/adapters/shared/postgres.rb +32 -9
  20. data/lib/sequel/adapters/shared/sqlanywhere.rb +2 -4
  21. data/lib/sequel/adapters/shared/sqlite.rb +4 -7
  22. data/lib/sequel/database/schema_methods.rb +15 -0
  23. data/lib/sequel/dataset.rb +1 -1
  24. data/lib/sequel/dataset/actions.rb +159 -27
  25. data/lib/sequel/dataset/graph.rb +29 -7
  26. data/lib/sequel/dataset/misc.rb +6 -0
  27. data/lib/sequel/dataset/placeholder_literalizer.rb +164 -0
  28. data/lib/sequel/dataset/query.rb +2 -0
  29. data/lib/sequel/dataset/sql.rb +103 -91
  30. data/lib/sequel/extensions/current_datetime_timestamp.rb +57 -0
  31. data/lib/sequel/extensions/pg_array.rb +68 -106
  32. data/lib/sequel/extensions/pg_hstore.rb +5 -5
  33. data/lib/sequel/extensions/schema_dumper.rb +49 -49
  34. data/lib/sequel/model.rb +4 -2
  35. data/lib/sequel/model/associations.rb +1 -1
  36. data/lib/sequel/model/base.rb +136 -3
  37. data/lib/sequel/model/errors.rb +6 -0
  38. data/lib/sequel/plugins/defaults_setter.rb +1 -1
  39. data/lib/sequel/plugins/eager_each.rb +9 -0
  40. data/lib/sequel/plugins/nested_attributes.rb +2 -2
  41. data/lib/sequel/plugins/timestamps.rb +2 -2
  42. data/lib/sequel/plugins/touch.rb +2 -2
  43. data/lib/sequel/sql.rb +20 -15
  44. data/lib/sequel/version.rb +1 -1
  45. data/spec/adapters/postgres_spec.rb +70 -8
  46. data/spec/core/dataset_spec.rb +172 -27
  47. data/spec/core/expression_filters_spec.rb +3 -3
  48. data/spec/core/object_graph_spec.rb +17 -1
  49. data/spec/core/placeholder_literalizer_spec.rb +128 -0
  50. data/spec/core/schema_spec.rb +54 -0
  51. data/spec/extensions/current_datetime_timestamp_spec.rb +27 -0
  52. data/spec/extensions/defaults_setter_spec.rb +12 -0
  53. data/spec/extensions/eager_each_spec.rb +6 -0
  54. data/spec/extensions/nested_attributes_spec.rb +14 -2
  55. data/spec/extensions/pg_array_spec.rb +15 -7
  56. data/spec/extensions/shared_caching_spec.rb +5 -5
  57. data/spec/extensions/timestamps_spec.rb +9 -0
  58. data/spec/extensions/touch_spec.rb +9 -0
  59. data/spec/integration/database_test.rb +1 -1
  60. data/spec/integration/dataset_test.rb +27 -5
  61. data/spec/model/eager_loading_spec.rb +32 -0
  62. data/spec/model/model_spec.rb +119 -9
  63. 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 it many cases it isn't necessary:
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 both the json and delegate libraries.
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
- typecast_method = opts[:typecast_method]
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)) unless typecast_method
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[:typecast_method] || opts[:type_symbol] || db_type}_array"] = PGArray
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 all types of input.
358
- #
359
- # This parser is very simple and unoptimized, but should still
360
- # be O(n) where n is the length of the input string.
361
- class Parser
362
- # Current position in the input string.
363
- attr_reader :pos
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
- @source = source
371
- @source_length = source.length
340
+ super(source)
372
341
  @converter = converter
373
- @pos = -1
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
- @entries.push(entry)
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(nested=false)
414
- # quote sets whether we are inside of a quoted string.
415
- quote = false
416
- until @pos >= @source_length
417
- escaped, char = next_char
418
- if char == OPEN_BRACE && !quote
419
- @dimension += 1
420
- if (@dimension > 1)
421
- # Multi-dimensional array encounter, use a subparser
422
- # to parse the next level down.
423
- subparser = self.class.new(@source[@pos..-1], @converter)
424
- @entries.push(subparser.parse(true))
425
- @pos += subparser.pos - 1
426
- end
427
- elsif char == CLOSE_BRACE && !quote
428
- @dimension -= 1
429
- if (@dimension == 0)
430
- new_entry
431
- # Exit early if inside a subparser, since the
432
- # text after parsing the current level should be
433
- # ignored as it is handled by the parent parser.
434
- return @entries if nested
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
- elsif char == QUOTE && !escaped
437
- # If already inside the quoted string, this is the
438
- # ending quote, so add the entry. Otherwise, this
439
- # is the opening quote, so set the quote flag.
440
- new_entry(true) if quote
441
- quote = !quote
442
- elsif char == COMMA && !quote
443
- # If not inside a string and a comma occurs, it indicates
444
- # the end of the entry, so add the entry.
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
- record(char)
410
+ @recorded << char
449
411
  end
450
412
  end
451
- raise Sequel::Error, "array dimensions not balanced" unless @dimension == 0
452
- @entries
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, :typecast_method=>:integer)
575
- register('oid', :oid=>1028, :parser=>:json, :typecast_method=>:integer)
576
- register('real', :oid=>1021, :scalar_oid=>701, :typecast_method=>:float)
577
- register('character', :oid=>1014, :array_type=>:text, :typecast_method=>:string)
578
- register('character varying', :oid=>1015, :typecast_method=>:string)
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
- # {:foo=>1}.hstore.to_hash # {'foo'=>'1'}
32
- # v = {}.hstore
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
- # {'foo'=>'bar'}.hstore[:foo] # 'bar'
40
- # {'foo'=>'bar'}.hstore.to_hash[:foo] # 'bar'
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=>{'foo'=>'bar'}.hstore)
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)