sequel 3.1.0 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
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