sequel 3.34.1 → 3.35.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 (101) hide show
  1. data/CHANGELOG +52 -0
  2. data/README.rdoc +3 -1
  3. data/Rakefile +2 -10
  4. data/doc/active_record.rdoc +1 -0
  5. data/doc/migration.rdoc +18 -7
  6. data/doc/model_hooks.rdoc +6 -0
  7. data/doc/opening_databases.rdoc +3 -0
  8. data/doc/prepared_statements.rdoc +0 -1
  9. data/doc/release_notes/3.35.0.txt +144 -0
  10. data/doc/schema_modification.rdoc +16 -1
  11. data/doc/thread_safety.rdoc +17 -0
  12. data/lib/sequel/adapters/do.rb +2 -2
  13. data/lib/sequel/adapters/do/postgres.rb +1 -52
  14. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  15. data/lib/sequel/adapters/firebird.rb +1 -1
  16. data/lib/sequel/adapters/ibmdb.rb +2 -2
  17. data/lib/sequel/adapters/jdbc.rb +23 -19
  18. data/lib/sequel/adapters/jdbc/db2.rb +0 -5
  19. data/lib/sequel/adapters/jdbc/derby.rb +29 -2
  20. data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
  21. data/lib/sequel/adapters/jdbc/h2.rb +1 -1
  22. data/lib/sequel/adapters/jdbc/hsqldb.rb +7 -0
  23. data/lib/sequel/adapters/jdbc/informix.rb +0 -5
  24. data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
  25. data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
  26. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -35
  27. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  28. data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
  29. data/lib/sequel/adapters/jdbc/transactions.rb +4 -4
  30. data/lib/sequel/adapters/mysql2.rb +1 -1
  31. data/lib/sequel/adapters/odbc.rb +3 -3
  32. data/lib/sequel/adapters/odbc/mssql.rb +14 -1
  33. data/lib/sequel/adapters/oracle.rb +6 -18
  34. data/lib/sequel/adapters/postgres.rb +36 -53
  35. data/lib/sequel/adapters/shared/db2.rb +16 -2
  36. data/lib/sequel/adapters/shared/mssql.rb +40 -9
  37. data/lib/sequel/adapters/shared/mysql.rb +16 -4
  38. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +2 -2
  39. data/lib/sequel/adapters/shared/oracle.rb +2 -0
  40. data/lib/sequel/adapters/shared/postgres.rb +135 -211
  41. data/lib/sequel/adapters/sqlite.rb +2 -2
  42. data/lib/sequel/adapters/swift.rb +1 -1
  43. data/lib/sequel/adapters/swift/postgres.rb +1 -71
  44. data/lib/sequel/adapters/tinytds.rb +3 -3
  45. data/lib/sequel/core.rb +27 -4
  46. data/lib/sequel/database/connecting.rb +7 -8
  47. data/lib/sequel/database/logging.rb +6 -1
  48. data/lib/sequel/database/misc.rb +20 -4
  49. data/lib/sequel/database/query.rb +38 -18
  50. data/lib/sequel/database/schema_generator.rb +5 -2
  51. data/lib/sequel/database/schema_methods.rb +34 -8
  52. data/lib/sequel/dataset/prepared_statements.rb +1 -1
  53. data/lib/sequel/dataset/sql.rb +18 -24
  54. data/lib/sequel/extensions/core_extensions.rb +0 -23
  55. data/lib/sequel/extensions/migration.rb +22 -8
  56. data/lib/sequel/extensions/pg_auto_parameterize.rb +4 -0
  57. data/lib/sequel/extensions/schema_dumper.rb +1 -1
  58. data/lib/sequel/model.rb +2 -2
  59. data/lib/sequel/model/associations.rb +95 -70
  60. data/lib/sequel/model/base.rb +16 -18
  61. data/lib/sequel/plugins/dirty.rb +214 -0
  62. data/lib/sequel/plugins/identity_map.rb +1 -1
  63. data/lib/sequel/plugins/json_serializer.rb +16 -1
  64. data/lib/sequel/plugins/many_through_many.rb +22 -32
  65. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +2 -2
  66. data/lib/sequel/plugins/prepared_statements.rb +22 -8
  67. data/lib/sequel/plugins/prepared_statements_associations.rb +2 -3
  68. data/lib/sequel/plugins/prepared_statements_with_pk.rb +1 -1
  69. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  70. data/lib/sequel/plugins/subclasses.rb +10 -2
  71. data/lib/sequel/plugins/timestamps.rb +1 -1
  72. data/lib/sequel/plugins/xml_serializer.rb +12 -1
  73. data/lib/sequel/sql.rb +1 -1
  74. data/lib/sequel/version.rb +2 -2
  75. data/spec/adapters/postgres_spec.rb +30 -79
  76. data/spec/core/database_spec.rb +46 -2
  77. data/spec/core/dataset_spec.rb +28 -22
  78. data/spec/core/schema_generator_spec.rb +1 -1
  79. data/spec/core/schema_spec.rb +51 -0
  80. data/spec/extensions/arbitrary_servers_spec.rb +0 -4
  81. data/spec/extensions/association_autoreloading_spec.rb +17 -0
  82. data/spec/extensions/association_proxies_spec.rb +4 -4
  83. data/spec/extensions/core_extensions_spec.rb +1 -24
  84. data/spec/extensions/dirty_spec.rb +155 -0
  85. data/spec/extensions/json_serializer_spec.rb +13 -0
  86. data/spec/extensions/migration_spec.rb +28 -15
  87. data/spec/extensions/named_timezones_spec.rb +6 -8
  88. data/spec/extensions/pg_auto_parameterize_spec.rb +6 -5
  89. data/spec/extensions/schema_dumper_spec.rb +3 -1
  90. data/spec/extensions/xml_serializer_spec.rb +13 -0
  91. data/spec/files/{transactionless_migrations → transaction_specified_migrations}/001_create_alt_basic.rb +1 -1
  92. data/spec/files/{transactionless_migrations → transaction_specified_migrations}/002_create_basic.rb +0 -0
  93. data/spec/files/{transaction_migrations → transaction_unspecified_migrations}/001_create_alt_basic.rb +0 -0
  94. data/spec/files/{transaction_migrations → transaction_unspecified_migrations}/002_create_basic.rb +0 -0
  95. data/spec/integration/associations_test.rb +5 -7
  96. data/spec/integration/dataset_test.rb +25 -7
  97. data/spec/integration/plugin_test.rb +1 -1
  98. data/spec/integration/schema_test.rb +16 -1
  99. data/spec/model/associations_spec.rb +2 -2
  100. metadata +14 -9
  101. data/lib/sequel/adapters/odbc/db2.rb +0 -17
