sequel 3.1.0 → 3.2.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 (65) hide show
  1. data/CHANGELOG +76 -0
  2. data/Rakefile +2 -2
  3. data/bin/sequel +9 -4
  4. data/doc/opening_databases.rdoc +279 -0
  5. data/doc/release_notes/3.2.0.txt +268 -0
  6. data/doc/virtual_rows.rdoc +42 -51
  7. data/lib/sequel/adapters/ado.rb +2 -5
  8. data/lib/sequel/adapters/db2.rb +5 -0
  9. data/lib/sequel/adapters/do.rb +3 -0
  10. data/lib/sequel/adapters/firebird.rb +6 -4
  11. data/lib/sequel/adapters/informix.rb +5 -3
  12. data/lib/sequel/adapters/jdbc.rb +10 -8
  13. data/lib/sequel/adapters/jdbc/h2.rb +17 -4
  14. data/lib/sequel/adapters/mysql.rb +6 -19
  15. data/lib/sequel/adapters/odbc.rb +14 -18
  16. data/lib/sequel/adapters/openbase.rb +8 -0
  17. data/lib/sequel/adapters/shared/mssql.rb +14 -8
  18. data/lib/sequel/adapters/shared/mysql.rb +53 -28
  19. data/lib/sequel/adapters/shared/oracle.rb +21 -12
  20. data/lib/sequel/adapters/shared/postgres.rb +46 -26
  21. data/lib/sequel/adapters/shared/progress.rb +10 -5
  22. data/lib/sequel/adapters/shared/sqlite.rb +28 -12
  23. data/lib/sequel/adapters/sqlite.rb +4 -3
  24. data/lib/sequel/adapters/utils/stored_procedures.rb +18 -9
  25. data/lib/sequel/connection_pool.rb +4 -3
  26. data/lib/sequel/database.rb +110 -10
  27. data/lib/sequel/database/schema_sql.rb +12 -3
  28. data/lib/sequel/dataset.rb +40 -3
  29. data/lib/sequel/dataset/convenience.rb +0 -11
  30. data/lib/sequel/dataset/graph.rb +25 -11
  31. data/lib/sequel/dataset/sql.rb +176 -68
  32. data/lib/sequel/extensions/migration.rb +37 -21
  33. data/lib/sequel/extensions/schema_dumper.rb +8 -61
  34. data/lib/sequel/model.rb +3 -3
  35. data/lib/sequel/model/associations.rb +9 -1
  36. data/lib/sequel/model/base.rb +8 -1
  37. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  38. data/lib/sequel/sql.rb +125 -18
  39. data/lib/sequel/version.rb +1 -1
  40. data/spec/adapters/ado_spec.rb +1 -0
  41. data/spec/adapters/firebird_spec.rb +1 -0
  42. data/spec/adapters/informix_spec.rb +1 -0
  43. data/spec/adapters/mysql_spec.rb +23 -8
  44. data/spec/adapters/oracle_spec.rb +1 -0
  45. data/spec/adapters/postgres_spec.rb +52 -4
  46. data/spec/adapters/spec_helper.rb +2 -2
  47. data/spec/adapters/sqlite_spec.rb +2 -1
  48. data/spec/core/connection_pool_spec.rb +16 -0
  49. data/spec/core/database_spec.rb +174 -0
  50. data/spec/core/dataset_spec.rb +121 -26
  51. data/spec/core/expression_filters_spec.rb +156 -0
  52. data/spec/core/object_graph_spec.rb +20 -1
  53. data/spec/core/schema_spec.rb +5 -5
  54. data/spec/extensions/migration_spec.rb +140 -74
  55. data/spec/extensions/schema_dumper_spec.rb +3 -69
  56. data/spec/extensions/single_table_inheritance_spec.rb +6 -0
  57. data/spec/integration/dataset_test.rb +84 -2
  58. data/spec/integration/schema_test.rb +24 -5
  59. data/spec/integration/spec_helper.rb +8 -6
  60. data/spec/model/eager_loading_spec.rb +9 -0
  61. data/spec/model/record_spec.rb +35 -8
  62. metadata +8 -7
  63. data/lib/sequel/adapters/utils/date_format.rb +0 -21
  64. data/lib/sequel/adapters/utils/savepoint_transactions.rb +0 -80
  65. data/lib/sequel/adapters/utils/unsupported.rb +0 -50
