sequel 3.38.0 → 3.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 (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
@@ -18,6 +18,7 @@ describe "An SQLite database" do
18
18
  @fk = @db.foreign_keys
19
19
  end
20
20
  after do
21
+ @db.drop_table?(:fk)
21
22
  @db.foreign_keys = @fk
22
23
  @db.case_sensitive_like = true
23
24
  Sequel.datetime_class = Time
@@ -74,16 +75,14 @@ describe "An SQLite database" do
74
75
  @db.foreign_keys = false
75
76
  end
76
77
 
77
- if SQLITE_DB.sqlite_version >= 30619
78
- specify "should enforce foreign key integrity if foreign_keys pragma is set" do
79
- @db.foreign_keys = true
80
- @db.create_table!(:fk){primary_key :id; foreign_key :parent_id, :fk}
81
- @db[:fk].insert(1, nil)
82
- @db[:fk].insert(2, 1)
83
- @db[:fk].insert(3, 3)
84
- proc{@db[:fk].insert(4, 5)}.should raise_error(Sequel::Error)
85
- end
86
- end
78
+ specify "should enforce foreign key integrity if foreign_keys pragma is set" do
79
+ @db.foreign_keys = true
80
+ @db.create_table!(:fk){primary_key :id; foreign_key :parent_id, :fk}
81
+ @db[:fk].insert(1, nil)
82
+ @db[:fk].insert(2, 1)
83
+ @db[:fk].insert(3, 3)
84
+ proc{@db[:fk].insert(4, 5)}.should raise_error(Sequel::Error)
85
+ end if SQLITE_DB.sqlite_version >= 30619
87
86
 
88
87
  specify "should not enforce foreign key integrity if foreign_keys pragma is unset" do
89
88
  @db.foreign_keys = false
@@ -93,24 +92,22 @@ describe "An SQLite database" do
93
92
  end
94
93
 
95
94
  specify "should support a use_timestamp_timezones setting" do
96
- @db.create_table!(:time){Time :time}
97
- @db[:time].insert(Time.now)
98
- @db[:time].get(Sequel.cast(:time, String)).should =~ /[-+]\d\d\d\d\z/
95
+ @db.create_table!(:fk){Time :time}
96
+ @db[:fk].insert(Time.now)
97
+ @db[:fk].get(Sequel.cast(:time, String)).should =~ /[-+]\d\d\d\d\z/
99
98
  @db.use_timestamp_timezones = false
100
- @db[:time].delete
101
- @db[:time].insert(Time.now)
102
- @db[:time].get(Sequel.cast(:time, String)).should_not =~ /[-+]\d\d\d\d\z/
99
+ @db[:fk].delete
100
+ @db[:fk].insert(Time.now)
101
+ @db[:fk].get(Sequel.cast(:time, String)).should_not =~ /[-+]\d\d\d\d\z/
103
102
  @db.use_timestamp_timezones = true
104
103
  end
105
104
 
106
105
  specify "should provide a list of existing tables" do
107
- @db.drop_table?(:testing)
106
+ @db.drop_table?(:fk)
108
107
  @db.tables.should be_a_kind_of(Array)
109
- @db.tables.should_not include(:testing)
110
- @db.create_table! :testing do
111
- text :name
112
- end
113
- @db.tables.should include(:testing)
108
+ @db.tables.should_not include(:fk)
109
+ @db.create_table!(:fk){String :name}
110
+ @db.tables.should include(:fk)
114
111
  end
115
112
 
116
113
  specify "should support getting and setting the synchronous pragma" do
@@ -136,23 +133,23 @@ describe "An SQLite database" do
136
133
  end
137
134
 
138
135
  cspecify "should support timestamps and datetimes and respect datetime_class", :do, :jdbc, :amalgalite, :swift do
139
- @db.create_table!(:time){timestamp :t; datetime :d}
136
+ @db.create_table!(:fk){timestamp :t; datetime :d}
140
137
  t1 = Time.at(1)
141
- @db[:time] << {:t => t1, :d => t1}
142
- @db[:time].map(:t).should == [t1]
143
- @db[:time].map(:d).should == [t1]
138
+ @db[:fk] << {:t => t1, :d => t1}
139
+ @db[:fk].map(:t).should == [t1]
140
+ @db[:fk].map(:d).should == [t1]
144
141
  Sequel.datetime_class = DateTime
145
142
  t2 = Sequel.string_to_datetime(t1.iso8601)
146
- @db[:time].map(:t).should == [t2]
147
- @db[:time].map(:d).should == [t2]
143
+ @db[:fk].map(:t).should == [t2]
144
+ @db[:fk].map(:d).should == [t2]
148
145
  end
149
146
 
150
147
  specify "should support sequential primary keys" do
151
- @db.create_table!(:with_pk) {primary_key :id; text :name}
152
- @db[:with_pk] << {:name => 'abc'}
153
- @db[:with_pk] << {:name => 'def'}
154
- @db[:with_pk] << {:name => 'ghi'}
155
- @db[:with_pk].order(:name).all.should == [
148
+ @db.create_table!(:fk) {primary_key :id; text :name}
149
+ @db[:fk] << {:name => 'abc'}
150
+ @db[:fk] << {:name => 'def'}
151
+ @db[:fk] << {:name => 'ghi'}
152
+ @db[:fk].order(:name).all.should == [
156
153
  {:id => 1, :name => 'abc'},
157
154
  {:id => 2, :name => 'def'},
158
155
  {:id => 3, :name => 'ghi'}
@@ -160,8 +157,21 @@ describe "An SQLite database" do
160
157
  end
161
158
 
162
159
  specify "should correctly parse the schema" do
163
- @db.create_table!(:time2) {timestamp :t}
164
- @db.schema(:time2, :reload=>true).should == [[:t, {:type=>:datetime, :allow_null=>true, :default=>nil, :ruby_default=>nil, :db_type=>"timestamp", :primary_key=>false}]]
160
+ @db.create_table!(:fk) {timestamp :t}
161
+ @db.schema(:fk, :reload=>true).should == [[:t, {:type=>:datetime, :allow_null=>true, :default=>nil, :ruby_default=>nil, :db_type=>"timestamp", :primary_key=>false}]]
162
+ end
163
+
164
+ specify "should handle and return BigDecimal values for numeric columns" do
165
+ SQLITE_DB.create_table!(:fk){numeric :d}
166
+ d = SQLITE_DB[:fk]
167
+ d.insert(:d=>BigDecimal.new('80.0'))
168
+ d.insert(:d=>BigDecimal.new('NaN'))
169
+ d.insert(:d=>BigDecimal.new('Infinity'))
170
+ d.insert(:d=>BigDecimal.new('-Infinity'))
171
+ ds = d.all
172
+ ds.shift.should == {:d=>BigDecimal.new('80.0')}
173
+ ds.map{|x| x[:d].to_s}.should == %w'NaN Infinity -Infinity'
174
+ SQLITE_DB
165
175
  end
166
176
  end
167
177
 
@@ -265,20 +275,6 @@ describe "An SQLite dataset" do
265
275
  end
266
276
  end
267
277
 
268
- describe "An SQLite numeric column" do
269
- specify "should handle and return BigDecimal values" do
270
- SQLITE_DB.create_table!(:d){numeric :d}
271
- d = SQLITE_DB[:d]
272
- d.insert(:d=>BigDecimal.new('80.0'))
273
- d.insert(:d=>BigDecimal.new('NaN'))
274
- d.insert(:d=>BigDecimal.new('Infinity'))
275
- d.insert(:d=>BigDecimal.new('-Infinity'))
276
- ds = d.all
277
- ds.shift.should == {:d=>BigDecimal.new('80.0')}
278
- ds.map{|x| x[:d].to_s}.should == %w'NaN Infinity -Infinity'
279
- end
280
- end
281
-
282
278
  describe "An SQLite dataset AS clause" do
283
279
  specify "should use a string literal for :col___alias" do
284
280
  SQLITE_DB.literal(:c___a).should == "`c` AS 'a'"
@@ -407,6 +403,9 @@ describe "A SQLite database" do
407
403
  integer :value
408
404
  end
409
405
  end
406
+ after do
407
+ @db.drop_table?(:test2)
408
+ end
410
409
 
411
410
  specify "should support add_column operations" do
412
411
  @db.add_column :test2, :xyz, :text
@@ -533,6 +532,7 @@ describe "A SQLite database" do
533
532
  @db.from(table_name).all.should == [{:"s s"=>1}]
534
533
  @db.rename_column table_name, :"s s", :"t t"
535
534
  @db.from(table_name).all.should == [{:"t t"=>1}]
535
+ @db.drop_table?(table_name)
536
536
  end
537
537
 
538
538
  specify "should choose a temporary table name that isn't already used when dropping or renaming columns" do
@@ -574,6 +574,7 @@ describe "A SQLite database" do
574
574
  @db[:test3_backup1].columns.should == [:k]
575
575
  @db[:test3_backup2].columns.should == [:l]
576
576
  @db.loggers.delete(l)
577
+ @db.drop_table?(:test3, :test3_backup0, :test3_backup1, :test3_backup2)
577
578
  end
578
579
 
579
580
  specify "should support add_index" do
@@ -365,6 +365,28 @@ shared_examples_for "A threaded connection pool" do
365
365
  Thread.new{@pool.hold{|cc2| cc2.should == c}}
366
366
  end
367
367
  end
368
+
369
+ specify "should not store connections if :connection_handling=>:disconnect" do
370
+ @pool = Sequel::ConnectionPool.get_pool(@cp_opts.merge(:connection_handling=>:disconnect)){@invoked_count += 1}
371
+ d = []
372
+ @pool.disconnection_proc = proc{|c| d << c}
373
+ c = @pool.hold do |cc|
374
+ cc.should == 1
375
+ Thread.new{@pool.hold{|cc2| cc2.should == 2}}.join
376
+ d.should == [2]
377
+ @pool.hold{|cc3| cc3.should == 1}
378
+ end
379
+ @pool.size.should == 0
380
+ d.should == [2, 1]
381
+
382
+ @pool.hold{|cc| cc.should == 3}
383
+ @pool.size.should == 0
384
+ d.should == [2, 1, 3]
385
+
386
+ @pool.hold{|cc| cc.should == 4}
387
+ @pool.size.should == 0
388
+ d.should == [2, 1, 3, 4]
389
+ end
368
390
  end
369
391
 
370
392
  describe "Threaded Unsharded Connection Pool" do
@@ -1726,16 +1726,17 @@ describe "Database#typecast_value" do
1726
1726
  end
1727
1727
 
1728
1728
  specify "should typecast datetime values to Sequel.datetime_class with correct timezone handling" do
1729
- t = Time.utc(2011, 1, 2, 3, 4, 5) # UTC Time
1730
- t2 = Time.mktime(2011, 1, 2, 3, 4, 5) # Local Time
1731
- t3 = Time.utc(2011, 1, 2, 3, 4, 5) - (t - t2) # Local Time in UTC Time
1732
- t4 = Time.mktime(2011, 1, 2, 3, 4, 5) + (t - t2) # UTC Time in Local Time
1733
- dt = DateTime.civil(2011, 1, 2, 3, 4, 5)
1729
+ t = Time.utc(2011, 1, 2, 3, 4, 5, 500000) # UTC Time
1730
+ t2 = Time.mktime(2011, 1, 2, 3, 4, 5, 500000) # Local Time
1731
+ t3 = Time.utc(2011, 1, 2, 3, 4, 5, 500000) - (t - t2) # Local Time in UTC Time
1732
+ t4 = Time.mktime(2011, 1, 2, 3, 4, 5, 500000) + (t - t2) # UTC Time in Local Time
1733
+ secs = defined?(Rational) ? Rational(11, 2) : 5.5
1734
1734
  r1 = defined?(Rational) ? Rational(t2.utc_offset, 86400) : t2.utc_offset/86400.0
1735
1735
  r2 = defined?(Rational) ? Rational((t - t2).to_i, 86400) : (t - t2).to_i/86400.0
1736
- dt2 = DateTime.civil(2011, 1, 2, 3, 4, 5, r1)
1737
- dt3 = DateTime.civil(2011, 1, 2, 3, 4, 5) - r2
1738
- dt4 = DateTime.civil(2011, 1, 2, 3, 4, 5, r1) + r2
1736
+ dt = DateTime.civil(2011, 1, 2, 3, 4, secs)
1737
+ dt2 = DateTime.civil(2011, 1, 2, 3, 4, secs, r1)
1738
+ dt3 = DateTime.civil(2011, 1, 2, 3, 4, secs) - r2
1739
+ dt4 = DateTime.civil(2011, 1, 2, 3, 4, secs, r1) + r2
1739
1740
 
1740
1741
  t.should == t4
1741
1742
  t2.should == t3
@@ -1751,25 +1752,26 @@ describe "Database#typecast_value" do
1751
1752
  v.offset.should == o.offset
1752
1753
  end
1753
1754
  end
1755
+ @db.extend_datasets(Module.new{def supports_timestamp_timezones?; true; end})
1754
1756
  begin
1755
1757
  @db.typecast_value(:datetime, dt).should == t
1756
1758
  @db.typecast_value(:datetime, dt2).should == t2
1757
1759
  @db.typecast_value(:datetime, t).should == t
1758
1760
  @db.typecast_value(:datetime, t2).should == t2
1759
- @db.typecast_value(:datetime, dt.to_s).should == t
1760
- @db.typecast_value(:datetime, dt.strftime('%F %T')).should == t2
1761
+ @db.typecast_value(:datetime, @db.literal(dt)[1...-1]).should == t
1762
+ @db.typecast_value(:datetime, dt.strftime('%F %T.%N')).should == t2
1761
1763
  @db.typecast_value(:datetime, Date.civil(2011, 1, 2)).should == Time.mktime(2011, 1, 2, 0, 0, 0)
1762
- @db.typecast_value(:datetime, :year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec).should == t2
1764
+ @db.typecast_value(:datetime, :year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000).should == t2
1763
1765
 
1764
1766
  Sequel.datetime_class = DateTime
1765
1767
  @db.typecast_value(:datetime, dt).should == dt
1766
1768
  @db.typecast_value(:datetime, dt2).should == dt2
1767
1769
  @db.typecast_value(:datetime, t).should == dt
1768
1770
  @db.typecast_value(:datetime, t2).should == dt2
1769
- @db.typecast_value(:datetime, dt.to_s).should == dt
1770
- @db.typecast_value(:datetime, dt.strftime('%F %T')).should == dt
1771
+ @db.typecast_value(:datetime, @db.literal(dt)[1...-1]).should == dt
1772
+ @db.typecast_value(:datetime, dt.strftime('%F %T.%N')).should == dt
1771
1773
  @db.typecast_value(:datetime, Date.civil(2011, 1, 2)).should == DateTime.civil(2011, 1, 2, 0, 0, 0)
1772
- @db.typecast_value(:datetime, :year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec).should == dt
1774
+ @db.typecast_value(:datetime, :year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000).should == dt
1773
1775
 
1774
1776
  Sequel.application_timezone = :utc
1775
1777
  Sequel.typecast_timezone = :local
@@ -1778,20 +1780,20 @@ describe "Database#typecast_value" do
1778
1780
  check[dt2, t3]
1779
1781
  check[t, t]
1780
1782
  check[t2, t3]
1781
- check[dt.to_s, t]
1782
- check[dt.strftime('%F %T'), t3]
1783
+ check[@db.literal(dt)[1...-1], t]
1784
+ check[dt.strftime('%F %T.%N'), t3]
1783
1785
  check[Date.civil(2011, 1, 2), Time.utc(2011, 1, 2, 0, 0, 0)]
1784
- check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, t3]
1786
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000}, t3]
1785
1787
 
