sequel 3.26.0 → 3.27.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 (39) hide show
  1. data/CHANGELOG +26 -0
  2. data/Rakefile +2 -3
  3. data/doc/mass_assignment.rdoc +54 -0
  4. data/doc/migration.rdoc +9 -533
  5. data/doc/prepared_statements.rdoc +8 -7
  6. data/doc/release_notes/3.27.0.txt +82 -0
  7. data/doc/schema_modification.rdoc +547 -0
  8. data/doc/testing.rdoc +64 -0
  9. data/lib/sequel/adapters/amalgalite.rb +4 -0
  10. data/lib/sequel/adapters/jdbc.rb +3 -1
  11. data/lib/sequel/adapters/jdbc/h2.rb +11 -5
  12. data/lib/sequel/adapters/mysql.rb +4 -122
  13. data/lib/sequel/adapters/mysql2.rb +4 -13
  14. data/lib/sequel/adapters/odbc.rb +4 -1
  15. data/lib/sequel/adapters/odbc/db2.rb +21 -0
  16. data/lib/sequel/adapters/shared/mysql.rb +12 -0
  17. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +143 -0
  18. data/lib/sequel/adapters/tinytds.rb +122 -3
  19. data/lib/sequel/core.rb +4 -3
  20. data/lib/sequel/database/misc.rb +7 -10
  21. data/lib/sequel/dataset/misc.rb +1 -1
  22. data/lib/sequel/dataset/sql.rb +7 -0
  23. data/lib/sequel/model/associations.rb +2 -2
  24. data/lib/sequel/model/base.rb +60 -10
  25. data/lib/sequel/plugins/prepared_statements_safe.rb +17 -7
  26. data/lib/sequel/sql.rb +5 -0
  27. data/lib/sequel/timezones.rb +12 -3
  28. data/lib/sequel/version.rb +1 -1
  29. data/spec/adapters/mysql_spec.rb +25 -21
  30. data/spec/core/database_spec.rb +200 -0
  31. data/spec/core/dataset_spec.rb +6 -0
  32. data/spec/extensions/prepared_statements_safe_spec.rb +10 -0
  33. data/spec/extensions/schema_dumper_spec.rb +2 -2
  34. data/spec/integration/schema_test.rb +30 -1
  35. data/spec/integration/type_test.rb +10 -3
  36. data/spec/model/base_spec.rb +44 -0
  37. data/spec/model/model_spec.rb +14 -0
  38. data/spec/model/record_spec.rb +131 -12
  39. metadata +14 -4
@@ -3,7 +3,7 @@ module Sequel
3
3
  MAJOR = 3
4
4
  # The minor version of Sequel. Bumped for every non-patch level
5
5
  # release, generally around once a month.
6
- MINOR = 26
6
+ MINOR = 27
7
7
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
8
8
  # releases that fix regressions from previous versions.
9
9
  TINY = 0
@@ -956,7 +956,7 @@ describe "MySQL::Dataset#calc_found_rows" do
956
956
  end
957
957
  end
958
958
 
959
- if MYSQL_DB.adapter_scheme == :mysql or MYSQL_DB.adapter_scheme == :jdbc
959
+ if MYSQL_DB.adapter_scheme == :mysql or MYSQL_DB.adapter_scheme == :jdbc or MYSQL_DB.adapter_scheme == :mysql2
960
960
  describe "MySQL Stored Procedures" do
961
961
  before do
962
962
  MYSQL_DB.create_table(:items){Integer :id; Integer :value}
@@ -977,26 +977,30 @@ if MYSQL_DB.adapter_scheme == :mysql or MYSQL_DB.adapter_scheme == :jdbc
977
977
  MYSQL_DB[:items].count.should == 0
978
978
  end
979
979
 