@@ -228,41 +228,9 @@ END_MIG
228
228
  @d.dump_table_schema(:t3).should == "create_table(:t3) do\n Date :c1\n DateTime :c2, :null=>false\nend"
229
229
  end
230
230
 
231
- it "should handle converting many default formats" do
232
- m = @d.method(:column_schema_to_ruby_default)
233
- m.call("adf", :string, :same_db=>true).inspect.should == '"adf".lit'
234
- p = lambda{|d,t| m.call(d,t,{})}
235
- p[nil, :integer].should == nil
236
- p['1', :integer].should == 1
237
- p['-1', :integer].should == -1
238
- p['1.0', :float].should == 1.0
239
- p['-1.0', :float].should == -1.0
240
- p['1.0', :decimal].should == BigDecimal.new('1.0')
241
- p['-1.0', :decimal].should == BigDecimal.new('-1.0')
242
- p['1', :boolean].should == true
243
- p['0', :boolean].should == false
244
- p['true', :boolean].should == true
245
- p['false', :boolean].should == false
246
- p["'t'", :boolean].should == true
247
- p["'f'", :boolean].should == false
248
- p["'a'", :string].should == 'a'
249
- p["'a'", :blob].should == 'a'.to_sequel_blob
250
- p["'a'", :blob].should be_a_kind_of(Sequel::SQL::Blob)
251
- p["''", :string].should == ''
252
- p["'\\a''b'", :string].should == "\\a'b"
253
- p["'NULL'", :string].should == "NULL"
254
- p["'2009-10-29'", :date].should == Date.new(2009,10,29)
255
- p["CURRENT_TIMESTAMP", :date].should == nil
256
- p["today()", :date].should == nil
257
- p["'2009-10-29T10:20:30-07:00'", :datetime].should == DateTime.parse('2009-10-29T10:20:30-07:00')
258
- p["'2009-10-29 10:20:30'", :datetime].should == DateTime.parse('2009-10-29 10:20:30')
259
- p["'10:20:30'", :time].should == Time.parse('10:20:30')
260
- p["NaN", :float].should == nil
261
- end
262
-
263
231
  it "should handle converting common defaults" do
264
232
  @d.meta_def(:schema) do |t, *os|