@@ -53,8 +53,7 @@ module Sequel
53
53
  when :many_to_many
54
54
  association_bound_variable_hash(opts.join_table_alias, opts[:left_keys], opts[:left_primary_keys])
55
55
  when :many_through_many
56
- opts.reverse_edges
57
- association_bound_variable_hash(opts[:final_reverse_edge][:alias], Array(opts[:left_key]), opts[:left_primary_keys])
56
+ association_bound_variable_hash(opts.final_reverse_edge[:alias], Array(opts[:left_key]), opts[:left_primary_keys])
58
57
  end
59
58
  end
60
59
 
@@ -62,7 +61,7 @@ module Sequel
62
61
  # that, given appropriate bound variables, the prepared statement will work correctly for any
63
62
  # instance.
64
63
  def association_prepared_statement(opts)
65
- opts[:prepared_statement] ||= begin
64
+ opts.send(:cached_fetch, :prepared_statement) do
66
65
  ps = _associated_dataset(opts, {}).unbind.first.prepare(opts.returns_array? ? :select : :first, :"smpsap_#{NEXT.call}")
67
66
  ps.log_sql = true
68
67
  ps
@@ -31,7 +31,7 @@ module Sequel
31
31
  # Return a prepared statement that can be used to lookup a row given a dataset for the row matching
32
32
  # the primary key.
33
33
  def prepared_lookup_dataset(ds)
34
- @prepared_statements[:lookup_sql][ds.sql] ||= prepare_statement(ds.filter(prepared_statement_key_array(primary_key).map{|k, v| [SQL::QualifiedIdentifier.new(ds.model.table_name, k), v]}), :first)
34
+ cached_prepared_statement(:lookup_sql, ds.sql){prepare_statement(ds.filter(prepared_statement_key_array(primary_key).map{|k, v| [SQL::QualifiedIdentifier.new(ds.model.table_name, k), v]}), :first)}
35
35
  end
