sequel 4.38.0 → 4.39.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +28 -0
  3. data/doc/association_basics.rdoc +1 -1
  4. data/doc/core_extensions.rdoc +8 -8
  5. data/doc/object_model.rdoc +7 -8
  6. data/doc/querying.rdoc +2 -2
  7. data/doc/release_notes/4.39.0.txt +127 -0
  8. data/doc/sql.rdoc +31 -31
  9. data/doc/transactions.rdoc +1 -1
  10. data/doc/virtual_rows.rdoc +1 -1
  11. data/lib/sequel/adapters/mock.rb +22 -66
  12. data/lib/sequel/adapters/shared/access.rb +2 -0
  13. data/lib/sequel/adapters/shared/cubrid.rb +2 -0
  14. data/lib/sequel/adapters/shared/db2.rb +2 -0
  15. data/lib/sequel/adapters/shared/firebird.rb +2 -0
  16. data/lib/sequel/adapters/shared/informix.rb +2 -0
  17. data/lib/sequel/adapters/shared/mssql.rb +10 -2
  18. data/lib/sequel/adapters/shared/mysql.rb +17 -4
  19. data/lib/sequel/adapters/shared/oracle.rb +9 -0
  20. data/lib/sequel/adapters/shared/postgres.rb +34 -1
  21. data/lib/sequel/adapters/shared/sqlanywhere.rb +1 -0
  22. data/lib/sequel/adapters/shared/sqlite.rb +8 -0
  23. data/lib/sequel/core.rb +10 -0
  24. data/lib/sequel/database.rb +5 -0
  25. data/lib/sequel/database/connecting.rb +28 -0
  26. data/lib/sequel/database/misc.rb +0 -51
  27. data/lib/sequel/database/query.rb +1 -1
  28. data/lib/sequel/database/schema_generator.rb +9 -0
  29. data/lib/sequel/database/transactions.rb +65 -0
  30. data/lib/sequel/dataset/actions.rb +1 -1
  31. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  32. data/lib/sequel/extensions/core_extensions.rb +1 -1
  33. data/lib/sequel/extensions/core_refinements.rb +1 -1
  34. data/lib/sequel/extensions/migration.rb +17 -1
  35. data/lib/sequel/extensions/no_auto_literal_strings.rb +1 -1
  36. data/lib/sequel/extensions/pg_array_ops.rb +1 -1
  37. data/lib/sequel/extensions/pg_hstore_ops.rb +1 -1
  38. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  39. data/lib/sequel/extensions/pg_interval.rb +1 -1
  40. data/lib/sequel/extensions/pg_json_ops.rb +28 -15
  41. data/lib/sequel/extensions/pg_range_ops.rb +1 -1
  42. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  43. data/lib/sequel/extensions/select_remove.rb +1 -1
  44. data/lib/sequel/extensions/sql_expr.rb +1 -1
  45. data/lib/sequel/model/associations.rb +1 -1
  46. data/lib/sequel/plugins/active_model.rb +19 -8
  47. data/lib/sequel/plugins/dataset_associations.rb +1 -1
  48. data/lib/sequel/plugins/hook_class_methods.rb +38 -2
  49. data/lib/sequel/plugins/list.rb +1 -1
  50. data/lib/sequel/plugins/pg_array_associations.rb +3 -3
  51. data/lib/sequel/plugins/timestamps.rb +15 -2
  52. data/lib/sequel/plugins/touch.rb +6 -0
  53. data/lib/sequel/sql.rb +15 -11
  54. data/lib/sequel/version.rb +1 -1
  55. data/spec/adapters/mysql_spec.rb +18 -0
  56. data/spec/adapters/postgres_spec.rb +30 -1
  57. data/spec/core/database_spec.rb +29 -0
  58. data/spec/core/mock_adapter_spec.rb +32 -1
  59. data/spec/extensions/migration_spec.rb +46 -0
  60. data/spec/extensions/pg_interval_spec.rb +1 -0
  61. data/spec/extensions/pg_json_ops_spec.rb +13 -0
  62. data/spec/extensions/timestamps_spec.rb +11 -0
  63. data/spec/extensions/touch_spec.rb +8 -0
  64. metadata +4 -2
