sequel 3.38.0 → 3.39.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/CHANGELOG +62 -0
  2. data/README.rdoc +2 -2
  3. data/bin/sequel +12 -2
  4. data/doc/advanced_associations.rdoc +1 -1
  5. data/doc/association_basics.rdoc +13 -0
  6. data/doc/release_notes/3.39.0.txt +237 -0
  7. data/doc/schema_modification.rdoc +4 -4
  8. data/lib/sequel/adapters/jdbc/derby.rb +1 -0
  9. data/lib/sequel/adapters/mock.rb +5 -0
  10. data/lib/sequel/adapters/mysql.rb +8 -1
  11. data/lib/sequel/adapters/mysql2.rb +10 -3
  12. data/lib/sequel/adapters/postgres.rb +72 -8
  13. data/lib/sequel/adapters/shared/db2.rb +1 -0
  14. data/lib/sequel/adapters/shared/mssql.rb +57 -0
  15. data/lib/sequel/adapters/shared/mysql.rb +95 -19
  16. data/lib/sequel/adapters/shared/oracle.rb +14 -0
  17. data/lib/sequel/adapters/shared/postgres.rb +63 -24
  18. data/lib/sequel/adapters/shared/sqlite.rb +6 -9
  19. data/lib/sequel/connection_pool/sharded_threaded.rb +8 -3
  20. data/lib/sequel/connection_pool/threaded.rb +9 -4
  21. data/lib/sequel/database/query.rb +60 -48
  22. data/lib/sequel/database/schema_generator.rb +13 -6
  23. data/lib/sequel/database/schema_methods.rb +65 -12
  24. data/lib/sequel/dataset/actions.rb +22 -4
  25. data/lib/sequel/dataset/features.rb +5 -0
  26. data/lib/sequel/dataset/graph.rb +2 -3
  27. data/lib/sequel/dataset/misc.rb +2 -2
  28. data/lib/sequel/dataset/query.rb +0 -2
  29. data/lib/sequel/dataset/sql.rb +33 -12
  30. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  31. data/lib/sequel/extensions/eval_inspect.rb +17 -2
  32. data/lib/sequel/extensions/pg_array_ops.rb +15 -5
  33. data/lib/sequel/extensions/pg_interval.rb +2 -2
  34. data/lib/sequel/extensions/pg_row_ops.rb +18 -0
  35. data/lib/sequel/extensions/schema_dumper.rb +3 -11
  36. data/lib/sequel/model/associations.rb +3 -2
  37. data/lib/sequel/model/base.rb +57 -13
  38. data/lib/sequel/model/exceptions.rb +20 -2
  39. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  40. data/lib/sequel/plugins/defaults_setter.rb +15 -1
  41. data/lib/sequel/plugins/dirty.rb +2 -2
  42. data/lib/sequel/plugins/identity_map.rb +12 -8
  43. data/lib/sequel/plugins/subclasses.rb +19 -1
  44. data/lib/sequel/plugins/tree.rb +3 -3
  45. data/lib/sequel/plugins/validation_helpers.rb +24 -4
  46. data/lib/sequel/sql.rb +64 -24
  47. data/lib/sequel/timezones.rb +10 -2
  48. data/lib/sequel/version.rb +1 -1
  49. data/spec/adapters/mssql_spec.rb +26 -25
  50. data/spec/adapters/mysql_spec.rb +57 -23
  51. data/spec/adapters/oracle_spec.rb +34 -49
  52. data/spec/adapters/postgres_spec.rb +226 -128
  53. data/spec/adapters/sqlite_spec.rb +50 -49
  54. data/spec/core/connection_pool_spec.rb +22 -0
  55. data/spec/core/database_spec.rb +53 -47
  56. data/spec/core/dataset_spec.rb +36 -32
  57. data/spec/core/expression_filters_spec.rb +14 -2
  58. data/spec/core/mock_adapter_spec.rb +4 -0
  59. data/spec/core/object_graph_spec.rb +0 -13
  60. data/spec/core/schema_spec.rb +64 -5
  61. data/spec/core_extensions_spec.rb +1 -0
  62. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  63. data/spec/extensions/constraint_validations_spec.rb +316 -0
  64. data/spec/extensions/defaults_setter_spec.rb +24 -0
  65. data/spec/extensions/eval_inspect_spec.rb +9 -0
  66. data/spec/extensions/identity_map_spec.rb +11 -2
  67. data/spec/extensions/pg_array_ops_spec.rb +9 -0
  68. data/spec/extensions/pg_row_ops_spec.rb +11 -1
  69. data/spec/extensions/pg_row_plugin_spec.rb +4 -0
  70. data/spec/extensions/schema_dumper_spec.rb +8 -5
  71. data/spec/extensions/subclasses_spec.rb +14 -0
  72. data/spec/extensions/validation_helpers_spec.rb +15 -2
  73. data/spec/integration/dataset_test.rb +75 -1
  74. data/spec/integration/plugin_test.rb +146 -0
  75. data/spec/integration/schema_test.rb +34 -0
  76. data/spec/model/dataset_methods_spec.rb +38 -0
  77. data/spec/model/hooks_spec.rb +6 -0
  78. data/spec/model/validations_spec.rb +27 -2
  79. metadata +8 -2