1786
1788
  Sequel.datetime_class = DateTime
1787
1789
  check[dt, dt]
1788
1790
  check[dt2, dt3]
1789
1791
  check[t, dt]
1790
1792
  check[t2, dt3]
1791
- check[dt.to_s, dt]
1792
- check[dt.strftime('%F %T'), dt3]
1793
+ check[@db.literal(dt)[1...-1], dt]
1794
+ check[dt.strftime('%F %T.%N'), dt3]
1793
1795
  check[Date.civil(2011, 1, 2), DateTime.civil(2011, 1, 2, 0, 0, 0)]
1794
- check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, dt3]
1796
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000}, dt3]
1795
1797
 
1796
1798
  Sequel.typecast_timezone = :utc
1797
1799
  Sequel.datetime_class = Time
@@ -1799,20 +1801,20 @@ describe "Database#typecast_value" do
1799
1801
  check[dt2, t3]
1800
1802
  check[t, t]
1801
1803
  check[t2, t3]
1802
- check[dt.to_s, t]
1803
- check[dt.strftime('%F %T'), t]
1804
+ check[@db.literal(dt)[1...-1], t]
1805
+ check[dt.strftime('%F %T.%N'), t]
1804
1806
  check[Date.civil(2011, 1, 2), Time.utc(2011, 1, 2, 0, 0, 0)]