36
36
  end
37
37
 
@@ -131,7 +131,7 @@ module Sequel
131
131
  # keys for all of their descendant classes.
132
132
  def sti_subclass_added(key)
133
133
  if sti_key_array
134
- sti_key_array << key
134
+ Sequel.synchronize{sti_key_array << key}
135
135
  superclass.sti_subclass_added(key)
136
136
  end
137
137
  end
@@ -28,7 +28,7 @@ module Sequel
28
28
 
29
29
  # All descendent classes of this model.
30
30
  def descendents
31
- subclasses.map{|x| [x] + x.descendents}.flatten
31
+ Sequel.synchronize{_descendents}
32
32
  end
33
33
 
34
34
  # Add the subclass to this model's current subclasses,
@@ -36,9 +36,17 @@ module Sequel
36
36
  # in the subclass.
37
37
  def inherited(subclass)
38
38
  super
39
- subclasses << subclass
39
+ Sequel.synchronize{subclasses << subclass}
40
40
  subclass.instance_variable_set(:@subclasses, [])
41
41
  end
42
+
43
+ private
44
+
45
+ # Recursive, non-thread safe version of descendents, since
46
+ # the mutex Sequel uses isn't reentrant.
47
+ def _descendents
48
+ subclasses.map{|x| [x] + x.send(:_descendents)}.flatten
49
+ end
42
50
  end
43
51
  end
44
52
  end
@@ -19,7 +19,7 @@ module Sequel
19
19
  # # timestamp, and setting the update timestamp when creating
20
20
  # Album.plugin :timestamps, :force=>true, :update_on_create=>true
21
21
  module Timestamps
22
- # Configure the plugin by setting the avialable options. Note that
22
+ # Configure the plugin by setting the available options. Note that
23
23
  # if this method is run more than once, previous settings are ignored,
24
24
  # and it will just use the settings given or the default settings. Options:
25
25
  # * :create - The field to hold the create timestamp (default: :created_at)
@@ -79,6 +79,11 @@ module Sequel
79
79
  #
80
80
  # Album.array_from_xml(Album.to_xml) # same as Album.all
81
81
  #
82
+ # If you have an existing array of model instances you want to convert to
83
+ # XML, you can call the class to_xml method with the :array option:
84
+ #
85
+ # Album.to_xml(:array=>[Album[1], Album[2]])
86
+ #
82
87
  # Usage:
83
88
  #
84
89
  # # Add XML output capability to all model subclass instances (called before loading subclasses)
@@ -316,8 +321,14 @@ module Sequel
316
321
  raise(Sequel::Error, "Dataset#to_xml") unless row_proc
317
322
  x = model.xml_builder(opts)
318
323
  name_proc = model.xml_serialize_name_proc(opts)
324
+ array = if opts[:array]
325
+ opts = opts.dup
326
+ opts.delete(:array)
327
+ else
328
+ all
329
+ end
319
330
  x.send(name_proc[opts.fetch(:array_root_name, model.send(:pluralize, model.send(:underscore, model.name))).to_s]) do |x1|
320
- all.each do |obj|
331
+ array.each do |obj|
321
332
  obj.to_xml(opts.merge(:builder=>x1))
322
333
  end
323
334
  end
data/lib/sequel/sql.rb CHANGED
@@ -138,7 +138,7 @@ module Sequel
138
138
  MATHEMATICAL_OPERATORS = [:+, :-, :/, :*]
139
139
 
140
140
  # Bitwise mathematical operators used in +NumericMethods+
141
- BITWISE_OPERATORS = [:&, :|, :^, :<<, :>>]
141
+ BITWISE_OPERATORS = [:&, :|, :^, :<<, :>>, :%]
142
142
 
143
143
  # Inequality operators used in +InequalityMethods+
144
144
  INEQUALITY_OPERATORS = [:<, :>, :<=, :>=]
@@ -3,10 +3,10 @@ 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 = 34
6
+ MINOR = 35
7
7
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
8
8
  # releases that fix regressions from previous versions.
9
- TINY = 1
9
+ TINY = 0
10
10
 
11
11
  # The version of Sequel you are using, as a string (e.g. "2.11.0")
12
12
  VERSION = [MAJOR, MINOR, TINY].join('.')