@@ -248,6 +248,20 @@ module Sequel
248
248
  end
249
249
  end
250
250
 
251
+ # Oracle treats empty strings like NULL values, and doesn't support
252
+ # char_length, so make char_length use length with a nonempty string.
253
+ # Unfortunately, as Oracle treats the empty string as NULL, there is
254
+ # no way to get trim to return an empty string instead of nil if
255
+ # the string only contains spaces.
256
+ def emulated_function_sql_append(sql, f)
257
+ case f.f
258
+ when :char_length
259
+ literal_append(sql, Sequel::SQL::Function.new(:length, Sequel.join([f.args.first, 'x'])) - 1)
260
+ else
261
+ super
262
+ end
263
+ end
264
+
251
265
  # Oracle uses MINUS instead of EXCEPT, and doesn't support EXCEPT ALL
252
266
  def except(dataset, opts={})
253
267
  opts = {:all=>opts} unless opts.is_a?(Hash)
@@ -96,6 +96,7 @@ module Sequel
96
96
  RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
97
97
  SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
98
98
  FOREIGN_KEY_LIST_ON_DELETE_MAP = {'a'.freeze=>:no_action, 'r'.freeze=>:restrict, 'c'.freeze=>:cascade, 'n'.freeze=>:set_null, 'd'.freeze=>:set_default}.freeze