@@ -94,7 +94,7 @@ module Sequel
94
94
  def after_destroy
95
95
  super
96
96
 
97
- f = Sequel.expr(position_field)
97
+ f = Sequel[position_field]
98
98
  list_dataset.where(f > position_value).update(f => f - 1)
99
99
  end
100
100
 
@@ -491,14 +491,14 @@ module Sequel
491
491
  expr = case obj
492
492
  when Sequel::Model
493
493
  if (assoc_pks = obj.get_column_value(key)) && !assoc_pks.empty?
494
- Sequel.expr(pk=>assoc_pks.to_a)
494
+ Sequel[pk=>assoc_pks.to_a]
495
495
  end
496
496
  when Array
497
497
  if (assoc_pks = obj.map{|o| o.get_column_value(key)}.flatten.compact.uniq) && !assoc_pks.empty?
498
- Sequel.expr(pk=>assoc_pks)
498
+ Sequel[pk=>assoc_pks]
499
499
  end
500
500
  when Sequel::Dataset
501
- Sequel.expr(pk=>obj.select{Sequel.pg_array_op(ref.qualify(obj.model.table_name, ref[:key_column])).unnest})
501
+ Sequel[pk=>obj.select{Sequel.pg_array_op(ref.qualify(obj.model.table_name, ref[:key_column])).unnest}]
502
502
  end
503
503
  expr = Sequel::SQL::Constants::FALSE unless expr
504
504
  expr = add_association_filter_conditions(ref, obj, expr)
@@ -24,12 +24,14 @@ module Sequel
24
24
  # Configure the plugin by setting the available options. Note that
25
25
  # if this method is run more than once, previous settings are ignored,
26
26
  # and it will just use the settings given or the default settings. Options:
27
+ # :allow_manual_update :: Whether to skip setting the update timestamp if it has been modified manually (default: false)
27
28
  # :create :: The field to hold the create timestamp (default: :created_at)
28
29
  # :force :: Whether to overwrite an existing create timestamp (default: false)
29
30
  # :update :: The field to hold the update timestamp (default: :updated_at)
30
31
  # :update_on_create :: Whether to set the update timestamp to the create timestamp when creating (default: false)
31
32
  def self.configure(model, opts=OPTS)
32
33
  model.instance_eval do
34
+ @allow_manual_timestamp_update = opts[:allow_manual_update]||false
33
35
  @create_timestamp_field = opts[:create]||:created_at
34
36
  @update_timestamp_field = opts[:update]||:updated_at
35
37
  @create_timestamp_overwrite = opts[:force]||false
@@ -49,9 +51,19 @@ module Sequel
49
51
  @create_timestamp_overwrite
50
52
  end
51
53
 
52
- Plugins.inherited_instance_variables(self, :@create_timestamp_field=>nil, :@update_timestamp_field=>nil,
53
- :@create_timestamp_overwrite=>nil, :@set_update_timestamp_on_create=>nil)
54
+ Plugins.inherited_instance_variables(self,
55
+ :@allow_manual_timestamp_update=>nil,
56
+ :@create_timestamp_field=>nil,
57
+ :@create_timestamp_overwrite=>nil,
58
+ :@set_update_timestamp_on_create=>nil,
59
+ :@update_timestamp_field=>nil)
54
60
 
61
+
62
+ # Whether to allow manual setting of the update timestamp when creating
63
+ def allow_manual_timestamp_update?
64
+ @allow_manual_timestamp_update
65
+ end
66
+
55
67
  # Whether to set the update timestamp to the create timestamp when creating
56
68
  def set_update_timestamp_on_create?
57
69
  @set_update_timestamp_on_create
