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
@@ -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