@@ -23,7 +23,7 @@ def logger.method_missing(m, msg)
23
23
  end
24
24
  POSTGRES_DB.loggers << logger
25
25
 
26
- #POSTGRES_DB.instance_variable_set(:@server_version, 80100)
26
+ #POSTGRES_DB.instance_variable_set(:@server_version, 80200)
27
27
  POSTGRES_DB.create_table! :test do
28
28
  text :name
29
29
  integer :value, :index => true
@@ -111,9 +111,6 @@ describe "A PostgreSQL dataset" do
111
111
 
112
112
  @d.insert_sql(:value => 333).should =~ \
113
113
  /\AINSERT INTO "test" \("value"\) VALUES \(333\)( RETURNING NULL)?\z/
114
-
115
- @d.disable_insert_returning.insert_sql(:value => 333).should =~ \
116
- /\AINSERT INTO "test" \("value"\) VALUES \(333\)\z/
117
114
  end
118
115
  end
119
116
 
@@ -163,6 +160,25 @@ describe "A PostgreSQL dataset" do
163
160
  specify "should raise an error if attempting to update a joined dataset with a single FROM table" do
164
161
  proc{POSTGRES_DB[:test].join(:test2, [:name]).update(:name=>'a')}.should raise_error(Sequel::Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs')
165
162
  end
163
+
164
+ specify "should truncate with options" do
165
+ @d << { :name => 'abc', :value => 1}
166
+ @d.count.should == 1
167
+ @d.truncate(:cascade => true)
168
+ @d.count.should == 0
169
+ if @d.db.server_version > 80400
170
+ @d << { :name => 'abc', :value => 1}
171
+ @d.truncate(:cascade => true, :only=>true, :restart=>true)
172
+ @d.count.should == 0
173
+ end
174
+ end
175
+
176
+ specify "should truncate multiple tables at once" do
177
+ tables = [:test, :test2, :test3, :test4]
178
+ tables.each{|t| @d.from(t).insert}
179
+ @d.from(:test, :test2, :test3, :test4).truncate
180
+ tables.each{|t| @d.from(t).count.should == 0}
181
+ end
166
182
  end
167
183
 
168
184
  describe "Dataset#distinct" do
@@ -262,7 +278,7 @@ describe "A PostgreSQL dataset with a timestamp field" do
262
278
  t2 = @d[:value =>1][:time]
263
279
  @d.literal(t2).should == @d.literal(t)
264
280
  t2.strftime('%Y-%m-%d %H:%M:%S').should == t.strftime('%Y-%m-%d %H:%M:%S')
265
- t2.is_a?(Time) ? t2.usec : t2.strftime('%N').to_i/1000 == t.usec
281
+ (t2.is_a?(Time) ? t2.usec : t2.strftime('%N').to_i/1000).should == t.usec
266
282
  end
267
283
 
268
284
  cspecify "should store milliseconds in time fields for DateTime objects", :do, :swift do
@@ -271,7 +287,7 @@ describe "A PostgreSQL dataset with a timestamp field" do
271
287
  t2 = @d[:value =>1][:time]
272
288
  @d.literal(t2).should == @d.literal(t)
273
289
  t2.strftime('%Y-%m-%d %H:%M:%S').should == t.strftime('%Y-%m-%d %H:%M:%S')
274
- t2.is_a?(Time) ? t2.usec : t2.strftime('%N').to_i/1000 == t.strftime('%N').to_i/1000
290
+ (t2.is_a?(Time) ? t2.usec : t2.strftime('%N').to_i/1000).should == t.strftime('%N').to_i/1000
275
291
  end
276
292
 
277
293
  if POSTGRES_DB.adapter_scheme == :postgres
@@ -483,46 +499,24 @@ describe "Postgres::Dataset#import" do
483
499
  @db.drop_table?(:test)
484
500
  end
485
501
 
486
- specify "#import should return separate insert statements if server_version < 80200" do
487
- @ds.meta_def(:server_version){80199}
488
- @ds.import([:x, :y], [[1, 2], [3, 4]])
489
- @db.sqls.should == ['BEGIN', 'INSERT INTO "test" ("x", "y") VALUES (1, 2)', 'INSERT INTO "test" ("x", "y") VALUES (3, 4)', 'COMMIT'] if check_sqls
490
- @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
491
- end
492
502
 
493
- specify "#import should a single insert statement if server_version >= 80200" do
494
- @ds.meta_def(:server_version){80200}
503
+ specify "#import should a single insert statement" do
495
504
  @ds.import([:x, :y], [[1, 2], [3, 4]])
496
505
  @db.sqls.should == ['BEGIN', 'INSERT INTO "test" ("x", "y") VALUES (1, 2), (3, 4)', 'COMMIT']
497
506
  @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
498
507
  end
499
508
 
500
- specify "#import should work correctly when returning primary keys for server_version < 80200" do
501
- @ds.meta_def(:server_version){80199}
509
+ specify "#import should work correctly when returning primary keys" do
502
510
  @ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key).should == [1, 3]