265
- [[:c1, {:db_type=>'boolean', :default=>"false", :type=>:boolean, :allow_null=>true}],
233
+ s = [[:c1, {:db_type=>'boolean', :default=>"false", :type=>:boolean, :allow_null=>true}],
266
234
  [:c2, {:db_type=>'varchar', :default=>"'blah'", :type=>:string, :allow_null=>true}],
267
235
  [:c3, {:db_type=>'integer', :default=>"-1", :type=>:integer, :allow_null=>true}],
268
236
  [:c4, {:db_type=>'float', :default=>"1.0", :type=>:float, :allow_null=>true}],
@@ -272,46 +240,12 @@ END_MIG
272
240
  [:c8, {:db_type=>'datetime', :default=>"'2008-10-29 10:20:30'", :type=>:datetime, :allow_null=>true}],
273
241
  [:c9, {:db_type=>'time', :default=>"'10:20:30'", :type=>:time, :allow_null=>true}],
274
242
  [:c10, {:db_type=>'interval', :default=>"'6 weeks'", :type=>:interval, :allow_null=>true}]]
243
+ s.each{|_, c| c[:ruby_default] = column_schema_to_ruby_default(c[:default], c[:type])}
244
+ s
275
245
  end
276
246
  @d.dump_table_schema(:t4).gsub(/[+-]\d\d:\d\d"\)/, '")').should == "create_table(:t4) do\n TrueClass :c1, :default=>false\n String :c2, :default=>\"blah\"\n Integer :c3, :default=>-1\n Float :c4, :default=>1.0\n BigDecimal :c5, :default=>BigDecimal.new(\"0.1005E3\")\n File :c6, :default=>Sequel::SQL::Blob.new(\"blah\")\n Date :c7, :default=>Date.parse(\"2008-10-29\")\n DateTime :c8, :default=>DateTime.parse(\"2008-10-29T10:20:30\")\n Time :c9, :default=>Time.parse(\"10:20:30\"), :only_time=>true\n String :c10\nend"
277
247
  @d.dump_table_schema(:t4, :same_db=>true).gsub(/[+-]\d\d:\d\d"\)/, '")').should == "create_table(:t4) do\n column :c1, \"boolean\", :default=>false\n column :c2, \"varchar\", :default=>\"blah\"\n column :c3, \"integer\", :default=>-1\n column :c4, \"float\", :default=>1.0\n column :c5, \"decimal\", :default=>BigDecimal.new(\"0.1005E3\")\n column :c6, \"blob\", :default=>Sequel::SQL::Blob.new(\"blah\")\n column :c7, \"date\", :default=>Date.parse(\"2008-10-29\")\n column :c8, \"datetime\", :default=>DateTime.parse(\"2008-10-29T10:20:30\")\n column :c9, \"time\", :default=>Time.parse(\"10:20:30\")\n column :c10, \"interval\", :default=>\"'6 weeks'\".lit\nend"
278
248
  end
279
-
280
- it "should handle converting PostgreSQL specific default formats" do
281
- m = @d.method(:column_schema_to_ruby_default)
282
- @d.meta_def(:database_type){:postgres}
283
- p = lambda{|d,t| m.call(d,t,{})}
284
- p["''::text", :string].should == ""
285
- p["'\\a''b'::character varying", :string].should == "\\a'b"
286
- p["'a'::bpchar", :string].should == "a"
287
- p["(-1)", :integer].should == -1
288
- p["(-1.0)", :float].should == -1.0
289
- p['(-1.0)', :decimal].should == BigDecimal.new('-1.0')
290
- p["'a'::bytea", :blob].should == 'a'.to_sequel_blob
291
- p["'a'::bytea", :blob].should be_a_kind_of(Sequel::SQL::Blob)
292
- p["'2009-10-29'::date", :date].should == Date.new(2009,10,29)
293
- p["'2009-10-29 10:20:30.241343'::timestamp without time zone", :datetime].should == DateTime.parse('2009-10-29 10:20:30.241343')
294
- p["'10:20:30'::time without time zone", :time].should == Time.parse('10:20:30')
295
- end
296
-
297
- it "should handle converting MySQL specific default formats" do
298
- m = @d.method(:column_schema_to_ruby_default)
299
- @d.meta_def(:database_type){:mysql}
300
- p = lambda{|d,t| m.call(d,t,{})}
301
- s = lambda{|d,t| m.call(d,t,{:same_db=>true})}
302
- p["\\a'b", :string].should == "\\a'b"
303
- p["a", :string].should == "a"
304
- p["NULL", :string].should == "NULL"
305
- p["-1", :float].should == -1.0
306
- p['-1', :decimal].should == BigDecimal.new('-1.0')
307
- p["2009-10-29", :date].should == Date.new(2009,10,29)
308
- p["2009-10-29 10:20:30", :datetime].should == DateTime.parse('2009-10-29 10:20:30')
309
- p["10:20:30", :time].should == Time.parse('10:20:30')
310
- p["CURRENT_DATE", :date].should == nil
311
- p["CURRENT_TIMESTAMP", :datetime].should == nil
312
- s["CURRENT_DATE", :date].inspect.should == "\"CURRENT_DATE\".lit"
313
- s["CURRENT_TIMESTAMP", :datetime].inspect.should == "\"CURRENT_TIMESTAMP\".lit"
314
- end
315
249
 
316
250
  it "should convert unknown database types to strings" do
317
251
  @d.dump_table_schema(:t5).should == "create_table(:t5) do\n String :c1\nend"
@@ -3,6 +3,7 @@ require File.join(File.dirname(__FILE__), "spec_helper")
3
3
  describe Sequel::Model, "#sti_key" do
4
4
  before do
5
5
  class ::StiTest < Sequel::Model
6
+ def kind; self[:kind]; end
6
7
  def kind=(x); self[:kind] = x; end
7
8
  def _refresh(x); end
8
9
  plugin :single_table_inheritance, :kind
@@ -74,6 +75,11 @@ describe Sequel::Model, "#sti_key" do
74
75
  MODEL_DB.sqls.should == ["INSERT INTO sti_tests (kind) VALUES ('StiTest')", "INSERT INTO sti_tests (kind) VALUES ('StiTestSub1')", "INSERT INTO sti_tests (kind) VALUES ('StiTestSub2')"]
75
76
  end
76
77
 
78
+ it "should have the before_create hook not override an existing value" do
79
+ StiTest.create(:kind=>'StiTestSub1')
80
+ MODEL_DB.sqls.should == ["INSERT INTO sti_tests (kind) VALUES ('StiTestSub1')"]
81
+ end
82
+
77
83
  it "should add a filter to model datasets inside subclasses hook to only retreive objects with the matching key" do
78
84
  StiTest.dataset.sql.should == "SELECT * FROM sti_tests"
79
85
  StiTestSub1.dataset.sql.should == "SELECT * FROM sti_tests WHERE (kind = 'StiTestSub1')"
@@ -249,7 +249,7 @@ describe "Dataset UNION, EXCEPT, and INTERSECT" do
249
249
 
250
250
  specify "should give the correct results for simple UNION, EXCEPT, and INTERSECT" do
251
251
  @ds1.union(@ds2).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 30'
252
- unless defined?(Sequel::Dataset::UnsupportedIntersectExcept) and @ds1.class.ancestors.include?(Sequel::Dataset::UnsupportedIntersectExcept)
252
+ if @ds1.supports_intersect_except?
253
253
  @ds1.except(@ds2).order(:number).map{|x| x[:number].to_s}.should == %w'20'
254
254
  @ds1.intersect(@ds2).order(:number).map{|x| x[:number].to_s}.should == %w'10'
255
255
  end
@@ -285,7 +285,7 @@ describe "Dataset UNION, EXCEPT, and INTERSECT" do
285
285
 
286
286
  @ds1.union(@ds2).union(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 30 40'
287
287
  @ds1.union(@ds2.union(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 30 40'
288
- unless defined?(Sequel::Dataset::UnsupportedIntersectExcept) and @ds1.class.ancestors.include?(Sequel::Dataset::UnsupportedIntersectExcept)
288
+ if @ds1.supports_intersect_except?
289
289
  @ds1.union(@ds2).except(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'20 30'
290
290
  @ds1.union(@ds2.except(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 30'
291
291
  @ds1.union(@ds2).intersect(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'10 '
@@ -307,3 +307,85 @@ describe "Dataset UNION, EXCEPT, and INTERSECT" do
307
307
  end
308
308
  end
309
309
  end
310
+
311
+ if INTEGRATION_DB.dataset.supports_cte?
312
+ describe "Common Table Expressions" do
313
+ before do
314
+ @db = INTEGRATION_DB
315
+ @db.create_table!(:i1){Integer :id; Integer :parent_id}
316
+ @ds = @db[:i1]
317
+ @ds.insert(:id=>1)
318
+ @ds.insert(:id=>2)
319
+ @ds.insert(:id=>3, :parent_id=>1)
320
+ @ds.insert(:id=>4, :parent_id=>1)
321
+ @ds.insert(:id=>5, :parent_id=>3)
322
+ @ds.insert(:id=>6, :parent_id=>5)
323
+ end
324
+ after do
325
+ @db.drop_table(:i1)
326
+ end
327
+
328
+ specify "should give correct results for WITH" do
329
+ @db[:t].with(:t, @ds.order(:id).filter(:parent_id=>nil).select(:id)).map(:id).should == [1, 2]
330
+ end
331
+
332
+ specify "should give correct results for recursive WITH" do
333
+ ds = @db[:t].select(:i___id, :pi___parent_id).with_recursive(:t, @ds.filter(:parent_id=>nil), @ds.join(:t, :i=>:parent_id).select(:i1__id, :i1__parent_id), :args=>[:i, :pi])
334
+ ds.all.should == [{:parent_id=>nil, :id=>1}, {:parent_id=>nil, :id=>2}, {:parent_id=>1, :id=>3}, {:parent_id=>1, :id=>4}, {:parent_id=>3, :id=>5}, {:parent_id=>5, :id=>6}]
335
+ ps = @db[:t].select(:i___id, :pi___parent_id).with_recursive(:t, @ds.filter(:parent_id=>:$n), @ds.join(:t, :i=>:parent_id).filter(:t__i=>:parent_id).select(:i1__id, :i1__parent_id), :args=>[:i, :pi]).prepare(:select, :cte_sel)
336
+ ps.call(:n=>1).should == [{:id=>3, :parent_id=>1}, {:id=>4, :parent_id=>1}, {:id=>5, :parent_id=>3}, {:id=>6, :parent_id=>5}]
337
+ ps.call(:n=>3).should == [{:id=>5, :parent_id=>3}, {:id=>6, :parent_id=>5}]
338
+ ps.call(:n=>5).should == [{:id=>6, :parent_id=>5}]
339
+ end
340
+ end
341
+ end
342
+
343
+ if INTEGRATION_DB.dataset.supports_window_functions?
344
+ describe "Window Functions" do
345
+ before do
346
+ @db = INTEGRATION_DB
347
+ @db.create_table!(:i1){Integer :id; Integer :group_id; Integer :amount}
348
+ @ds = @db[:i1].order(:id)
349
+ @ds.insert(:id=>1, :group_id=>1, :amount=>1)
350
+ @ds.insert(:id=>2, :group_id=>1, :amount=>10)
351
+ @ds.insert(:id=>3, :group_id=>1, :amount=>100)
352
+ @ds.insert(:id=>4, :group_id=>2, :amount=>1000)
353
+ @ds.insert(:id=>5, :group_id=>2, :amount=>10000)
354
+ @ds.insert(:id=>6, :group_id=>2, :amount=>100000)
355
+ end
356
+ after do
357
+ @db.drop_table(:i1)
358
+ end
359
+
360
+ specify "should give correct results for window functions" do
361
+ @ds.select(:id){sum(:over, :args=>amount, :partition=>group_id, :order=>id){}}.all.should ==
362
+ [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1000, :id=>4}, {:sum=>11000, :id=>5}, {:sum=>111000, :id=>6}]
363
+ @ds.select(:id){sum(:over, :args=>amount, :partition=>group_id){}}.all.should ==
364
+ [{:sum=>111, :id=>1}, {:sum=>111, :id=>2}, {:sum=>111, :id=>3}, {:sum=>111000, :id=>4}, {:sum=>111000, :id=>5}, {:sum=>111000, :id=>6}]
365
+ @ds.select(:id){sum(:over, :args=>amount, :order=>id){}}.all.should ==
366
+ [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1111, :id=>4}, {:sum=>11111, :id=>5}, {:sum=>111111, :id=>6}]
367
+ @ds.select(:id){sum(:over, :args=>amount){}}.all.should ==
368
+ [{:sum=>111111, :id=>1}, {:sum=>111111, :id=>2}, {:sum=>111111, :id=>3}, {:sum=>111111, :id=>4}, {:sum=>111111, :id=>5}, {:sum=>111111, :id=>6}]
369
+ end
370
+
371
+ specify "should give correct results for window functions with frames" do
372
+ @ds.select(:id){sum(:over, :args=>amount, :partition=>group_id, :order=>id, :frame=>:all){}}.all.should ==
373
+ [{:sum=>111, :id=>1}, {:sum=>111, :id=>2}, {:sum=>111, :id=>3}, {:sum=>111000, :id=>4}, {:sum=>111000, :id=>5}, {:sum=>111000, :id=>6}]
374
+ @ds.select(:id){sum(:over, :args=>amount, :partition=>group_id, :frame=>:all){}}.all.should ==
375
+ [{:sum=>111, :id=>1}, {:sum=>111, :id=>2}, {:sum=>111, :id=>3}, {:sum=>111000, :id=>4}, {:sum=>111000, :id=>5}, {:sum=>111000, :id=>6}]
376
+ @ds.select(:id){sum(:over, :args=>amount, :order=>id, :frame=>:all){}}.all.should ==
377
+ [{:sum=>111111, :id=>1}, {:sum=>111111, :id=>2}, {:sum=>111111, :id=>3}, {:sum=>111111, :id=>4}, {:sum=>111111, :id=>5}, {:sum=>111111, :id=>6}]
378
+ @ds.select(:id){sum(:over, :args=>amount, :frame=>:all){}}.all.should ==
379
+ [{:sum=>111111, :id=>1}, {:sum=>111111, :id=>2}, {:sum=>111111, :id=>3}, {:sum=>111111, :id=>4}, {:sum=>111111, :id=>5}, {:sum=>111111, :id=>6}]
380
+
381
+ @ds.select(:id){sum(:over, :args=>amount, :partition=>group_id, :order=>id, :frame=>:rows){}}.all.should ==
382
+ [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1000, :id=>4}, {:sum=>11000, :id=>5}, {:sum=>111000, :id=>6}]
383
+ @ds.select(:id){sum(:over, :args=>amount, :partition=>group_id, :frame=>:rows){}}.all.should ==
384
+ [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1000, :id=>4}, {:sum=>11000, :id=>5}, {:sum=>111000, :id=>6}]
385
+ @ds.select(:id){sum(:over, :args=>amount, :order=>id, :frame=>:rows){}}.all.should ==
386
+ [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1111, :id=>4}, {:sum=>11111, :id=>5}, {:sum=>111111, :id=>6}]
387
+ @ds.select(:id){sum(:over, :args=>amount, :frame=>:rows){}}.all.should ==
388
+ [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1111, :id=>4}, {:sum=>11111, :id=>5}, {:sum=>111111, :id=>6}]
389
+ end
390
+ end
391
+ end
@@ -74,11 +74,11 @@ describe "Database schema parser" do
74
74
 
75
75
  specify "should parse defaults from the schema properly" do
76
76
  INTEGRATION_DB.create_table!(:items){Integer :number}
77
- INTEGRATION_DB.schema(:items).first.last[:default].should == nil
77
+ INTEGRATION_DB.schema(:items).first.last[:ruby_default].should == nil
78
78
  INTEGRATION_DB.create_table!(:items){Integer :number, :default=>0}
79
- INTEGRATION_DB.schema(:items).first.last[:default].to_s.should == "0"
79
+ INTEGRATION_DB.schema(:items).first.last[:ruby_default].should == 0
80
80
  INTEGRATION_DB.create_table!(:items){String :a, :default=>"blah"}
81
- INTEGRATION_DB.schema(:items).first.last[:default].should include('blah')
81
+ INTEGRATION_DB.schema(:items).first.last[:ruby_default].should == 'blah'
82
82
  end
83
83
 
84
84
  specify "should parse types from the schema properly" do
@@ -146,9 +146,7 @@ end
146
146
 
147
147
  describe "Database schema modifiers" do
148
148
  before do
149
- INTEGRATION_DB.create_table!(:items){Integer :number}
150
149
  @ds = INTEGRATION_DB[:items]
151
- @ds.insert([10])
152
150
  clear_sqls
153
151
  end
154
152
  after do
@@ -156,12 +154,27 @@ describe "Database schema modifiers" do
156
154
  end
157
155
 
158
156
  specify "should create tables correctly" do
157
+ INTEGRATION_DB.create_table!(:items){Integer :number}
159
158
  INTEGRATION_DB.table_exists?(:items).should == true
160
159
  INTEGRATION_DB.schema(:items, :reload=>true).map{|x| x.first}.should == [:number]
160
+ @ds.insert([10])
161
161
  @ds.columns!.should == [:number]
162
162
  end
163
163
 
164
+ specify "should handle foreign keys correctly when creating tables" do
165
+ INTEGRATION_DB.create_table!(:items) do
166
+ primary_key :id
167
+ foreign_key :item_id, :items
168
+ unique [:item_id, :id]
169
+ foreign_key [:id, :item_id], :items, :key=>[:item_id, :id]
170
+ end
171
+ INTEGRATION_DB.table_exists?(:items).should == true
172
+ INTEGRATION_DB.schema(:items, :reload=>true).map{|x| x.first}.should == [:id, :item_id]
173
+ @ds.columns!.should == [:id, :item_id]
174
+ end
175
+
164
176
  specify "should add columns to tables correctly" do
177
+ INTEGRATION_DB.create_table!(:items){Integer :number}
165
178
  INTEGRATION_DB.schema(:items, :reload=>true).map{|x| x.first}.should == [:number]
166
179
  @ds.columns!.should == [:number]
167
180
  INTEGRATION_DB.alter_table(:items){add_column :name, :text}
@@ -174,6 +187,12 @@ describe "Database schema modifiers" do
174
187
  INTEGRATION_DB.alter_table(:items){add_foreign_key :item_id, :items}
175
188
  INTEGRATION_DB.schema(:items, :reload=>true).map{|x| x.first}.should == [:number, :name, :id, :item_id]
176
189
  @ds.columns!.should == [:number, :name, :id, :item_id]
190
+ INTEGRATION_DB.alter_table(:items) do
191
+ add_unique_constraint [:item_id, :id]
192
+ add_foreign_key [:id, :item_id], :items, :key=>[:item_id, :id]
193
+ end
194
+ INTEGRATION_DB.schema(:items, :reload=>true).map{|x| x.first}.should == [:number, :name, :id, :item_id]
195
+ @ds.columns!.should == [:number, :name, :id, :item_id]
177
196
  end
178
197
  end
179
198
 
@@ -1,10 +1,11 @@
1
1
  require 'rubygems'
2
+ require 'logger'
2
3
  unless Object.const_defined?('Sequel')
3
4
  $:.unshift(File.join(File.dirname(__FILE__), "../../lib/"))
4
5
  require 'sequel'
5
6
  end
6
7
  begin
7
- require File.join(File.dirname(__FILE__), '../spec_config.rb')
8
+ require File.join(File.dirname(File.dirname(__FILE__)), 'spec_config.rb') unless defined?(INTEGRATION_DB)
8
9
  rescue LoadError
9
10
  end
10
11
 
@@ -16,12 +17,13 @@ def clear_sqls
16
17
  end
17
18
 
18
19
  class Spec::Example::ExampleGroup
19
- def start_logging
20
- require 'logger'
21
- INTEGRATION_DB.loggers << Logger.new(STDOUT)
22
- end
23
- def stop_logging
20
+ def log
21
+ begin
22
+ INTEGRATION_DB.loggers << Logger.new(STDOUT)
23
+ yield
24
+ ensure
24
25
  INTEGRATION_DB.loggers.clear
26
+ end
25
27
  end
26
28
  end
27
29
 
@@ -628,6 +628,15 @@ describe Sequel::Model, "#eager_graph" do
628
628
  proc{GraphAlbum.eager_graph(Object.new)}.should raise_error(Sequel::Error)
629
629
  end
630
630
 
631
+ it "should not split results and assign associations if ungraphed is called" do
632
+ ds = GraphAlbum.eager_graph(:band).ungraphed
633
+ ds.sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id)'
634
+ def ds.fetch_rows(sql, &block)
635
+ yield({:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3})
636
+ end
637
+ ds.all.should == [GraphAlbum.load(:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3)]
638
+ end
639
+
631
640
  it "should eagerly load a single many_to_one association" do
632
641
  ds = GraphAlbum.eager_graph(:band)
633
642
  ds.sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id)'
