sequel 3.34.1 → 3.35.0

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