980
- specify "should be callable on the dataset object" do
981
- MYSQL_DB.execute_ddl('CREATE PROCEDURE test_sproc(a INTEGER) BEGIN SELECT *, a AS b FROM items; END')
982
- MYSQL_DB[:items].delete
983
- @d = MYSQL_DB[:items]
984
- @d.call_sproc(:select, :test_sproc, 3).should == []
985
- @d.insert(:value=>1)
986
- @d.call_sproc(:select, :test_sproc, 4).should == [{:id=>nil, :value=>1, :b=>4}]
987
- @d.row_proc = proc{|r| r.keys.each{|k| r[k] *= 2 if r[k].is_a?(Integer)}; r}
988
- @d.call_sproc(:select, :test_sproc, 3).should == [{:id=>nil, :value=>2, :b=>6}]
989
- end
990
-
991
- specify "should be callable on the dataset object with multiple arguments" do
992
- MYSQL_DB.execute_ddl('CREATE PROCEDURE test_sproc(a INTEGER, c INTEGER) BEGIN SELECT *, a AS b, c AS d FROM items; END')
993
- MYSQL_DB[:items].delete
994
- @d = MYSQL_DB[:items]
995
- @d.call_sproc(:select, :test_sproc, 3, 4).should == []
996
- @d.insert(:value=>1)
997
- @d.call_sproc(:select, :test_sproc, 4, 5).should == [{:id=>nil, :value=>1, :b=>4, :d=>5}]
998
- @d.row_proc = proc{|r| r.keys.each{|k| r[k] *= 2 if r[k].is_a?(Integer)}; r}
999
- @d.call_sproc(:select, :test_sproc, 3, 4).should == [{:id=>nil, :value=>2, :b=>6, :d => 8}]
980
+ # Mysql2 doesn't support stored procedures that return result sets, probably because
981
+ # CLIENT_MULTI_RESULTS is not set.
982
+ unless MYSQL_DB.adapter_scheme == :mysql2
983
+ specify "should be callable on the dataset object" do
984
+ MYSQL_DB.execute_ddl('CREATE PROCEDURE test_sproc(a INTEGER) BEGIN SELECT *, a AS b FROM items; END')
985
+ MYSQL_DB[:items].delete
986
+ @d = MYSQL_DB[:items]
987
+ @d.call_sproc(:select, :test_sproc, 3).should == []
988
+ @d.insert(:value=>1)
989
+ @d.call_sproc(:select, :test_sproc, 4).should == [{:id=>nil, :value=>1, :b=>4}]
990
+ @d.row_proc = proc{|r| r.keys.each{|k| r[k] *= 2 if r[k].is_a?(Integer)}; r}
991
+ @d.call_sproc(:select, :test_sproc, 3).should == [{:id=>nil, :value=>2, :b=>6}]
992
+ end
993
+
994
+ specify "should be callable on the dataset object with multiple arguments" do
995
+ MYSQL_DB.execute_ddl('CREATE PROCEDURE test_sproc(a INTEGER, c INTEGER) BEGIN SELECT *, a AS b, c AS d FROM items; END')
996
+ MYSQL_DB[:items].delete
997
+ @d = MYSQL_DB[:items]
998
+ @d.call_sproc(:select, :test_sproc, 3, 4).should == []
999
+ @d.insert(:value=>1)
1000
+ @d.call_sproc(:select, :test_sproc, 4, 5).should == [{:id=>nil, :value=>1, :b=>4, :d=>5}]
1001
+ @d.row_proc = proc{|r| r.keys.each{|k| r[k] *= 2 if r[k].is_a?(Integer)}; r}
1002
+ @d.call_sproc(:select, :test_sproc, 3, 4).should == [{:id=>nil, :value=>2, :b=>6, :d => 8}]
1003
+ end
1000
1004
  end
1001
1005
 
1002
1006
  specify "should deal with nil values" do
@@ -1344,6 +1344,206 @@ describe "Database#typecast_value" do
1344
1344
  @db.typecast_value(:integer, "0x80").should == 128
1345
1345
  end
1346
1346
 