@@ -88,6 +100,7 @@ module Sequel
88
100
  # Set the update timestamp to the time given or the current time if the
89
101
  # object has a setter method for the update timestamp field.
90
102
  def set_update_timestamp(time=nil)
103
+ return if model.allow_manual_timestamp_update? && modified?(model.update_timestamp_field)
91
104
  meth = :"#{model.update_timestamp_field}="
92
105
  set_column_value(meth, time||model.dataset.current_datetime) if respond_to?(meth)
93
106
  end
@@ -78,6 +78,12 @@ module Sequel
78
78
  end
79
79
 
80
80
  module InstanceMethods
81
+ # Touch all of the model's touched_associations when creating the object.
82
+ def after_create
83
+ super
84
+ touch_associations
85
+ end
86
+
81
87
  # Touch all of the model's touched_associations when destroying the object.
82
88
  def after_destroy
83
89
  super
@@ -465,6 +465,10 @@ module Sequel
465
465
  # to construct via other methods. For example:
466
466
  #
467
467
  # Sequel.expr(1) - :a # SQL: (1 - a)
468
+ #
469
+ # On the Sequel module, this is aliased as #[], for easier use:
470
+ #
471
+ # Sequel[1] - :a # SQL: (1 - a)
468
472
  def expr(arg=(no_arg=true), &block)
469
473
  if block_given?
470
474
  if no_arg
@@ -793,7 +797,7 @@ module Sequel
793
797
  # If the argument given is Numeric, treat it as a NumericExpression,
794
798
  # allowing code such as:
795
799
  #
796
- # 1 + Sequel.expr(:x) # SQL: (1 + x)
800
+ # 1 + Sequel[:x] # SQL: (1 + x)
797
801
  # Sequel.expr{1 - x(y)} # SQL: (1 - x(y))
798
802
  def coerce(other)
799
803
  if other.is_a?(Numeric)
@@ -822,17 +826,17 @@ module Sequel
822
826
  # return when using a hash with a single entry, where the receiver was the key
823
827
  # and the argument was the value. Example:
824
828
  #
825
- # Sequel.expr(:a) =~ 1 # (a = 1)
826
- # Sequel.expr(:a) =~ [1, 2] # (a IN [1, 2])
827
- # Sequel.expr(:a) =~ nil # (a IS NULL)
829
+ # Sequel[:a] =~ 1 # (a = 1)
830
+ # Sequel[:a] =~ [1, 2] # (a IN [1, 2])
831
+ # Sequel[:a] =~ nil # (a IS NULL)
828
832
  #
829
833
  # On Ruby 1.9+, this also adds the !~ method, for easily setting up not equals,
830
834
  # exclusion, and inverse pattern matching. This is the same as as inverting the
831
835
  # result of the =~ method
832
836
  #
833
- # Sequel.expr(:a) !~ 1 # (a != 1)
834
- # Sequel.expr(:a) !~ [1, 2] # (a NOT IN [1, 2])
835
- # Sequel.expr(:a) !~ nil # (a IS NOT NULL)
837
+ # Sequel[:a] !~ 1 # (a != 1)
838
+ # Sequel[:a] !~ [1, 2] # (a NOT IN [1, 2])
839
+ # Sequel[:a] !~ nil # (a IS NOT NULL)
836
840
  module PatternMatchMethods
837
841
  # Set up an equality, inclusion, or pattern match operation, based on the type
838
842
  # of the argument.
@@ -929,7 +933,7 @@ module Sequel
929
933
  module QualifyingMethods
930
934
  # If no arguments are given, return an SQL::ColumnAll:
931
935
  #
932
- # Sequel.expr(:a__b).* # a.b.*
936
+ # Sequel[:a__b].* # a.b.*
933
937
  def *(ce=(arg=false;nil))
934
938
  if arg == false
935
939
  Sequel::SQL::ColumnAll.new(self)
@@ -940,8 +944,8 @@ module Sequel
940
944
 