@@ -35,19 +35,19 @@ describe "Model#save server use" do
35
35
  end
36
36
 
37
37
  describe "Model#save" do
38
-
39
- before(:each) do
38
+ before do
40
39
  @c = Class.new(Sequel::Model(:items)) do
41
40
  columns :id, :x, :y
42
41
  end
42
+ @c.dataset.meta_def(:insert){|h| super(h); 1}
43
43
  MODEL_DB.reset
44
44
  end
45
45
 
46
46
  it "should insert a record for a new model instance" do
47
47
  o = @c.new(:x => 1)
48
48
  o.save
49
-
50
- MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
49
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (1)",
50
+ "SELECT * FROM items WHERE (id = 1) LIMIT 1"]
51
51
  end
52
52
 
53
53
  it "should use dataset's insert_select method if present" do
@@ -60,15 +60,42 @@ describe "Model#save" do
60
60
  o.save
61
61
 
62
62
  o.values.should == {:y=>2}
63
- MODEL_DB.sqls.first.should == "INSERT INTO items (y) VALUES (2)"
63
+ MODEL_DB.sqls.should == ["INSERT INTO items (y) VALUES (2)"]
64
+ end
65
+
66
+ it "should use value returned by insert as the primary key" do
67
+ @c.dataset.meta_def(:insert){|h| super(h); 13}
68
+ o = @c.new(:x => 11)
69
+ o.save
70
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (11)",
71
+ "SELECT * FROM items WHERE (id = 13) LIMIT 1"]
72
+ end
73
+
74
+ it "should work correctly for inserting a record without a primary key" do
75
+ @c.dataset.meta_def(:insert){|h| super(h); 13}
76
+ @c.no_primary_key
77
+ o = @c.new(:x => 11)
78
+ o.save
79
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (11)"]
80
+ end
81
+
82
+ it "should set the autoincrementing_primary_key value to the value returned by insert" do
83
+ @c.dataset.meta_def(:insert){|h| super(h); 13}
84
+ @c.unrestrict_primary_key
85
+ @c.set_primary_key [:x, :y]
86
+ o = @c.new(:x => 11)
87
+ o.meta_def(:autoincrementing_primary_key){:y}
88
+ o.save
89
+ MODEL_DB.sqls.length.should == 2
90
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (11)"
91
+ MODEL_DB.sqls.last.should =~ %r{SELECT \* FROM items WHERE \(\([xy] = 1[13]\) AND \([xy] = 1[13]\)\) LIMIT 1}
64
92
  end