99
+ POSTGRES_DEFAULT_RE = /\A(?:B?('.*')::[^']+|\((-?\d+(?:\.\d+)?)\))\z/
99
100
 
100
101
  # SQL fragment for custom sequences (ones not created by serial primary key),
101
102
  # Returning the schema and literal form of the sequence name, by parsing
@@ -136,7 +137,7 @@ module Sequel
136
137
  SELECT_SERIAL_SEQUENCE_SQL = (<<-end_sql
137
138
  SELECT name.nspname AS "schema", seq.relname AS "sequence"
138
139
  FROM pg_class seq, pg_attribute attr, pg_depend dep,
139
- pg_namespace name, pg_constraint cons
140
+ pg_namespace name, pg_constraint cons, pg_class t
140
141
  WHERE seq.oid = dep.objid
141
142
  AND seq.relnamespace = name.oid
142
143
  AND seq.relkind = 'S'
@@ -144,6 +145,7 @@ module Sequel
144
145
  AND attr.attnum = dep.refobjsubid
145
146
  AND attr.attrelid = cons.conrelid
146
147
  AND attr.attnum = cons.conkey[1]
148
+ AND attr.attrelid = t.oid
147
149
  AND cons.contype = 'p'
148
150
  end_sql
149
151
  ).strip.gsub(/\s+/, ' ').freeze
@@ -373,30 +375,31 @@ module Sequel
373
375
  # Return primary key for the given table.
374
376
  def primary_key(table, opts={})
375
377
  quoted_table = quote_schema_table(table)
376
- @primary_keys.fetch(quoted_table) do
377
- schema, table = schema_and_table(table)
378
- sql = "#{SELECT_PK_SQL} AND pg_class.relname = #{literal(table)}"
379
- sql << "AND pg_namespace.nspname = #{literal(schema)}" if schema
380
- @primary_keys[quoted_table] = fetch(sql).single_value
381
- end
378
+ Sequel.synchronize{return @primary_keys[quoted_table] if @primary_keys.has_key?(quoted_table)}
379
+ schema, table = schema_and_table(table)
380
+ sql = "#{SELECT_PK_SQL} AND pg_class.relname = #{literal(table)}"
381
+ sql << " AND pg_namespace.nspname = #{literal(schema)}" if schema
382
+ value = fetch(sql).single_value
383
+ Sequel.synchronize{@primary_keys[quoted_table] = value}
382
384
  end
383
385
 
384
386
  # Return the sequence providing the default for the primary key for the given table.
385
387
  def primary_key_sequence(table, opts={})
386
388
  quoted_table = quote_schema_table(table)
387
- @primary_key_sequences.fetch(quoted_table) do
388
- schema, table = schema_and_table(table)
389
- table = literal(table)
390
- sql = "#{SELECT_SERIAL_SEQUENCE_SQL} AND seq.relname = #{table}"
389
+ Sequel.synchronize{return @primary_key_sequences[quoted_table] if @primary_key_sequences.has_key?(quoted_table)}
390
+ schema, table = schema_and_table(table)
391
+ table = literal(table)
392
+ sql = "#{SELECT_SERIAL_SEQUENCE_SQL} AND t.relname = #{table}"
393
+ sql << " AND name.nspname = #{literal(schema)}" if schema
394
+ if pks = fetch(sql).single_record
395
+ value = literal(SQL::QualifiedIdentifier.new(pks[:schema], pks[:sequence]))
396
+ Sequel.synchronize{@primary_key_sequences[quoted_table] = value}
397
+ else
398
+ sql = "#{SELECT_CUSTOM_SEQUENCE_SQL} AND t.relname = #{table}"
391
399
  sql << " AND name.nspname = #{literal(schema)}" if schema
392
- unless pks = fetch(sql).single_record
393
- sql = "#{SELECT_CUSTOM_SEQUENCE_SQL} AND t.relname = #{table}"
394
- sql << " AND name.nspname = #{literal(schema)}" if schema
395
- pks = fetch(sql).single_record
396
- end
397
-
398
- @primary_key_sequences[quoted_table] = if pks
399
- literal(SQL::QualifiedIdentifier.new(pks[:schema], LiteralString.new(pks[:sequence])))
400
+ if pks = fetch(sql).single_record
401
+ value = literal(SQL::QualifiedIdentifier.new(pks[:schema], LiteralString.new(pks[:sequence])))
402
+ Sequel.synchronize{@primary_key_sequences[quoted_table] = value}
400
403
  end
401
404
  end
402
405
  end
@@ -407,7 +410,7 @@ module Sequel
407
410
  @conversion_procs = get_conversion_procs
408
411
  end
409
412
 
410
- # Reset the primary key sequence for the given table, baseing it on the
413
+ # Reset the primary key sequence for the given table, basing it on the
411
414
  # maximum current value of the table's primary key.
412
415
  def reset_primary_key_sequence(table)
413
416
  return unless seq = primary_key_sequence(table)
@@ -513,7 +516,7 @@ module Sequel
513
516
  end
514
517
 
515
518
  # Handle :using option for set_column_type op, and the :validate_constraint op.
516
- def alter_table_sql(table, op)
519
+ def alter_table_op_sql(table, op)
517
520
  case op[:op]
518
521
  when :set_column_type
519
522
  s = super
@@ -524,7 +527,7 @@ module Sequel
524
527
  end
525
528
  s
526
529
  when :validate_constraint
527
- "ALTER TABLE #{quote_schema_table(table)} VALIDATE CONSTRAINT #{quote_identifier(op[:name])}"
530
+ "VALIDATE CONSTRAINT #{quote_identifier(op[:name])}"
528
531
  else
529
532
  super
530
533
  end
@@ -549,6 +552,14 @@ module Sequel
549
552
  end
550
553
  end
551
554
 
555
+ # Handle PostgreSQL specific default format.
556
+ def column_schema_normalize_default(default, type)
557
+ if m = POSTGRES_DEFAULT_RE.match(default)
558
+ default = m[1] || m[2]
559
+ end
560
+ super(default, type)
561
+ end
562
+
552
563
  # If the :prepare option is given and we aren't in a savepoint,
553
564
  # prepare the transaction for a two-phase commit.
554
565
  def commit_transaction(conn, opts={})
@@ -559,6 +570,12 @@ module Sequel
559
570
  end
560
571
  end
561
572
 
573
+ # PostgreSQL can't combine rename_column operations, and it can combine
574
+ # the custom validate_constraint operation.
575
+ def combinable_alter_table_op?(op)
576
+ (super || op[:op] == :validate_constraint) && op[:op] != :rename_column
577
+ end
578
+
562
579
  # The SQL queries to execute when starting a new connection.
563
580
  def connection_configuration_sqls
564
581
  sqls = []
@@ -739,8 +756,10 @@ module Sequel
739
756
  # changed.
740
757
  def remove_cached_schema(table)
741
758
  tab = quote_schema_table(table)
742
- @primary_keys.delete(tab)
743
- @primary_key_sequences.delete(tab)
759
+ Sequel.synchronize do
760
+ @primary_keys.delete(tab)
761
+ @primary_key_sequences.delete(tab)
762
+ end
744
763
  super
745
764
  end
746
765
 
@@ -756,6 +775,16 @@ module Sequel
756
775
  super && schema[:default] =~ /\Anextval/io
757
776
  end
758
777
 
778
+ # Recognize PostgreSQL interval type.
779
+ def schema_column_type(db_type)
780
+ case db_type
781
+ when /\Ainterval\z/io
782
+ :interval
783
+ else
784
+ super
785
+ end
786
+ end
787
+
759
788
  # The dataset used for parsing table schemas, using the pg_* system catalogs.
760
789
  def schema_parse_table(table_name, opts)
761
790
  m = output_identifier_meth(opts[:dataset])
@@ -813,6 +842,11 @@ module Sequel
813
842
  "(#{Array(args).map{|a| Array(a).reverse.join(' ')}.join(', ')})"
814
843
  end
815
844
 
845
+ # PostgreSQL can combine multiple alter table ops into a single query.
846
+ def supports_combining_alter_table_ops?
847
+ true
848
+ end
849
+
816
850
  # Handle bigserial type if :serial option is present
817
851
  def type_literal_generic_bignum(column)
818
852
  column[:serial] ? :bigserial : super
@@ -1011,6 +1045,11 @@ module Sequel
1011
1045
  true
1012
1046
  end
1013
1047
 
1048
+ # PostgreSQL supports pattern matching via regular expressions
1049
+ def supports_regexp?
1050
+ true
1051
+ end
1052
+
1014
1053
  # PostgreSQL supports timezones in literal timestamps
1015
1054
  def supports_timestamp_timezones?
1016
1055
  true
@@ -232,10 +232,8 @@ module Sequel
232
232
  duplicate_table(table){|columns| columns.each{|s| s[:primary_key] = nil}}
233
233
  when :foreign_key
234
234
  duplicate_table(table, :no_foreign_keys=>true)
235
- when :unique
236
- duplicate_table(table)
237
235
  else
238
- raise Error, "Unsupported :type option for drop_constraint: #{op[:type].inspect}"
236
+ duplicate_table(table)
239
237
  end
240
238
  when :add_constraint
241
239
  duplicate_table(table, :constraints=>[op])
@@ -413,6 +411,7 @@ module Sequel
413
411
  module DatasetMethods
414
412
  SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct columns from join where group having compounds order limit')
415
413
  CONSTANT_MAP = {:CURRENT_DATE=>"date(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIMESTAMP=>"datetime(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIME=>"time(CURRENT_TIMESTAMP, 'localtime')".freeze}
414
+ EMULATED_FUNCTION_MAP = {:char_length=>'length'.freeze}
416
415
  EXTRACT_MAP = {:year=>"'%Y'", :month=>"'%m'", :day=>"'%d'", :hour=>"'%H'", :minute=>"'%M'", :second=>"'%f'"}
417
416
  NOT_SPACE = Dataset::NOT_SPACE
418
417
  COMMA = Dataset::COMMA
@@ -445,13 +444,11 @@ module Sequel
445
444
  end
446
445
  end
447
446
 
448
- # SQLite does not support pattern matching via regular expressions.
449
- # SQLite is case insensitive (depending on pragma), so use LIKE for
450
- # ILIKE.
447
+ # SQLite is case insensitive (depending on pragma), so use LIKE for ILIKE.
448
+ # It also doesn't support a NOT LIKE b, you need to use NOT (a LIKE b).
449
+ # It doesn't support xor or the extract function natively, so those have to be emulated.
451
450
  def complex_expression_sql_append(sql, op, args)
452
451
  case op
453
- when :~, :'!~', :'~*', :'!~*'
454
- raise Error, "SQLite does not support pattern matching via regular expressions"
455
452
  when :ILIKE
456
453
  super(sql, :LIKE, args.map{|a| SQL::Function.new(:upper, a)})
457
454
  when :"NOT LIKE", :"NOT ILIKE"
@@ -578,7 +575,7 @@ module Sequel
578
575
  col
579
576
  end
580
577
  end
581
-
578
+
582
579
  # SQL fragment specifying a list of identifiers
583
580
  def identifier_list(columns)
584
581
  columns.map{|i| quote_identifier(i)}.join(COMMA)
@@ -221,10 +221,15 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
221
221
  if @connections_to_remove.include?(conn)
222
222
  remove(thread, conn, server)
223
223
  else
224
- if @queue
225
- available_connections(server).unshift(allocated(server).delete(thread))
224
+ conn = allocated(server).delete(thread)
225
+
226
+ case @connection_handling
227
+ when :queue
228
+ available_connections(server).unshift(conn)
229
+ when :disconnect
230
+ @disconnection_proc.call(conn) if @disconnection_proc
226
231
  else
227
- available_connections(server) << allocated(server).delete(thread)
232
+ available_connections(server) << conn
228
233
  end
229
234
  end
230
235
  end
@@ -27,7 +27,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
27
27
  @max_size = Integer(opts[:max_connections] || 4)
28
28
  raise(Sequel::Error, ':max_connections must be positive') if @max_size < 1
29
29
  @mutex = Mutex.new
30
- @queue = opts[:connection_handling] == :queue
30
+ @connection_handling = opts[:connection_handling]
31
31
  @available_connections = []
32
32
  @allocated = {}
33
33
  @timeout = Integer(opts[:pool_timeout] || 5)
@@ -157,10 +157,15 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
157
157
  # Releases the connection assigned to the supplied thread back to the pool.
158
158
  # The calling code should already have the mutex before calling this.
159
159
  def release(thread)
160
- if @queue
161
- @available_connections.unshift(@allocated.delete(thread))
160
+ conn = @allocated.delete(thread)
161
+
162
+ case @connection_handling
163
+ when :queue
164
+ @available_connections.unshift(conn)
165
+ when :disconnect
166
+ @disconnection_proc.call(conn) if @disconnection_proc
162
167
  else
163
- @available_connections << @allocated.delete(thread)
168
+ @available_connections << conn
164
169
  end
165
170
  end
166
171
 
@@ -21,10 +21,10 @@ module Sequel
21
21
  :repeatable=>'REPEATABLE READ'.freeze,
22
22
  :serializable=>'SERIALIZABLE'.freeze}
23
23
 
24
- POSTGRES_DEFAULT_RE = /\A(?:B?('.*')::[^']+|\((-?\d+(?:\.\d+)?)\))\z/
25
- MSSQL_DEFAULT_RE = /\A(?:\(N?('.*')\)|\(\((-?\d+(?:\.\d+)?)\)\))\z/
26
- MYSQL_TIMESTAMP_RE = /\ACURRENT_(?:DATE|TIMESTAMP)?\z/
27
24
  STRING_DEFAULT_RE = /\A'(.*)'\z/
25
+ CURRENT_TIMESTAMP_RE = /now|CURRENT|getdate/io
26
+ COLUMN_SCHEMA_DATETIME_TYPES = [:date, :datetime]
27
+ COLUMN_SCHEMA_STRING_TYPES = [:string, :blob, :date, :datetime, :time, :enum, :set, :interval]
28
28
 
29
29
  # The prepared statement object hash for this database, keyed by name symbol
30
30
  attr_reader :prepared_statements
@@ -390,56 +390,70 @@ module Sequel
390
390
  SQL_BEGIN
391
391
  end
392
392
 
393
+ # Whether the type should be treated as a string type when parsing the
394
+ # column schema default value.
395
+ def column_schema_default_string_type?(type)
396
+ COLUMN_SCHEMA_STRING_TYPES.include?(type)
397
+ end
398
+
399
+ # Transform the given normalized default string into a ruby object for the
400
+ # given type.
401
+ def column_schema_default_to_ruby_value(default, type)
402
+ case type
403
+ when :boolean
404
+ case default
405
+ when /[f0]/i
406
+ false
407
+ when /[t1]/i
408
+ true
409
+ end
410
+ when :string, :enum, :set, :interval
411
+ default
412
+ when :blob
413
+ Sequel::SQL::Blob.new(default)
414
+ when :integer
415
+ Integer(default)
416
+ when :float
417
+ Float(default)
418
+ when :date
419
+ Sequel.string_to_date(default)
420
+ when :datetime
421
+ DateTime.parse(default)
422
+ when :time
423
+ Sequel.string_to_time(default)
424
+ when :decimal
425
+ BigDecimal.new(default)
426
+ end
427
+ end
428
+
429
+ # Normalize the default value string for the given type
430
+ # and return the normalized value.
431
+ def column_schema_normalize_default(default, type)
432
+ if column_schema_default_string_type?(type)
433
+ return unless m = STRING_DEFAULT_RE.match(default)
434
+ m[1].gsub("''", "'")
435
+ else
436
+ default
437
+ end
438
+ end
439
+
393
440
  # Convert the given default, which should be a database specific string, into
394
441
  # a ruby object.
395
442
  def column_schema_to_ruby_default(default, type)
396
443
  return if default.nil?
397
- orig_default = default
398
- if database_type == :postgres and m = POSTGRES_DEFAULT_RE.match(default)
399
- default = m[1] || m[2]
400
- end
401
- if database_type == :mssql and m = MSSQL_DEFAULT_RE.match(default)
402
- default = m[1] || m[2]
403
- end
404
- if [:string, :blob, :date, :datetime, :time, :enum].include?(type)
405
- if database_type == :mysql
406
- return if [:date, :datetime, :time].include?(type) && MYSQL_TIMESTAMP_RE.match(default)
407
- orig_default = default = "'#{default.gsub("'", "''").gsub('\\', '\\\\')}'"
408
- end
409
- return unless m = STRING_DEFAULT_RE.match(default)
410
- default = m[1].gsub("''", "'")
411
- end
412
- res = begin
413
- case type
414
- when :boolean
415
- case default
416
- when /[f0]/i
417
- false
418
- when /[t1]/i
419
- true
444
+ if COLUMN_SCHEMA_DATETIME_TYPES.include?(type)
445
+ if CURRENT_TIMESTAMP_RE.match(default)
446
+ if type == :date
447
+ return Sequel::CURRENT_DATE
448
+ else
449
+ return Sequel::CURRENT_TIMESTAMP
420
450
  end
421
- when :string, :enum
422
- default
423
- when :blob
424
- Sequel::SQL::Blob.new(default)
425
- when :integer
426
- Integer(default)
427
- when :float
428
- Float(default)
429
- when :date
430
- Sequel.string_to_date(default)
431
- when :datetime
432
- DateTime.parse(default)
433
- when :time
434
- Sequel.string_to_time(default)
435
- when :decimal
436
- BigDecimal.new(default)
437
451
  end
438
- rescue
439
- nil
440
452
  end
453
+ default = column_schema_normalize_default(default, type)
454
+ column_schema_default_to_ruby_value(default, type) rescue nil
441
455
  end
442
-
456
+
443
457
  if (! defined?(RUBY_ENGINE) or RUBY_ENGINE == 'ruby' or RUBY_ENGINE == 'rbx') and RUBY_VERSION < '1.9'
444
458
  # Whether to commit the current transaction. On ruby 1.8 and rubinius,
445
459
  # Thread.current.status is checked because Thread#kill skips rescue
@@ -568,15 +582,13 @@ module Sequel
568
582
  # such as :integer or :string.
569
583
  def schema_column_type(db_type)
570
584
  case db_type
571
- when /\Ainterval\z/io
572
- :interval
573
585
  when /\A(character( varying)?|n?(var)?char|n?text)/io
574
586
  :string
575
587
  when /\A(int(eger)?|(big|small|tiny)int)/io
576
588
  :integer
577
589
  when /\Adate\z/io
578
590
  :date
579
- when /\A((small)?datetime|timestamp( with(out)? time zone)?)\z/io
591
+ when /\A((small)?datetime|timestamp( with(out)? time zone)?)(\(\d+\))?\z/io
580
592
  :datetime
581
593
  when /\Atime( with(out)? time zone)?\z/io
582
594
  :time
@@ -13,7 +13,7 @@ module Sequel
13
13
  # the column method, which makes for a nicer DSL.
14
14
  #
15
15
  # For more information on Sequel's support for schema modification, see
16
- # the {"Migrations and Schema Modification" guide}[link:files/doc/migration_rdoc.html].
16
+ # the {"Schema Modification" guide}[link:files/doc/schema_modification_rdoc.html].
17
17
  class CreateTableGenerator
18
18
  # Classes specifying generic types that Sequel will convert to database-specific types.
19
19
  GENERIC_TYPES=[String, Integer, Fixnum, Bignum, Float, Numeric, BigDecimal,
@@ -270,7 +270,7 @@ module Sequel
270
270
  # alter a table's description.
271
271
  #
272
272
  # For more information on Sequel's support for schema modification, see
273
- # the {"Migrations and Schema Modification" guide}[link:files/doc/migration_rdoc.html].
273
+ # the {"Schema Modification" guide}[link:files/doc/schema_modification_rdoc.html].
274
274
  class AlterTableGenerator
275
275
  # An array of DDL operations to perform
276
276
  attr_reader :operations
@@ -294,7 +294,7 @@ module Sequel
294
294
  # Add a constraint with the given name and args to the DDL for the table.
295
295
  # See CreateTableGenerator#constraint.
296
296
  #
297
- # add_constraint(:valid_name, :name.like('A%'))
297
+ # add_constraint(:valid_name, Sequel.like(:name, 'A%'))
298
298
  # # ADD CONSTRAINT valid_name CHECK (name LIKE 'A%')
299
299
  def add_constraint(name, *args, &block)
300
300
  @operations << {:op => :add_constraint, :name => name, :type => :check, :check => block || args}
@@ -430,13 +430,20 @@ module Sequel
430
430
  @operations << {:op => :set_column_type, :name => name, :type => type}.merge(opts)
431
431
  end
432
432
 
433
- # Modify a column's NOT NULL constraint.
433
+ # Set a given column as allowing NULL values.
434
434
  #
435
- # set_column_allow_null(:artist_name, false) # ALTER COLUMN artist_name SET NOT NULL
436
- def set_column_allow_null(name, allow_null)
435
+ # set_column_allow_null(:artist_name) # ALTER COLUMN artist_name DROP NOT NULL
436
+ def set_column_allow_null(name, allow_null=true)
437
437
  @operations << {:op => :set_column_null, :name => name, :null => allow_null}
438
438
  end
439
439
 
440
+ # Set a given column as not allowing NULL values.
441
+ #
442
+ # set_column_not_null(:artist_name) # ALTER COLUMN artist_name SET NOT NULL
443
+ def set_column_not_null(name)
444
+ set_column_allow_null(name, false)
445
+ end
446
+
440
447
  private
441
448
 
442
449
  # Add a composite primary key constraint