1805
- check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, t]
1807
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000}, t]
1806
1808
 
1807
1809
  Sequel.datetime_class = DateTime
1808
1810
  check[dt, dt]
1809
1811
  check[dt2, dt3]
1810
1812
  check[t, dt]
1811
1813
  check[t2, dt3]
1812
- check[dt.to_s, dt]
1813
- check[dt.strftime('%F %T'), dt]
1814
+ check[@db.literal(dt)[1...-1], dt]
1815
+ check[dt.strftime('%F %T.%N'), dt]
1814
1816
  check[Date.civil(2011, 1, 2), DateTime.civil(2011, 1, 2, 0, 0, 0)]
1815
- check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, dt]
1817
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000}, dt]
1816
1818
 
1817
1819
  Sequel.application_timezone = :local
1818
1820
  Sequel.datetime_class = Time
@@ -1820,20 +1822,20 @@ describe "Database#typecast_value" do
1820
1822
  check[dt2, t2]
1821
1823
  check[t, t4]
1822
1824
  check[t2, t2]
1823
- check[dt.to_s, t4]
1824
- check[dt.strftime('%F %T'), t4]
1825
+ check[@db.literal(dt)[1...-1], t4]
1826
+ check[dt.strftime('%F %T.%N'), t4]
1825
1827
  check[Date.civil(2011, 1, 2), Time.local(2011, 1, 2, 0, 0, 0)]