65
93
 
66
94
  it "should update a record for an existing model instance" do
67
95
  o = @c.load(:id => 3, :x => 1)
68
96
  o.save
69
-
70
- MODEL_DB.sqls.first.should =~
71
- /UPDATE items SET (id = 3, x = 1|x = 1, id = 3) WHERE \(id = 3\)/
97
+ MODEL_DB.sqls.length.should == 1
98
+ MODEL_DB.sqls.first.should =~ /UPDATE items SET (id = 3, x = 1|x = 1, id = 3) WHERE \(id = 3\)/
72
99
  end
73
100
 
74
101
  it "should update only the given columns if given" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-04 00:00:00 -07:00
12
+ date: 2009-07-02 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -31,6 +31,7 @@ extra_rdoc_files:
31
31
  - doc/sharding.rdoc
32
32
  - doc/virtual_rows.rdoc
33
33
  - doc/reflection.rdoc
34
+ - doc/opening_databases.rdoc
34
35
  - doc/release_notes/2.10.0.txt
35
36
  - doc/release_notes/2.9.0.txt
36
37
  - doc/release_notes/2.8.0.txt
@@ -51,6 +52,7 @@ extra_rdoc_files:
51
52
  - doc/release_notes/2.12.0.txt
52
53
  - doc/release_notes/3.0.0.txt