503
511
  @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
504
512
  end
505
513
 
506
- specify "#import should work correctly when returning primary keys for server_version >= 80200" do
507
- @ds.meta_def(:server_version){80200}
508
- @ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key).should == [1, 3]
509
- @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
510
- end
511
-
512
- specify "#import should work correctly when returning primary keys with :slice option for server_version < 80200" do
513
- @ds.meta_def(:server_version){80199}
514
- @ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key, :slice=>1).should == [1, 3]
515
- @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
516
- end
517
-
518
- specify "#import should work correctly when returning primary keys with :slice option for server_version >= 80200" do
519
- @ds.meta_def(:server_version){80200}
514
+ specify "#import should work correctly when returning primary keys with :slice option" do
520
515
  @ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key, :slice=>1).should == [1, 3]
521
516
  @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
522
517
  end
523
518
 
524
519
  specify "#import should work correctly with an arbitrary returning value" do
525
- @ds.meta_def(:server_version){80200}
526
520
  @ds.returning(:y, :x).import([:x, :y], [[1, 2], [3, 4]]).should == [{:y=>2, :x=>1}, {:y=>4, :x=>3}]
527
521
  @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
528
522
  end
@@ -545,56 +539,17 @@ describe "Postgres::Dataset#insert" do
545
539
  @ds.all.should == [{:xid=>1, :value=>10}, {:xid=>2, :value=>20}]
546
540
  end
547
541
 
548
- specify "should work regardless of how it is used" do
549
- @ds.insert(:value=>10).should == 1
550
- @ds.disable_insert_returning.insert(:value=>20).should == 2
551
- @ds.meta_def(:server_version){80100}
552
- @ds.insert(:value=>13).should == 3
553
-
554
- @db.sqls.reject{|x| x =~ /pg_class/}.should == [
555
- 'INSERT INTO "test5" ("value") VALUES (10) RETURNING "xid"',
556
- 'INSERT INTO "test5" ("value") VALUES (20)',
557
- "SELECT currval('\"public\".test5_xid_seq')",
558
- 'INSERT INTO "test5" ("value") VALUES (13)',
559
- "SELECT currval('\"public\".test5_xid_seq')"
560
- ] if check_sqls
561
- @ds.all.should == [{:xid=>1, :value=>10}, {:xid=>2, :value=>20}, {:xid=>3, :value=>13}]
562
- end
563
-
564
- specify "should insert correctly if server_version < 80200" do
565
- @ds.meta_def(:server_version){80100}
566
- @ds.insert(:value=>10).should == 1
567
- @ds.all.should == [{:xid=>1, :value=>10}]
568
- end
569
-
570
- specify "should insert correctly if disabling insert returning" do
571
- @ds.disable_insert_returning.insert(:value=>10).should == 1
572
- @ds.all.should == [{:xid=>1, :value=>10}]
573
- end
574
-
575
- specify "should insert correctly if using a column array and a value array and server_version < 80200" do
576
- @ds.meta_def(:server_version){80100}
542
+ specify "should insert correctly if using a column array and a value array" do
577
543
  @ds.insert([:value], [10]).should == 1
578
544
  @ds.all.should == [{:xid=>1, :value=>10}]
579
545
  end
580
546
 
581
- specify "should use INSERT RETURNING if server_version >= 80200" do
582
- @ds.meta_def(:server_version){80201}
547
+ specify "should use INSERT RETURNING" do
583
548
  @ds.insert(:value=>10).should == 1
584
549
  @db.sqls.last.should == 'INSERT INTO "test5" ("value") VALUES (10) RETURNING "xid"' if check_sqls
585
550
  end
586
551
 