1826
- check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, t4]
1828
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000}, t4]
1827
1829
 
1828
1830
  Sequel.datetime_class = DateTime
1829
1831
  check[dt, dt4]
1830
1832
  check[dt2, dt2]
1831
1833
  check[t, dt4]
1832
1834
  check[t2, dt2]
1833
- check[dt.to_s, dt4]
1834
- check[dt.strftime('%F %T'), dt4]
1835
+ check[@db.literal(dt)[1...-1], dt4]
1836
+ check[dt.strftime('%F %T.%N'), dt4]
1835
1837
  check[Date.civil(2011, 1, 2), DateTime.civil(2011, 1, 2, 0, 0, 0, r1)]
1836
- check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, dt4]
1838
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000}, dt4]
1837
1839
 
1838
1840
  Sequel.typecast_timezone = :local
1839
1841
  Sequel.datetime_class = Time
@@ -1841,20 +1843,20 @@ describe "Database#typecast_value" do
1841
1843
  check[dt2, t2]
1842
1844
  check[t, t4]
1843
1845
  check[t2, t2]
1844
- check[dt.to_s, t4]
1845
- check[dt.strftime('%F %T'), t2]
1846
+ check[@db.literal(dt)[1...-1], t4]
1847
+ check[dt.strftime('%F %T.%N'), t2]
1846
1848
  check[Date.civil(2011, 1, 2), Time.local(2011, 1, 2, 0, 0, 0)]