941
945
  # Qualify the receiver with the given +qualifier+ (table for column/schema for table).
942
946
  #
943
- # Sequel.expr(:column).qualify(:table) # "table"."column"
944
- # Sequel.expr(:table).qualify(:schema) # "schema"."table"
947
+ # Sequel[:column].qualify(:table) # "table"."column"
948
+ # Sequel[:table].qualify(:schema) # "schema"."table"
945
949
  # Sequel.qualify(:table, :column).qualify(:schema) # "schema"."table"."column"
946
950
  def qualify(qualifier)
947
951
  QualifiedIdentifier.new(qualifier, self)
@@ -1392,7 +1396,7 @@ module Sequel
1392
1396
  # Return a new function where the function name will not be quoted even
1393
1397
  # if the database supports quoted functions:
1394
1398
  #
1395
- # Sequel.expr(:foo).function.unquoted # foo()
1399
+ # Sequel[:foo].function.unquoted # foo()
1396
1400
  def unquoted
1397
1401
  with_opts(:quoted=>false)
1398
1402
  end
@@ -5,7 +5,7 @@ module Sequel
5
5
  MAJOR = 4
6
6
  # The minor version of Sequel. Bumped for every non-patch level
7
7
  # release, generally around once a month.
8
- MINOR = 38
8
+ MINOR = 39
9
9
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
10
10
  # releases that fix regressions from previous versions.
11
11
  TINY = 0
@@ -515,6 +515,24 @@ describe "A MySQL database" do
515
515
  end
516
516
  end
517
517
 
518
+ it "should correctly handle add_column :after option" do
519
+ @db.create_table(:items){Integer :id; Integer :value}
520
+ @db.alter_table(:items){add_column :name, String, :after=>:id}
521
+ @db[:items].columns.must_equal [:id, :name, :value]
522
+ end
523
+
524
+ it "should correctly handle add_column :first option" do
525
+ @db.create_table(:items){Integer :id; Integer :value}
526
+ @db.alter_table(:items){add_column :name, String, :first => true}
527
+ @db[:items].columns.must_equal [:name, :id, :value]
528
+ end
529
+
530
+ it "should correctly handle add_foreign_key :first option" do
531
+ @db.create_table(:items){primary_key :id; Integer :value}
532
+ @db.alter_table(:items){add_foreign_key :parent_id, :items, :first => true}
533
+ @db[:items].columns.must_equal [:parent_id, :id, :value]
534
+ end
535
+
518
536
  it "should have rename_column support keep existing options" do
519
537
  @db.create_table(:items){String :id, :null=>false, :default=>'blah'}
520
538
  @db.alter_table(:items){rename_column :id, :nid}
@@ -204,6 +204,17 @@ describe "PostgreSQL", 'INSERT ON CONFLICT' do
204
204
  @ds.all.must_equal [{:a=>1, :b=>2, :c=>3}]
205
205
  end
206
206
 
207
+ it "Dataset#insert_ignore and insert_conflict should work with multi_insert/import" do
208
+ @ds.insert(1, 2, 3)
209
+ @ds.insert_ignore.multi_insert([{:a=>1, :b=>3, :c=>4}])
210
+ @ds.insert_ignore.import([:a, :b, :c], [[1, 3, 4]])
211
+ @ds.all.must_equal [{:a=>1, :b=>2, :c=>3}]
212
+ @ds.insert_conflict(:target=>:a, :update=>{:b=>3}).import([:a, :b, :c], [[1, 3, 4]])
213
+ @ds.all.must_equal [{:a=>1, :b=>3, :c=>3}]
214
+ @ds.insert_conflict(:target=>:a, :update=>{:b=>4}).multi_insert([{:a=>1, :b=>5, :c=>6}])
215
+ @ds.all.must_equal [{:a=>1, :b=>4, :c=>3}]
216
+ end
217
+
207
218
  it "Dataset#insert_conflict should handle upserts" do