587
- specify "should have insert_select return nil if server_version < 80200" do
588
- @ds.meta_def(:server_version){80100}
589
- @ds.insert_select(:value=>10).should == nil
590
- end
591
-
592
- specify "should have insert_select return nil if disable_insert_returning is used" do
593
- @ds.disable_insert_returning.insert_select(:value=>10).should == nil
594
- end
595
-
596
- specify "should have insert_select insert the record and return the inserted record if server_version >= 80200" do
597
- @ds.meta_def(:server_version){80201}
552
+ specify "should have insert_select insert the record and return the inserted record" do
598
553
  h = @ds.insert_select(:value=>10)
599
554
  h[:value].should == 10
600
555
  @ds.first(:xid=>h[:xid])[:value].should == 10
@@ -1024,11 +979,7 @@ describe "Postgres::Database functions, languages, schemas, and triggers" do
1024
979
  @d.send(:drop_schema_sql, :sequel, :if_exists=>true, :cascade=>true).should == 'DROP SCHEMA IF EXISTS "sequel" CASCADE'
1025
980
  @d.create_schema(:sequel)
1026
981
  @d.create_table(:sequel__test){Integer :a}
1027
- if @d.server_version >= 80200
1028
- @d.drop_schema(:sequel, :if_exists=>true, :cascade=>true)
1029
- else
1030
- @d.drop_schema(:sequel, :cascade=>true)
1031
- end
982
+ @d.drop_schema(:sequel, :if_exists=>true, :cascade=>true)
1032
983
  end
1033
984
 
1034
985
  specify "#create_trigger and #drop_trigger should create and drop triggers" do
@@ -1237,7 +1188,7 @@ if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG && POSTGRE
1237
1188
  i = 0
1238
1189
  @db.listen('foo2', :timeout=>0.001, :loop=>proc{i+=1; throw :stop if i > 3}){|ev, pid, payload| called = true}.should == nil
1239
1190
  i.should == 4
1240
- end unless RUBY_VERSION == '1.9.2' && RUBY_PLATFORM =~ /mingw/ # Ruby freezes on this spec on this platform/version
1191
+ end unless RUBY_VERSION =~ /1.8.7|1.9.2/ && RUBY_PLATFORM =~ /mingw/ # Ruby freezes on this spec on this platform/version
1241
1192
  end
1242
1193
  end
1243
1194
 
@@ -193,8 +193,7 @@ describe "A new Database" do
193
193
  end
194
194
 
195
195
  specify "should populate :adapter option when using connection string" do
196
- Sequel::Database.should_receive(:adapter_class).once.with(:do).and_return(Sequel::Database)
197
- Sequel.connect('do:test://host/db_name').opts[:adapter] == :do
196
+ Sequel.connect('mock:/').opts[:adapter].should == "mock"
198
197
  end
199
198
  end
200
199
 
@@ -637,6 +636,45 @@ shared_examples_for "Database#transaction" do
637
636
  proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
638
637
  end
639
638
 
639
+ specify "should handle errors when sending BEGIN" do
640
+ ec = Class.new(StandardError)
641
+ @db.meta_def(:database_error_classes){[ec]}
642
+ @db.meta_def(:log_connection_execute){|c, sql| sql =~ /BEGIN/ ? raise(ec, 'bad') : super(c, sql)}
643
+ begin
644
+ @db.transaction{@db.execute 'DROP TABLE test;'}
645
+ rescue Sequel::DatabaseError => e
646
+ end
647
+ e.should_not be_nil
648
+ e.wrapped_exception.should be_a_kind_of(ec)
649
+ @db.sqls.should == ['ROLLBACK']
650
+ end
651
+
652
+ specify "should handle errors when sending COMMIT" do
653
+ ec = Class.new(StandardError)
654
+ @db.meta_def(:database_error_classes){[ec]}
655
+ @db.meta_def(:log_connection_execute){|c, sql| sql =~ /COMMIT/ ? raise(ec, 'bad') : super(c, sql)}
656
+ begin
657
+ @db.transaction{@db.execute 'DROP TABLE test;'}
658
+ rescue Sequel::DatabaseError => e
659
+ end
660
+ e.should_not be_nil
661
+ e.wrapped_exception.should be_a_kind_of(ec)
662
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;']
663
+ end
664
+
665
+ specify "should handle errors when sending ROLLBACK" do
666
+ ec = Class.new(StandardError)
667
+ @db.meta_def(:database_error_classes){[ec]}
668
+ @db.meta_def(:log_connection_execute){|c, sql| sql =~ /ROLLBACK/ ? raise(ec, 'bad') : super(c, sql)}
669
+ begin
670
+ @db.transaction{raise ArgumentError, 'asdf'}
671
+ rescue Sequel::DatabaseError => e
672
+ end
673
+ e.should_not be_nil
674
+ e.wrapped_exception.should be_a_kind_of(ec)
675
+ @db.sqls.should == ['BEGIN']
676
+ end
677
+
640
678
  specify "should issue ROLLBACK if Sequel::Rollback is called in the transaction" do