1847
- check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, t2]
1849
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000}, t2]
1848
1850
 
1849
1851
  Sequel.datetime_class = DateTime
1850
1852
  check[dt, dt4]
1851
1853
  check[dt2, dt2]
1852
1854
  check[t, dt4]
1853
1855
  check[t2, dt2]
1854
- check[dt.to_s, dt4]
1855
- check[dt.strftime('%F %T'), dt2]
1856
+ check[@db.literal(dt)[1...-1], dt4]
1857
+ check[dt.strftime('%F %T.%N'), dt2]
1856
1858
  check[Date.civil(2011, 1, 2), DateTime.civil(2011, 1, 2, 0, 0, 0, r1)]
1857
- check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, dt2]
1859
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec, :nanos=>500000000}, dt2]
1858
1860
 
1859
1861
  ensure
1860
1862
  Sequel.default_timezone = nil
@@ -2063,8 +2065,7 @@ end
2063
2065
  describe "Database#column_schema_to_ruby_default" do
2064
2066
  specify "should handle converting many default formats" do
2065
2067
  db = Sequel::Database.new
2066
- m = db.method(:column_schema_to_ruby_default)
2067
- p = lambda{|d,t| m.call(d,t)}
2068
+ p = lambda{|d,t| db.send(:column_schema_to_ruby_default, d, t)}
2068
2069
  p[nil, :integer].should be_nil