208
219
  @ds.insert(1, 2, 3)
209
220
  @ds.insert_conflict(:target=>:a, :update=>{:b=>3}).insert(1, 3, 4).must_equal nil
@@ -991,6 +1002,11 @@ describe "A PostgreSQL database" do
991
1002
  @db[:posts].full_text_search(:title, 'rubinius ruby', :plain=>true).select_order_map(:title).must_equal ['jruby rubinius ruby maglev mri iron', 'ruby jruby maglev mri rubinius iron']
992
1003
  @db[:posts].full_text_search(:title, 'jruby maglev', :plain=>true).select_order_map(:title).must_equal ['jruby rubinius ruby maglev mri iron', 'ruby jruby maglev mri rubinius iron']
993
1004
 
1005
+ if DB.server_version >= 90600
1006
+ @db[:posts].full_text_search(:title, 'rubinius ruby', :to_tsquery=>:phrase).select_order_map(:title).must_equal ['jruby rubinius ruby maglev mri iron']
1007
+ @db[:posts].full_text_search(:title, 'jruby maglev', :to_tsquery=>:phrase).select_order_map(:title).must_equal ['ruby jruby maglev mri rubinius iron']
1008
+ end
1009
+
994
1010
  @db[:posts].full_text_search(Sequel.function(:to_tsvector, 'simple', :title), 'rails', :tsvector=>true).all.must_equal [{:title=>'ruby rails', :body=>'yowsa'}]
995
1011
  @db[:posts].full_text_search(:title, Sequel.function(:to_tsquery, 'simple', 'rails'), :tsquery=>true).all.must_equal [{:title=>'ruby rails', :body=>'yowsa'}]
996
1012
  proc{@db[:posts].full_text_search(Sequel.function(:to_tsvector, 'simple', :title), 'rubinius ruby', :tsvector=>true, :phrase=>true)}.must_raise(Sequel::Error)
@@ -1058,6 +1074,13 @@ describe "A PostgreSQL database" do
1058
1074
  @db.create_table!(:posts1){primary_key :a}
1059
1075
  @db.rename_table(:posts1, :posts)
1060
1076
  end
1077
+
1078
+ it "should adding a primary key only if it does not already exists" do
1079
+ @db.create_table(:posts){Integer :a}
1080
+ @db.alter_table(:posts){add_column :b, Integer}
1081
+ @db.alter_table(:posts){add_column :b, Integer, :if_not_exists=>true}
1082
+ proc{@db.alter_table(:posts){add_column :b, Integer}}.must_raise Sequel::DatabaseError
1083
+ end if DB.server_version >= 90600
1061
1084
  end
1062
1085
 
1063
1086
  describe "Postgres::Dataset#import" do
@@ -1071,7 +1094,6 @@ describe "Postgres::Dataset#import" do
1071
1094
  @db.drop_table?(:test)
1072
1095
  end
1073
1096
 
1074
-
1075
1097
  it "#import should a single insert statement" do
1076
1098
  @ds.import([:x, :y], [[1, 2], [3, 4]])
1077
1099
  check_sqls do
@@ -2916,6 +2938,13 @@ describe 'PostgreSQL json type' do
2916
2938
  @db.get(jo.set(%w'a', 'f'=>'g')['a']['f']).must_equal 'g'
2917
2939
  end
2918
2940
 
2941
+ if DB.server_version >= 90600 && json_type == :jsonb
2942
+ @db.get(pg_json.call([3]).op.insert(['0'], {'a'=>2})[0]['a']).must_equal 2
2943
+ @db.get(pg_json.call([3]).op.insert(['0'], {'a'=>2}, false)[0]['a']).must_equal 2
2944
+ @db.get(pg_json.call([3]).op.insert(['0'], {'a'=>2}, true)[0]).must_equal 3
2945
+ @db.get(pg_json.call([3]).op.insert(['0'], {'a'=>2}, true)[1]['a']).must_equal 2
2946
+ end
2947
+
2919
2948
  @db.from(jo.keys.as(:k)).select_order_map(:k).must_equal %w'a b'