1347
+ specify "should typecast blobs as as Sequel::SQL::Blob" do
1348
+ v = @db.typecast_value(:blob, "0x013")
1349
+ v.should be_a_kind_of(Sequel::SQL::Blob)
1350
+ v.should == Sequel::SQL::Blob.new("0x013")
1351
+ @db.typecast_value(:blob, v).object_id.should == v.object_id
1352
+ end
1353
+
1354
+ specify "should typecast boolean values to true, false, or nil" do
1355
+ @db.typecast_value(:boolean, false).should be_false
1356
+ @db.typecast_value(:boolean, 0).should be_false
1357
+ @db.typecast_value(:boolean, "0").should be_false
1358
+ @db.typecast_value(:boolean, 'f').should be_false
1359
+ @db.typecast_value(:boolean, 'false').should be_false
1360
+ @db.typecast_value(:boolean, true).should be_true
1361
+ @db.typecast_value(:boolean, 1).should be_true
1362
+ @db.typecast_value(:boolean, '1').should be_true
1363
+ @db.typecast_value(:boolean, 't').should be_true
1364
+ @db.typecast_value(:boolean, 'true').should be_true
1365
+ @db.typecast_value(:boolean, '').should be_nil
1366
+ end
1367
+
1368
+ specify "should typecast date values to Date" do
1369
+ @db.typecast_value(:date, Date.today).should == Date.today
1370
+ @db.typecast_value(:date, DateTime.now).should == Date.today
1371
+ @db.typecast_value(:date, Time.now).should == Date.today
1372
+ @db.typecast_value(:date, Date.today.to_s).should == Date.today
1373
+ @db.typecast_value(:date, :year=>Date.today.year, :month=>Date.today.month, :day=>Date.today.day).should == Date.today
1374
+ end
1375
+
1376
+ specify "should typecast datetime values to Sequel.datetime_class with correct timezone handling" do
1377
+ t = Time.utc(2011, 1, 2, 3, 4, 5) # UTC Time
1378
+ t2 = Time.mktime(2011, 1, 2, 3, 4, 5) # Local Time
1379
+ t3 = Time.utc(2011, 1, 2, 3, 4, 5) - (t - t2) # Local Time in UTC Time
1380
+ t4 = Time.mktime(2011, 1, 2, 3, 4, 5) + (t - t2) # UTC Time in Local Time
1381
+ dt = DateTime.civil(2011, 1, 2, 3, 4, 5)
1382
+ r1 = defined?(Rational) ? Rational(t2.utc_offset, 86400) : t2.utc_offset/86400.0
1383
+ r2 = defined?(Rational) ? Rational((t - t2).to_i, 86400) : (t - t2).to_i/86400.0
1384
+ dt2 = DateTime.civil(2011, 1, 2, 3, 4, 5, r1)
1385
+ dt3 = DateTime.civil(2011, 1, 2, 3, 4, 5) - r2
1386
+ dt4 = DateTime.civil(2011, 1, 2, 3, 4, 5, r1) + r2
1387
+
1388
+ t.should == t4
1389
+ t2.should == t3
1390
+ dt.should == dt4
1391
+ dt2.should == dt3
1392
+
1393
+ check = proc do |i, o|
1394
+ v = @db.typecast_value(:datetime, i)
1395
+ v.should == o
1396
+ if o.is_a?(Time)
1397
+ v.utc_offset.should == o.utc_offset
1398
+ else
1399
+ v.offset.should == o.offset
1400
+ end
1401
+ end
1402
+ begin
1403
+ @db.typecast_value(:datetime, dt).should == t
1404
+ @db.typecast_value(:datetime, dt2).should == t2
1405
+ @db.typecast_value(:datetime, t).should == t
1406
+ @db.typecast_value(:datetime, t2).should == t2
1407
+ @db.typecast_value(:datetime, dt.to_s).should == t
1408
+ @db.typecast_value(:datetime, dt.strftime('%F %T')).should == t2
1409
+ @db.typecast_value(:datetime, Date.civil(2011, 1, 2)).should == Time.mktime(2011, 1, 2, 0, 0, 0)
1410
+ @db.typecast_value(:datetime, :year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec).should == t2
1411
+
1412
+ Sequel.datetime_class = DateTime
1413
+ @db.typecast_value(:datetime, dt).should == dt
1414
+ @db.typecast_value(:datetime, dt2).should == dt2
1415
+ @db.typecast_value(:datetime, t).should == dt
1416
+ @db.typecast_value(:datetime, t2).should == dt2
1417
+ @db.typecast_value(:datetime, dt.to_s).should == dt
1418
+ @db.typecast_value(:datetime, dt.strftime('%F %T')).should == dt
1419
+ @db.typecast_value(:datetime, Date.civil(2011, 1, 2)).should == DateTime.civil(2011, 1, 2, 0, 0, 0)
1420
+ @db.typecast_value(:datetime, :year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec).should == dt
1421
+
1422
+ Sequel.application_timezone = :utc
1423
+ Sequel.typecast_timezone = :local
1424
+ Sequel.datetime_class = Time
1425
+ check[dt, t]
1426
+ check[dt2, t3]
1427
+ check[t, t]
1428
+ check[t2, t3]
1429
+ check[dt.to_s, t]
1430
+ check[dt.strftime('%F %T'), t3]
1431
+ check[Date.civil(2011, 1, 2), Time.utc(2011, 1, 2, 0, 0, 0)]
1432
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, t3]
1433
+
1434
+ Sequel.datetime_class = DateTime
1435
+ check[dt, dt]
1436
+ check[dt2, dt3]
1437
+ check[t, dt]
1438
+ check[t2, dt3]
1439
+ check[dt.to_s, dt]
1440
+ check[dt.strftime('%F %T'), dt3]
1441
+ check[Date.civil(2011, 1, 2), DateTime.civil(2011, 1, 2, 0, 0, 0)]
1442
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, dt3]
1443
+
1444
+ Sequel.typecast_timezone = :utc
1445
+ Sequel.datetime_class = Time
1446
+ check[dt, t]
1447
+ check[dt2, t3]
1448
+ check[t, t]
1449
+ check[t2, t3]
1450
+ check[dt.to_s, t]
1451
+ check[dt.strftime('%F %T'), t]
1452
+ check[Date.civil(2011, 1, 2), Time.utc(2011, 1, 2, 0, 0, 0)]
1453
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, t]
1454
+
1455
+ Sequel.datetime_class = DateTime
1456
+ check[dt, dt]
1457
+ check[dt2, dt3]
1458
+ check[t, dt]
1459
+ check[t2, dt3]
1460
+ check[dt.to_s, dt]
1461
+ check[dt.strftime('%F %T'), dt]
1462
+ check[Date.civil(2011, 1, 2), DateTime.civil(2011, 1, 2, 0, 0, 0)]
1463
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, dt]
1464
+
1465
+ Sequel.application_timezone = :local
1466
+ Sequel.datetime_class = Time
1467
+ check[dt, t4]
1468
+ check[dt2, t2]
1469
+ check[t, t4]
1470
+ check[t2, t2]
1471
+ check[dt.to_s, t4]
1472
+ check[dt.strftime('%F %T'), t4]
1473
+ check[Date.civil(2011, 1, 2), Time.local(2011, 1, 2, 0, 0, 0)]
1474
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, t4]
1475
+
1476
+ Sequel.datetime_class = DateTime
1477
+ check[dt, dt4]
1478
+ check[dt2, dt2]
1479
+ check[t, dt4]
1480
+ check[t2, dt2]
1481
+ check[dt.to_s, dt4]
1482
+ check[dt.strftime('%F %T'), dt4]
1483
+ check[Date.civil(2011, 1, 2), DateTime.civil(2011, 1, 2, 0, 0, 0, r1)]
1484
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, dt4]
1485
+
1486
+ Sequel.typecast_timezone = :local
1487
+ Sequel.datetime_class = Time
1488
+ check[dt, t4]
1489
+ check[dt2, t2]
1490
+ check[t, t4]
1491
+ check[t2, t2]
1492
+ check[dt.to_s, t4]
1493
+ check[dt.strftime('%F %T'), t2]
1494
+ check[Date.civil(2011, 1, 2), Time.local(2011, 1, 2, 0, 0, 0)]
1495
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, t2]
1496
+
1497
+ Sequel.datetime_class = DateTime
1498
+ check[dt, dt4]
1499
+ check[dt2, dt2]
1500
+ check[t, dt4]
1501
+ check[t2, dt2]
1502
+ check[dt.to_s, dt4]
1503
+ check[dt.strftime('%F %T'), dt2]
1504
+ check[Date.civil(2011, 1, 2), DateTime.civil(2011, 1, 2, 0, 0, 0, r1)]
1505
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, dt2]
1506
+
1507
+ ensure
1508
+ Sequel.default_timezone = nil
1509
+ Sequel.datetime_class = Time
1510
+ end
1511
+ end
1512
+
1513
+ specify "should typecast decimal values to BigDecimal" do
1514
+ [1.0, 1, '1.0', BigDecimal('1.0')].each do |i|
1515
+ v = @db.typecast_value(:decimal, i)
1516
+ v.should be_a_kind_of(BigDecimal)
1517
+ v.should == BigDecimal.new('1.0')
1518
+ end
1519
+ end
1520
+
1521
+ specify "should typecast float values to Float" do
1522
+ [1.0, 1, '1.0', BigDecimal('1.0')].each do |i|
1523
+ v = @db.typecast_value(:float, i)
1524
+ v.should be_a_kind_of(Float)
1525
+ v.should == 1.0
1526
+ end
1527
+ end
1528
+
1529
+ specify "should typecast string values to String" do
1530
+ [1.0, '1.0', '1.0'.to_sequel_blob].each do |i|
1531
+ v = @db.typecast_value(:string, i)
1532
+ v.should be_an_instance_of(String)
1533
+ v.should == "1.0"
1534
+ end
1535
+ end
1536
+
1537
+ specify "should typecast time values to SQLTime" do
1538
+ t = Time.now
1539
+ st = Sequel::SQLTime.local(t.year, t.month, t.day, 1, 2, 3)
1540
+ [st, Time.utc(t.year, t.month, t.day, 1, 2, 3), Time.local(t.year, t.month, t.day, 1, 2, 3), '01:02:03', {:hour=>1, :minute=>2, :second=>3}].each do |i|
1541
+ v = @db.typecast_value(:time, i)
1542
+ v.should be_an_instance_of(Sequel::SQLTime)
1543
+ v.should == st
1544
+ end
1545
+ end
1546
+
1347
1547
  specify "should have an underlying exception class available at wrapped_exception" do