2069
2070
  p['1', :integer].should == 1
2070
2071
  p['-1', :integer].should == -1
@@ -2085,14 +2086,20 @@ describe "Database#column_schema_to_ruby_default" do
2085
2086
  p["'\\a''b'", :string].should == "\\a'b"
2086
2087
  p["'NULL'", :string].should == "NULL"
2087
2088
  p["'2009-10-29'", :date].should == Date.new(2009,10,29)
2088
- p["CURRENT_TIMESTAMP", :date].should be_nil
2089
- p["today()", :date].should be_nil
2089
+ p["CURRENT_TIMESTAMP", :date].should == Sequel::CURRENT_DATE
2090
+ p["CURRENT_DATE", :date].should == Sequel::CURRENT_DATE
2091
+ p["now()", :date].should == Sequel::CURRENT_DATE
2092
+ p["getdate()", :date].should == Sequel::CURRENT_DATE
2093
+ p["CURRENT_TIMESTAMP", :datetime].should == Sequel::CURRENT_TIMESTAMP
2094
+ p["CURRENT_DATE", :datetime].should == Sequel::CURRENT_TIMESTAMP
2095
+ p["now()", :datetime].should == Sequel::CURRENT_TIMESTAMP
2096
+ p["getdate()", :datetime].should == Sequel::CURRENT_TIMESTAMP
2090
2097
  p["'2009-10-29T10:20:30-07:00'", :datetime].should == DateTime.parse('2009-10-29T10:20:30-07:00')
2091
2098
  p["'2009-10-29 10:20:30'", :datetime].should == DateTime.parse('2009-10-29 10:20:30')
2092
2099
  p["'10:20:30'", :time].should == Time.parse('10:20:30')
2093
2100
  p["NaN", :float].should be_nil
2094
2101
 
2095
- db.meta_def(:database_type){:postgres}
2102
+ db = Sequel.mock(:host=>'postgres')
2096
2103
  p["''::text", :string].should == ""
2097
2104
  p["'\\a''b'::character varying", :string].should == "\\a'b"
2098
2105
  p["'a'::bpchar", :string].should == "a"
@@ -2105,7 +2112,7 @@ describe "Database#column_schema_to_ruby_default" do
2105
2112
  p["'2009-10-29 10:20:30.241343'::timestamp without time zone", :datetime].should == DateTime.parse('2009-10-29 10:20:30.241343')
2106
2113
  p["'10:20:30'::time without time zone", :time].should == Time.parse('10:20:30')
2107
2114
 
2108
- db.meta_def(:database_type){:mysql}
2115
+ db = Sequel.mock(:host=>'mysql')
2109
2116
  p["\\a'b", :string].should == "\\a'b"
2110
2117
  p["a", :string].should == "a"
2111
2118
  p["NULL", :string].should == "NULL"
@@ -2114,11 +2121,10 @@ describe "Database#column_schema_to_ruby_default" do
2114
2121
  p["2009-10-29", :date].should == Date.new(2009,10,29)
2115
2122
  p["2009-10-29 10:20:30", :datetime].should == DateTime.parse('2009-10-29 10:20:30')
2116
2123
  p["10:20:30", :time].should == Time.parse('10:20:30')