2920
2949
  @db.from(jo.each).select_order_map(:key).must_equal %w'a b'
2921
2950
  @db.from(jo.each).order(:key).select_map(:value).must_equal [1, {'c'=>2, 'd'=>{'e'=>3}}]
@@ -834,6 +834,35 @@ DatabaseTransactionSpecs = shared_description do
834
834
  @db.in_transaction?.must_equal false
835
835
  end
836
836
 
837
+ it "should have rollback_checker return a proc which returns whether the transaction was rolled back" do
838
+ proc{@db.rollback_checker}.must_raise Sequel::Error
839
+ proc{@db.transaction(:server=>:test){@db.rollback_checker}}.must_raise Sequel::Error
840
+
841
+ rbc = nil
842
+ @db.transaction do
843
+ rbc = @db.rollback_checker
844
+ rbc.call.must_equal nil
845
+ end
846
+ rbc.call.must_equal false
847
+
848
+ @db.transaction(:rollback=>:always) do
849
+ rbc = @db.rollback_checker
850
+ rbc.call.must_equal nil
851
+ end
852
+ rbc.call.must_equal true
853
+
854
+ proc do
855
+ @db.transaction do
856
+ rbc = @db.rollback_checker
857
+ raise
858
+ end
859
+ end.must_raise RuntimeError
860
+ rbc.call.must_equal true
861
+
862
+ @db.transaction(:server=>:test){rbc = @db.rollback_checker(:server=>:test)}
863
+ rbc.call.must_equal false
864
+ end
865
+
837
866
  it "should return nil if Sequel::Rollback is called in the transaction" do
838
867
  @db.transaction{raise Sequel::Rollback}.must_equal nil
839
868
  end
@@ -7,6 +7,37 @@ describe "Sequel Mock Adapter" do
7
7
  db.adapter_scheme.must_equal :mock
8
8
  end
9
9
 
10
+ it "should support registering mock adapter type" do
11
+ begin
12
+ mod = Module.new do
13
+ Sequel::Database.set_shared_adapter_scheme(:foo, self)
14
+
15
+ def self.mock_adapter_setup(db)
16
+ db.instance_variable_set(:@foo, :foo)
17
+ end
18
+
19
+ module self::DatabaseMethods
20
+ def foo
21
+ @foo
22
+ end
23
+ end
24
+
25
+ module self::DatasetMethods
26
+ def foo
27
+ db.foo
28
+ end
29
+ end
30
+ end
31
+
32
+ Sequel.connect('mock://foo') do |db|
33
+ db.foo.must_equal :foo
34
+ db.dataset.foo.must_equal :foo
35
+ end
36
+ ensure
37
+ Sequel.synchronize{Sequel::SHARED_ADAPTER_MAP.delete(:foo)}
38
+ end
39
+ end
40
+
10
41
  it "should have constructor accept no arguments" do
11
42
  Sequel::Mock::Database.new.must_be_kind_of(Sequel::Mock::Database)
12
43
  end
@@ -443,7 +474,7 @@ describe "Sequel Mock Adapter" do
443
474
  end
444
475
  end
445
476
 
446
- it "should automatically set version for adapters nedding versions" do
477
+ it "should automatically set version for adapters needing versions" do
447
478
  Sequel.mock(:host=>'postgres').server_version.must_be :>=, 90400
448
479
  Sequel.mock(:host=>'mssql').server_version.must_be :>=, 11000000
449
480
  Sequel.mock(:host=>'mysql').server_version.must_be :>=, 50617
@@ -319,6 +319,52 @@ describe "Sequel::IntegerMigrator" do
319
319
  @db.dataset.columns.must_equal [:sic]
320
320
  end
321
321
 