1348
1548
  begin
1349
1549
  @db.typecast_value(:date, 'a')
@@ -1020,6 +1020,12 @@ describe "Dataset#literal" do
1020
1020
  d.literal(d).should == "(#{d.sql})"
1021
1021
  end
1022
1022
 
1023
+ specify "should literalize Sequel::SQLTime properly" do
1024
+ t = Sequel::SQLTime.now
1025
+ s = t.strftime("'%H:%M:%S")
1026
+ @dataset.literal(t).should == "#{s}.#{sprintf('%06i', t.usec)}'"
1027
+ end
1028
+
1023
1029
  specify "should literalize Time properly" do
1024
1030
  t = Time.now
1025
1031
  s = t.strftime("'%Y-%m-%d %H:%M:%S")
@@ -66,4 +66,14 @@ describe "prepared_statements_safe plugin" do
66
66
  @p.update(:i=>3)
67
67
  @sqls[1].should =~ /UPDATE people SET (name = 'foo'|i = 3), (name = 'foo'|i = 3) WHERE \(id = 1\)/
68
68
  end
69
+
70
+ specify "should work with abstract classes" do
71
+ c = Class.new(Sequel::Model)
72
+ c.plugin :prepared_statements_safe
73
+ c1 = Class.new(c)
74
+ c1.meta_def(:get_db_schema){@db_schema = {:i=>{:default=>'f(x)'}, :name=>{:ruby_default=>'foo'}, :id=>{:primary_key=>true}}}
75
+ c1.set_dataset(:people)
76
+ c1.prepared_statements_column_defaults.should == {:name=>'foo'}
77
+ Class.new(c1).prepared_statements_column_defaults.should == {:name=>'foo'}
78
+ end
69
79
  end
