sequel 4.38.0 → 4.39.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 (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