641
679
  @db.transaction do
642
680
  @db.drop_table(:a)
@@ -1952,6 +1990,12 @@ describe "Database#schema_autoincrementing_primary_key?" do
1952
1990
  end
1953
1991
  end
1954
1992
 
1993
+ describe "Database#supports_transactional_ddl?" do
1994
+ specify "should be false by default" do
1995
+ Sequel::Database.new.supports_transactional_ddl?.should == false
1996
+ end
1997
+ end
1998
+
1955
1999
  describe "Database#supports_savepoints?" do
1956
2000
  specify "should be false by default" do
1957
2001
  Sequel::Database.new.supports_savepoints?.should == false
@@ -214,6 +214,11 @@ describe "A simple dataset" do
214
214
  @dataset.truncate_sql.should == 'TRUNCATE TABLE test'
215
215
  end
216
216
 
217
+ specify "should format a truncate statement with multiple tables if supported" do
218
+ @dataset.meta_def(:check_truncation_allowed!){}
219
+ @dataset.from(:test, :test2).truncate_sql.should == 'TRUNCATE TABLE test, test2'
220
+ end
221
+
217
222
  specify "should format an insert statement with default values" do
218
223
  @dataset.insert_sql.should == 'INSERT INTO test DEFAULT VALUES'
219
224
  end
@@ -709,7 +714,7 @@ describe "Dataset#and" do
709
714
  specify "should raise if no filter exists" do
710
715
  proc {@dataset.and(:a => 1)}.should raise_error(Sequel::Error)
711
716
  proc {@dataset.where(:a => 1).group(:t).and(:b => 2)}.should_not raise_error(Sequel::Error)
712
- @dataset.where(:a => 1).group(:t).and(:b => 2).sql == "SELECT * FROM test WHERE (a = 1) AND (b = 2) GROUP BY t"
717
+ @dataset.where(:a => 1).group(:t).and(:b => 2).sql.should == "SELECT * FROM test WHERE ((a = 1) AND (b = 2)) GROUP BY t"
713
718
  end
714
719
 
715
720
  specify "should add an alternative expression to the where clause" do
@@ -987,9 +992,9 @@ describe "Dataset#literal" do
987
992
  @dataset.literal('a"x"bc').should == "'a\"x\"bc'"
988
993
  @dataset.literal("a'bc").should == "'a''bc'"
989
994
  @dataset.literal("a''bc").should == "'a''''bc'"
990
- @dataset.literal("a\\bc").should == "'a\\\\bc'"
991
- @dataset.literal("a\\\\bc").should == "'a\\\\\\\\bc'"
992
- @dataset.literal("a\\'bc").should == "'a\\\\''bc'"
995
+ @dataset.literal("a\\bc").should == "'a\\bc'"
996
+ @dataset.literal("a\\\\bc").should == "'a\\\\bc'"
997
+ @dataset.literal("a\\'bc").should == "'a\\''bc'"
993
998
  end
994
999
 
995
1000
  specify "should escape blobs as strings by default" do
@@ -3216,6 +3221,7 @@ describe "Dataset default #fetch_rows, #insert, #update, #delete, #with_sql_dele
3216
3221
 
3217
3222
  specify "#truncate should raise an InvalidOperation exception if the dataset is filtered" do
3218
3223
  proc{@ds.filter(:a=>1).truncate}.should raise_error(Sequel::InvalidOperation)
3224
+ proc{@ds.having(:a=>1).truncate}.should raise_error(Sequel::InvalidOperation)
3219
3225
  end
3220
3226
 
3221
3227
  specify "#execute should execute the SQL on the database" do