53
54
  - doc/release_notes/3.1.0.txt
55
+ - doc/release_notes/3.2.0.txt
54
56
  files:
55
57
  - COPYING
56
58
  - CHANGELOG
@@ -84,8 +86,10 @@ files:
84
86
  - doc/release_notes/2.12.0.txt
85
87
  - doc/release_notes/3.0.0.txt
86
88
  - doc/release_notes/3.1.0.txt
89
+ - doc/release_notes/3.2.0.txt
87
90
  - doc/virtual_rows.rdoc
88
91
  - doc/reflection.rdoc
92
+ - doc/opening_databases.rdoc
89
93
  - spec/adapters
90
94
  - spec/adapters/ado_spec.rb
91
95
  - spec/adapters/informix_spec.rb
@@ -111,9 +115,9 @@ files:
111
115
  - spec/core/core_sql_spec.rb
112
116
  - spec/core/dataset_spec.rb
113
117
  - spec/core/expression_filters_spec.rb
118
+ - spec/core/schema_spec.rb
114
119
  - spec/core/object_graph_spec.rb
115
120
  - spec/core/schema_generator_spec.rb
116
- - spec/core/schema_spec.rb
117
121
  - spec/core/spec_helper.rb
118
122
  - spec/core/version_spec.rb
119
123
  - spec/model