2117
- p["CURRENT_DATE", :date].should be_nil
2118
- p["CURRENT_TIMESTAMP", :datetime].should be_nil
2119
2124
  p["a", :enum].should == "a"
2125
+ p["a,b", :set].should == "a,b"
2120
2126
 
2121
- db.meta_def(:database_type){:mssql}
2127
+ db = Sequel.mock(:host=>'mssql')
2122
2128
  p["(N'a')", :string].should == "a"
2123
2129
  p["((-12))", :integer].should == -12
2124
2130
  p["((12.1))", :float].should == 12.1
@@ -241,16 +241,6 @@ describe "A simple dataset" do
241
241
  should match(/INSERT INTO test \(name, price\) VALUES \('wxyz', 342\)|INSERT INTO test \(price, name\) VALUES \(342, 'wxyz'\)/)
242
242
  end
243
243
 
244
- specify "should format an insert statement with an object that respond_to? :values" do
245
- v = Object.new
246
- def v.values; {:a => 1}; end
247
-
248
- @dataset.insert_sql(v).should == "INSERT INTO test (a) VALUES (1)"
249
-
250
- def v.values; {}; end
251
- @dataset.insert_sql(v).should == "INSERT INTO test DEFAULT VALUES"
252
- end
253
-
254
244
  specify "should format an insert statement with an arbitrary value" do
255
245
  @dataset.insert_sql(123).should == "INSERT INTO test VALUES (123)"
256
246
  end
@@ -338,7 +328,7 @@ end
338
328
 
339
329
  describe "Dataset#exists" do
340
330
  before do
341
- @ds1 = Sequel::Dataset.new(nil).from(:test)
331
+ @ds1 = Sequel.mock[:test]
342
332
  @ds2 = @ds1.filter(Sequel.expr(:price) < 100)
343
333
  @ds3 = @ds1.filter(Sequel.expr(:price) > 50)
344
334
  end
@@ -358,7 +348,7 @@ end
358
348
 
359
349
  describe "Dataset#where" do
360
350
  before do
361
- @dataset = Sequel::Dataset.new(nil).from(:test)
351
+ @dataset = Sequel.mock[:test]
362
352
  @d1 = @dataset.where(:region => 'Asia')
363
353
  @d2 = @dataset.where('region = ?', 'Asia')
364
354
  @d3 = @dataset.where("a = 1")
@@ -1836,6 +1826,25 @@ describe "Dataset#count" do
1836
1826
  @db.sqls.should == ['SELECT COUNT(*) AS count FROM test LIMIT 1']
1837
1827
  end
1838
1828
 
1829
+ specify "should accept an argument" do
1830
+ @dataset.count(:foo).should == 1
1831
+ @db.sqls.should == ['SELECT COUNT(foo) AS count FROM test LIMIT 1']
1832
+ end
1833
+
1834
+ specify "should work with a nil argument" do
1835
+ @dataset.count(nil).should == 1
1836
+ @db.sqls.should == ['SELECT COUNT(NULL) AS count FROM test LIMIT 1']
1837
+ end
1838
+
1839
+ specify "should accept a virtual row block" do
1840
+ @dataset.count{foo(bar)}.should == 1
1841
+ @db.sqls.should == ['SELECT COUNT(foo(bar)) AS count FROM test LIMIT 1']
1842
+ end
1843
+
1844
+ specify "should raise an Error if given an argument and a block" do
1845
+ proc{@dataset.count(:foo){foo(bar)}}.should raise_error(Sequel::Error)
1846
+ end
1847
+
1839
1848
  specify "should include the where clause if it's there" do
1840
1849
  @dataset.filter(Sequel.expr(:abc) < 30).count.should == 1
1841
1850
  @db.sqls.should == ['SELECT COUNT(*) AS count FROM test WHERE (abc < 30) LIMIT 1']
@@ -2159,12 +2168,6 @@ describe "Dataset#join_table" do
2159
2168
  'RIGHT OUTER JOIN (SELECT * FROM attributes WHERE (name = \'blah\')) AS "t3" ON ("t3"."attribute_id" = "t2"."id")'
2160
2169
  end
2161
2170
 