322
+ it "should support :relative option for running relative migrations" do
323
+ Sequel::Migrator.run(@db, @dirname, :relative=>2).must_equal 2
324
+ @db.creates.must_equal [1111, 2222]
325
+ @db.version.must_equal 2
326
+ @db.sqls.map{|x| x =~ /\AUPDATE.*(\d+)/ ? $1.to_i : nil}.compact.must_equal [1, 2]
327
+
328
+ Sequel::Migrator.run(@db, @dirname, :relative=>-1).must_equal 1
329
+ @db.drops.must_equal [2222]
330
+ @db.version.must_equal 1
331
+ @db.sqls.map{|x| x =~ /\AUPDATE.*(\d+)/ ? $1.to_i : nil}.compact.must_equal [1]
332
+
333
+ Sequel::Migrator.run(@db, @dirname, :relative=>2).must_equal 3
334
+ @db.creates.must_equal [1111, 2222, 2222, 3333]
335
+ @db.version.must_equal 3
336
+ @db.sqls.map{|x| x =~ /\AUPDATE.*(\d+)/ ? $1.to_i : nil}.compact.must_equal [2, 3]
337
+
338
+ Sequel::Migrator.run(@db, @dirname, :relative=>-3).must_equal 0
339
+ @db.drops.must_equal [2222, 3333, 2222, 1111]
340
+ @db.version.must_equal 0
341
+ @db.sqls.map{|x| x =~ /\AUPDATE.*(\d+)/ ? $1.to_i : nil}.compact.must_equal [2, 1, 0]
342
+ end
343
+
344
+ it "should handle :relative option beyond the upper and lower limit" do
345
+ Sequel::Migrator.run(@db, @dirname, :relative=>100).must_equal 3
346
+ @db.creates.must_equal [1111, 2222, 3333]
347
+ @db.version.must_equal 3
348
+ @db.sqls.map{|x| x =~ /\AUPDATE.*(\d+)/ ? $1.to_i : nil}.compact.must_equal [1, 2, 3]
349
+
350
+ Sequel::Migrator.run(@db, @dirname, :relative=>-200).must_equal 0
351
+ @db.drops.must_equal [3333, 2222, 1111]
352
+ @db.version.must_equal 0
353
+ @db.sqls.map{|x| x =~ /\AUPDATE.*(\d+)/ ? $1.to_i : nil}.compact.must_equal [2, 1, 0]
354
+ end
355
+
356
+ it "should correctly handle migration target versions beyond the upper and lower limits" do
357
+ Sequel::Migrator.run(@db, @dirname, :target=>100).must_equal 3
358
+ @db.creates.must_equal [1111, 2222, 3333]
359
+ @db.version.must_equal 3
360
+ @db.sqls.map{|x| x =~ /\AUPDATE.*(\d+)/ ? $1.to_i : nil}.compact.must_equal [1, 2, 3]
361
+
362
+ Sequel::Migrator.run(@db, @dirname, :target=>-100).must_equal 0
363
+ @db.drops.must_equal [3333, 2222, 1111]
364
+ @db.version.must_equal 0
365
+ @db.sqls.map{|x| x =~ /\AUPDATE.*(\d+)/ ? $1.to_i : nil}.compact.must_equal [2, 1, 0]
366
+ end
367
+
322
368
  it "should apply migrations correctly in the up direction if no target is given" do
323
369
  Sequel::Migrator.apply(@db, @dirname)
324
370
  @db.creates.must_equal [1111, 2222, 3333]
@@ -15,6 +15,7 @@ describe "pg_interval extension" do
15
15
  @db.literal(ActiveSupport::Duration.new(0, [])).must_equal "'0'::interval"
16
16
  @db.literal(ActiveSupport::Duration.new(0, [[:seconds, 0]])).must_equal "'0'::interval"
17
17
  @db.literal(ActiveSupport::Duration.new(0, [[:seconds, 10], [:minutes, 20], [:days, 3], [:months, 4], [:years, 6]])).must_equal "'6 years 4 months 3 days 20 minutes 10 seconds '::interval"