@@ -263,8 +263,8 @@ END_MIG
263
263
  s.each{|_, c| c[:ruby_default] = column_schema_to_ruby_default(c[:default], c[:type])}
264
264
  s
265
265
  end
266
- @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"
267
- @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"
266
+ @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=>Sequel::SQLTime.parse(\"10:20:30\"), :only_time=>true\n String :c10\nend"
267
+ @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=>Sequel::SQLTime.parse(\"10:20:30\")\n column :c10, \"interval\", :default=>\"'6 weeks'\".lit\nend"
268
268
  end
269
269
 
270
270
  it "should not use a '...'.lit as a fallback if using MySQL with the :same_db option" do
@@ -246,6 +246,15 @@ describe "Database schema modifiers" do
246
246
  @db.schema(:items, :reload=>true).map{|x| x.first}.should == [:number, :id]
247
247
  @ds.columns!.should == [:number, :id]
248
248
  @ds.map(:number).should == [10]
249
+ proc{@ds.insert(:id=>@ds.map(:id).first)}.should raise_error
250
+ end
251
+
252
+ cspecify "should drop primary key constraints from tables correctly", :sqlite do
253
+ @db.create_table!(:items){Integer :number; primary_key [:number], :name=>:items_pk}
254
+ @ds.insert(:number=>10)
255
+ @db.alter_table(:items){drop_constraint :items_pk, :type=>:primary_key}
256
+ @ds.map(:number).should == [10]
257
+ proc{@ds.insert(10)}.should_not raise_error
249
258
  end
250
259
 
251
260
  specify "should add foreign key columns to tables correctly" do
@@ -320,13 +329,33 @@ describe "Database schema modifiers" do
320
329
  end
321
330
 
322
331
  cspecify "should add unique constraints and foreign key table constraints correctly", :sqlite do
323
- @db.create_table!(:items){Integer :id; Integer :item_id}
332
+ @db.create_table!(:items, :engine=>:InnoDB){Integer :id; Integer :item_id}
324
333
  @db.alter_table(:items) do
325
334
  add_unique_constraint [:item_id, :id]
326
335
  add_foreign_key [:id, :item_id], :items, :key=>[:item_id, :id]
327
336
  end
328
337
  @db.schema(:items, :reload=>true).map{|x| x.first}.should == [:id, :item_id]
329
338
  @ds.columns!.should == [:id, :item_id]
339
+ proc{@ds.insert(1, 1)}.should_not raise_error
340
+ proc{@ds.insert(1, 1)}.should raise_error
341
+ proc{@ds.insert(1, 2)}.should raise_error
342
+ end
343
+
344
+ cspecify "should drop unique constraints and foreign key table constraints correctly", :sqlite do
345
+ @db.create_table!(:items) do
346
+ Integer :id
347
+ Integer :item_id
348
+ unique [:item_id, :id], :name=>:items_uk
349
+ foreign_key [:id, :item_id], :items, :key=>[:item_id, :id], :name=>:items_fk
350
+ end
351
+ @db.alter_table(:items) do
352
+ drop_constraint(:items_fk, :type=>:foreign_key)
353
+ drop_constraint(:items_uk, :type=>:unique)
354
+ end
355
+ @db.schema(:items, :reload=>true).map{|x| x.first}.should == [:id, :item_id]
356
+ @ds.columns!.should == [:id, :item_id]
357
+ proc{@ds.insert(1, 2)}.should_not raise_error
358
+ proc{@ds.insert(1, 2)}.should_not raise_error
330
359
  end
331
360
 
332
361
  cspecify "should remove columns from tables correctly", :h2, :mssql do
@@ -68,11 +68,18 @@ describe "Supported types" do
68
68
  ds.first[:dat].to_s.should == d.to_s
69
69
  end
70
70
 
71
- cspecify "should support generic time type", [:do], [:swift], [:odbc], [:jdbc, :mssql], [:tinytds] do
71
+ cspecify "should support generic time type", [:do], [:swift], [:odbc], [:jdbc, :mssql], [:jdbc, :postgres], [:mysql2], [:tinytds] do
72
72
  ds = create_items_table_with_column(:tim, Time, :only_time=>true)
73
- t = Time.now
73
+ t = Sequel::SQLTime.now
74
74
  ds.insert(:tim => t)
75
- ds.first[:tim].strftime('%H%M%S').should == t.strftime('%H%M%S')
75
+ v = ds.first[:tim]
76
+ ds.literal(v).should == ds.literal(t)
77
+ v.should be_a_kind_of(Sequel::SQLTime)
78
+ ds.delete
79
+ ds.insert(:tim => v)
80
+ v2 = ds.first[:tim]
81
+ ds.literal(v2).should == ds.literal(t)
82
+ v2.should be_a_kind_of(Sequel::SQLTime)
76
83
  end
77
84
 
78
85
  cspecify "should support generic datetime type", [:do, :sqlite], [:jdbc, :sqlite] do
@@ -131,6 +131,50 @@ describe Sequel::Model, ".def_dataset_method" do
131
131
  end
132
132
  end
133
133
 
134
+ describe Sequel::Model, ".dataset_module" do
135
+ before do
136
+ @c = Class.new(Sequel::Model(:items))
137
+ end
138
+
139
+ it "should extend the dataset with the module if the model has a dataset" do
140
+ @c.instance_eval{dataset_module{def return_3() 3 end}}
141
+ @c.dataset.return_3.should == 3
142
+ end
143
+
144
+ it "should add methods defined in the module to the class" do
145
+ @c.instance_eval{dataset_module{def return_3() 3 end}}
146
+ @c.return_3.should == 3
147
+ end
148
+
149
+ it "should cache calls and readd methods if set_dataset is used" do
150
+ @c.instance_eval{dataset_module{def return_3() 3 end}}
151
+ @c.set_dataset :items
152
+ @c.return_3.should == 3
153
+ @c.dataset.return_3.should == 3
154
+ end
155
+
156
+ it "should readd methods to subclasses, if set_dataset is used in a subclass" do
157
+ @c.instance_eval{dataset_module{def return_3() 3 end}}
158
+ c = Class.new(@c)
159
+ c.set_dataset :items
160
+ c.return_3.should == 3
161
+ c.dataset.return_3.should == 3
162
+ end
163
+
164
+ it "should only have a single dataset_module per class" do
165
+ @c.instance_eval{dataset_module{def return_3() 3 end}}
166
+ @c.instance_eval{dataset_module{def return_3() 3 + (begin; super; rescue NoMethodError; 1; end) end}}
167
+ @c.return_3.should == 4
168
+ end
169
+
170
+ it "should not have subclasses share the dataset_module" do
171
+ @c.instance_eval{dataset_module{def return_3() 3 end}}
172
+ c = Class.new(@c)
173
+ c.instance_eval{dataset_module{def return_3() 3 + (begin; super; rescue NoMethodError; 1; end) end}}
174
+ c.return_3.should == 6
175
+ end
176
+ end
177
+
134
178
  describe "A model class with implicit table name" do
135
179
  before do
136
180
  class Donkey < Sequel::Model
@@ -40,6 +40,13 @@ describe "Sequel::Model()" do
40
40
  c.table_name.should == :boo
41
41
  end
42
42
 
43
+ it "should return a model subclass with the given dataset if given a dataset using an SQL::Identifier" do
44
+ ds = @db[:blah.identifier]
45
+ c = Sequel::Model(ds)
46
+ c.superclass.should == Sequel::Model
47
+ c.dataset.should == ds
48
+ end
49
+
43
50
  it "should return a model subclass associated to the given database if given a database" do
44
51
  db = Sequel::Database.new
45
52
  c = Sequel::Model(db)
@@ -98,6 +105,13 @@ describe "Sequel::Model()" do
98
105
  class ::Album < Sequel::Model(@db[:table]); end
99
106
  end.should_not raise_error
100
107
  end
108
+
109
+ it "should work without raising an exception with a dataset with an SQL::Identifier" do
110
+ proc do
111
+ class ::Album < Sequel::Model(@db[:table.identifier]); end
112
+ class ::Album < Sequel::Model(@db[:table.identifier]); end
113
+ end.should_not raise_error
114
+ end
101
115
  end
102
116
  end
103
117