2162
- specify "should support joining objects that respond to :table_name" do
2163
- ds = Object.new
2164
- def ds.table_name; :categories end
2165
- @d.join(ds, :item_id => :id).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."item_id" = "items"."id")'
2166
- end
2167
-
2168
2171
  specify "should support using an SQL String as the join condition" do
2169
2172
  @d.join(:categories, "c.item_id = items.id", :c).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" AS "c" ON (c.item_id = items.id)'
2170
2173
  end
@@ -2323,6 +2326,13 @@ describe "Dataset#insert_multiple" do
2323
2326
  specify "should return array of inserted ids" do
2324
2327
  @ds.insert_multiple(['aa', 5, 3, {:a => 2}]).should == [2, 3, 4, 5]
2325
2328
  end
2329
+
2330
+ specify "should work exactly like in metioned in the example" do
2331
+ @ds.insert_multiple([{:x=>1}, {:x=>2}]){|row| row[:y] = row[:x] * 2 ; row }
2332
+ sqls = @db.sqls
2333
+ ["INSERT INTO items (x, y) VALUES (1, 2)", "INSERT INTO items (y, x) VALUES (2, 1)"].should include(sqls[0])
2334
+ ["INSERT INTO items (x, y) VALUES (2, 4)", "INSERT INTO items (y, x) VALUES (4, 2)"].should include(sqls[1])
2335
+ end
2326
2336
  end
2327
2337
 
2328
2338
  describe "Dataset aggregate methods" do
@@ -3055,18 +3065,6 @@ describe "Dataset#insert_sql" do
3055
3065
  specify "should accept an array of columns and an LiteralString" do
3056
3066
  @ds.insert_sql([:a, :b, :c], Sequel.lit('VALUES (1, 2, 3)')).should == "INSERT INTO items (a, b, c) VALUES (1, 2, 3)"
3057
3067
  end
3058
-
3059
- specify "should accept an object that responds to values and returns a hash by using that hash as the columns and values" do
3060
- o = Object.new
3061
- def o.values; {:c=>'d'}; end
3062
- @ds.insert_sql(o).should == "INSERT INTO items (c) VALUES ('d')"
3063
- end
3064
-
3065
- specify "should accept an object that responds to values and returns something other than a hash by using the object itself as a single value" do
3066
- o = Date.civil(2000, 1, 1)
3067
- def o.values; self; end
3068
- @ds.insert_sql(o).should == "INSERT INTO items VALUES ('2000-01-01')"
3069
- end
3070
3068
  end
3071
3069
 
3072
3070
  describe "Dataset#inspect" do
@@ -3108,7 +3106,7 @@ end
3108
3106
 
3109
3107
  describe "Dataset#grep" do
3110
3108
  before do
3111
- @ds = Sequel::Dataset.new(nil).from(:posts)
3109
+ @ds = Sequel.mock[:posts]
3112
3110
  end
3113
3111
 
3114
3112
  specify "should format a SQL filter correctly" do
@@ -3155,7 +3153,13 @@ describe "Dataset#grep" do
3155
3153
  @ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :all_columns=>true, :case_insensitive=>true).sql.should == "SELECT * FROM posts WHERE ((title ILIKE 'abc') AND (body ILIKE 'abc') AND (title ILIKE 'def') AND (body ILIKE 'def'))"
3156
3154
  end
3157
3155
 
3158
- specify "should support regexps though the database may not support it" do
3156
+ specify "should not support regexps if the database doesn't supports it" do
3157
+ proc{@ds.grep(:title, /ruby/).sql}.should raise_error(Sequel::InvalidOperation)
3158
+ proc{@ds.grep(:title, [/^ruby/, 'ruby']).sql}.should raise_error(Sequel::InvalidOperation)
3159
+ end
3160
+
3161
+ specify "should support regexps if the database supports it" do
3162
+ def @ds.supports_regexp?; true end
3159
3163
  @ds.grep(:title, /ruby/).sql.should == "SELECT * FROM posts WHERE ((title ~ 'ruby'))"
3160
3164
  @ds.grep(:title, [/^ruby/, 'ruby']).sql.should == "SELECT * FROM posts WHERE ((title ~ '^ruby') OR (title LIKE 'ruby'))"
3161
3165
  end