18
+ @db.literal(ActiveSupport::Duration.new(0, [[:seconds, 10], [:minutes, 20], [:hours, 8], [:days, 3], [:weeks, 2], [:months, 4], [:years, 6]])).must_equal "'6 years 4 months 2 weeks 3 days 8 hours 20 minutes 10 seconds '::interval"
18
19
  @db.literal(ActiveSupport::Duration.new(0, [[:seconds, -10.000001], [:minutes, -20], [:days, -3], [:months, -4], [:years, -6]])).must_equal "'-6 years -4 months -3 days -20 minutes -10.000001 seconds '::interval"
19
20
  end
20
21
 
@@ -190,6 +190,19 @@ describe "Sequel::Postgres::JSONOp" do
190
190
  @l[@jb.concat([1, 2])].must_equal "(j || '[1,2]'::jsonb)"
191
191
  end
192
192
 
193
+ it "#insert should use the jsonb_insert function" do
194
+ @l[@jb.insert(:a, :h)].must_equal "jsonb_insert(j, a, h, false)"
195
+ @l[@jb.insert(:a, :h, true)].must_equal "jsonb_insert(j, a, h, true)"
196
+ end
197
+
198
+ it "#insert should handle hashes" do
199
+ @l[@jb.insert(:a, 'a'=>'b')].must_equal "jsonb_insert(j, a, '{\"a\":\"b\"}'::jsonb, false)"
200
+ end
201
+
202
+ it "#insert should handle arrays" do
203
+ @l[@jb.insert(%w'a b', [1, 2])].must_equal "jsonb_insert(j, ARRAY['a','b'], '[1,2]'::jsonb, false)"
204
+ end
205
+
193
206
  it "#set should use the jsonb_set function" do
194
207
  @l[@jb.set(:a, :h)].must_equal "jsonb_set(j, a, h, true)"
195
208
  @l[@jb.set(:a, :h, false)].must_equal "jsonb_set(j, a, h, false)"
@@ -50,6 +50,17 @@ describe "Sequel::Plugins::Timestamps" do
50
50
  o.updated_at.must_equal '2009-08-01'
51
51
  end
52
52
 
53
+ it "should leave manually set update timestamp, if :allow_manual_update was given" do
54
+ o = @c.load(:id=>1).update(:updated_at=>Date.new(2016))
55
+ @c.db.sqls.must_equal ["UPDATE t SET updated_at = '2009-08-01' WHERE (id = 1)"]
56
+ o.updated_at.must_equal '2009-08-01'
57
+
58
+ @c.plugin :timestamps, :allow_manual_update=>true
59
+ o = @c.load(:id=>1).update(:updated_at=>Date.new(2016))
60
+ @c.db.sqls.must_equal ["UPDATE t SET updated_at = '2016-01-01' WHERE (id = 1)"]
61
+ o.updated_at.must_equal Date.new(2016)
62
+ end
63
+
53
64
  it "should work with current_datetime_timestamp extension" do
54
65
  Sequel.datetime_class = Time
55
66
  @c.dataset = @c.dataset.extension(:current_datetime_timestamp)
@@ -127,6 +127,14 @@ describe "Touch plugin" do
127
127
  end
128
128
 
129
129
  it "should be able to touch many_to_one associations" do
130
+ @Album.plugin :touch, :associations=>:artist
131
+ @Album.plugin :skip_create_refresh
132
+ @Album.create(:artist_id=>4)
133
+ DB.sqls.must_equal ["INSERT INTO albums (artist_id) VALUES (4)",
134
+ "UPDATE artists SET updated_at = CURRENT_TIMESTAMP WHERE (artists.id = 4)"]
135
+ end
136
+
137
+ it "should be able to touch one_to_one associations" do
130
138
  @Artist.one_to_one :album, :class=>@Album, :key=>:artist_id
131
139
  @Artist.plugin :touch, :associations=>:album
132
140
  @a.touch