@@ -3677,42 +3683,42 @@ describe Sequel::SQL::Constants do
3677
3683
  end
3678
3684
 
3679
3685
  it "should have CURRENT_DATE" do
3680
- @db.literal(Sequel::SQL::Constants::CURRENT_DATE) == 'CURRENT_DATE'
3681
- @db.literal(Sequel::CURRENT_DATE) == 'CURRENT_DATE'
3686
+ @db.literal(Sequel::SQL::Constants::CURRENT_DATE).should == 'CURRENT_DATE'
3687
+ @db.literal(Sequel::CURRENT_DATE).should == 'CURRENT_DATE'
3682
3688
  end
3683
3689
 
3684
3690
  it "should have CURRENT_TIME" do
3685
- @db.literal(Sequel::SQL::Constants::CURRENT_TIME) == 'CURRENT_TIME'
3686
- @db.literal(Sequel::CURRENT_TIME) == 'CURRENT_TIME'
3691
+ @db.literal(Sequel::SQL::Constants::CURRENT_TIME).should == 'CURRENT_TIME'
3692
+ @db.literal(Sequel::CURRENT_TIME).should == 'CURRENT_TIME'
3687
3693
  end
3688
3694
 
3689
3695
  it "should have CURRENT_TIMESTAMP" do
3690
- @db.literal(Sequel::SQL::Constants::CURRENT_TIMESTAMP) == 'CURRENT_TIMESTAMP'
3691
- @db.literal(Sequel::CURRENT_TIMESTAMP) == 'CURRENT_TIMESTAMP'
3696
+ @db.literal(Sequel::SQL::Constants::CURRENT_TIMESTAMP).should == 'CURRENT_TIMESTAMP'
3697
+ @db.literal(Sequel::CURRENT_TIMESTAMP).should == 'CURRENT_TIMESTAMP'
3692
3698
  end
3693
3699
 
3694
3700
  it "should have NULL" do
3695
- @db.literal(Sequel::SQL::Constants::NULL) == 'NULL'
3696
- @db.literal(Sequel::NULL) == 'NULL'
3701
+ @db.literal(Sequel::SQL::Constants::NULL).should == 'NULL'
3702
+ @db.literal(Sequel::NULL).should == 'NULL'
3697
3703
  end
3698
3704
 
3699
3705
  it "should have NOTNULL" do
3700
- @db.literal(Sequel::SQL::Constants::NOTNULL) == 'NOT NULL'
3701
- @db.literal(Sequel::NOTNULL) == 'NOT NULL'
3706
+ @db.literal(Sequel::SQL::Constants::NOTNULL).should == 'NOT NULL'
3707
+ @db.literal(Sequel::NOTNULL).should == 'NOT NULL'
3702
3708
  end
3703
3709
 
3704
3710
  it "should have TRUE and SQLTRUE" do
3705
- @db.literal(Sequel::SQL::Constants::TRUE) == '1'
3706
- @db.literal(Sequel::TRUE) == '1'
3707
- @db.literal(Sequel::SQL::Constants::SQLTRUE) == '1'
3708
- @db.literal(Sequel::SQLTRUE) == '1'
3711
+ @db.literal(Sequel::SQL::Constants::TRUE).should == "'t'"
3712
+ @db.literal(Sequel::TRUE).should == "'t'"
3713
+ @db.literal(Sequel::SQL::Constants::SQLTRUE).should == "'t'"
3714
+ @db.literal(Sequel::SQLTRUE).should == "'t'"
3709
3715
  end
3710
3716
 
3711
3717
  it "should have FALSE and SQLFALSE" do
3712
- @db.literal(Sequel::SQL::Constants::FALSE) == '0'
3713
- @db.literal(Sequel::FALSE) == '0'
3714
- @db.literal(Sequel::SQL::Constants::SQLFALSE) == '0'
3715
- @db.literal(Sequel::SQLFALSE) == '0'
3718
+ @db.literal(Sequel::SQL::Constants::FALSE).should == "'f'"
3719
+ @db.literal(Sequel::FALSE).should == "'f'"
3720
+ @db.literal(Sequel::SQL::Constants::SQLFALSE).should == "'f'"
3721
+ @db.literal(Sequel::SQLFALSE).should == "'f'"
3716
3722
  end
3717
3723
  end
3718
3724