@@ -210,10 +214,7 @@ files:
210
214
  - lib/sequel/adapters/shared/sqlite.rb
211
215
  - lib/sequel/adapters/sqlite.rb
212
216
  - lib/sequel/adapters/utils
213
- - lib/sequel/adapters/utils/date_format.rb
214
217
  - lib/sequel/adapters/utils/stored_procedures.rb
215
- - lib/sequel/adapters/utils/unsupported.rb
216
- - lib/sequel/adapters/utils/savepoint_transactions.rb
217
218
  - lib/sequel/adapters/amalgalite.rb
218
219
  - lib/sequel/connection_pool.rb
219
220
  - lib/sequel/core.rb
@@ -232,6 +233,7 @@ files:
232
233
  - lib/sequel/model.rb
233
234
  - lib/sequel/exceptions.rb
234
235
  - lib/sequel/metaprogramming.rb
236
+ - lib/sequel/sql.rb
235
237
  - lib/sequel/model
236
238
  - lib/sequel/model/associations.rb
237
239
  - lib/sequel/model/base.rb
@@ -239,7 +241,6 @@ files:
239
241
  - lib/sequel/model/errors.rb
240
242
  - lib/sequel/model/exceptions.rb
241
243
  - lib/sequel/model/inflections.rb
242
- - lib/sequel/sql.rb
243
244
  - lib/sequel/version.rb
244
245
  has_rdoc: true
245
246
  homepage: http://sequel.rubyforge.org