sequel 2.7.1 → 2.8.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.
- data/CHANGELOG +56 -0
- data/README +1 -0
- data/Rakefile +1 -1
- data/lib/sequel_core.rb +9 -16
- data/lib/sequel_core/adapters/ado.rb +6 -15
- data/lib/sequel_core/adapters/db2.rb +8 -10
- data/lib/sequel_core/adapters/dbi.rb +6 -4
- data/lib/sequel_core/adapters/informix.rb +21 -22
- data/lib/sequel_core/adapters/jdbc.rb +69 -10
- data/lib/sequel_core/adapters/jdbc/postgresql.rb +1 -0
- data/lib/sequel_core/adapters/mysql.rb +81 -13
- data/lib/sequel_core/adapters/odbc.rb +32 -4
- data/lib/sequel_core/adapters/openbase.rb +6 -5
- data/lib/sequel_core/adapters/oracle.rb +23 -7
- data/lib/sequel_core/adapters/postgres.rb +42 -32
- data/lib/sequel_core/adapters/shared/mssql.rb +37 -62
- data/lib/sequel_core/adapters/shared/mysql.rb +22 -7
- data/lib/sequel_core/adapters/shared/oracle.rb +27 -48
- data/lib/sequel_core/adapters/shared/postgres.rb +64 -43
- data/lib/sequel_core/adapters/shared/progress.rb +31 -0
- data/lib/sequel_core/adapters/shared/sqlite.rb +15 -4
- data/lib/sequel_core/adapters/sqlite.rb +6 -14
- data/lib/sequel_core/connection_pool.rb +47 -13
- data/lib/sequel_core/database.rb +60 -35
- data/lib/sequel_core/database/schema.rb +4 -4
- data/lib/sequel_core/dataset.rb +12 -3
- data/lib/sequel_core/dataset/convenience.rb +4 -13
- data/lib/sequel_core/dataset/prepared_statements.rb +30 -28
- data/lib/sequel_core/dataset/sql.rb +144 -85
- data/lib/sequel_core/dataset/stored_procedures.rb +75 -0
- data/lib/sequel_core/dataset/unsupported.rb +31 -0
- data/lib/sequel_core/exceptions.rb +6 -0
- data/lib/sequel_core/schema/generator.rb +4 -3
- data/lib/sequel_core/schema/sql.rb +41 -23
- data/lib/sequel_core/sql.rb +29 -1
- data/lib/sequel_model/associations.rb +1 -1
- data/lib/sequel_model/record.rb +31 -28
- data/spec/adapters/mysql_spec.rb +37 -4
- data/spec/adapters/oracle_spec.rb +26 -4
- data/spec/adapters/sqlite_spec.rb +7 -0
- data/spec/integration/prepared_statement_test.rb +24 -0
- data/spec/integration/schema_test.rb +1 -1
- data/spec/sequel_core/connection_pool_spec.rb +49 -2
- data/spec/sequel_core/core_sql_spec.rb +9 -2
- data/spec/sequel_core/database_spec.rb +64 -14
- data/spec/sequel_core/dataset_spec.rb +105 -7
- data/spec/sequel_core/schema_spec.rb +40 -12
- data/spec/sequel_core/spec_helper.rb +1 -0
- data/spec/sequel_model/spec_helper.rb +1 -0
- metadata +6 -3
data/spec/adapters/mysql_spec.rb
CHANGED
@@ -12,7 +12,6 @@ unless defined?(MYSQL_SOCKET_FILE)
|
|
12
12
|
end
|
13
13
|
|
14
14
|
MYSQL_URI = URI.parse(MYSQL_DB.uri)
|
15
|
-
MYSQL_DB_NAME = (m = /\/(.*)/.match(MYSQL_URI.path)) && m[1]
|
16
15
|
|
17
16
|
MYSQL_DB.create_table! :items do
|
18
17
|
text :name
|
@@ -482,23 +481,32 @@ context "A MySQL database" do
|
|
482
481
|
@db << 'DELETE FROM items'
|
483
482
|
@db[:items].first.should == nil
|
484
483
|
end
|
484
|
+
|
485
|
+
specify "should handle multiple select statements at once" do
|
486
|
+
@db << 'DELETE FROM items; '
|
487
|
+
|
488
|
+
@db[:items].delete
|
489
|
+
@db[:items].insert(:name => 'tutu', :value => 1234)
|
490
|
+
@db["SELECT * FROM items; SELECT * FROM items"].all.should == \
|
491
|
+
[{:name => 'tutu', :value => 1234}, {:name => 'tutu', :value => 1234}]
|
492
|
+
end
|
485
493
|
end
|
486
494
|
|
487
495
|
# Socket tests should only be run if the MySQL server is on localhost
|
488
496
|
if %w'localhost 127.0.0.1 ::1'.include? MYSQL_URI.host
|
489
497
|
context "A MySQL database" do
|
490
498
|
specify "should accept a socket option" do
|
491
|
-
db = Sequel.mysql(
|
499
|
+
db = Sequel.mysql(MYSQL_DB.opts[:database], :host => 'localhost', :user => MYSQL_DB.opts[:user], :password => MYSQL_DB.opts[:password], :socket => MYSQL_SOCKET_FILE)
|
492
500
|
proc {db.test_connection}.should_not raise_error
|
493
501
|
end
|
494
502
|
|
495
503
|
specify "should accept a socket option without host option" do
|
496
|
-
db = Sequel.mysql(
|
504
|
+
db = Sequel.mysql(MYSQL_DB.opts[:database], :user => MYSQL_DB.opts[:user], :password => MYSQL_DB.opts[:password], :socket => MYSQL_SOCKET_FILE)
|
497
505
|
proc {db.test_connection}.should_not raise_error
|
498
506
|
end
|
499
507
|
|
500
508
|
specify "should fail to connect with invalid socket" do
|
501
|
-
db = Sequel.mysql(
|
509
|
+
db = Sequel.mysql(MYSQL_DB.opts[:database], :user => MYSQL_DB.opts[:user], :password => MYSQL_DB.opts[:password], :socket =>'blah')
|
502
510
|
proc {db.test_connection}.should raise_error
|
503
511
|
end
|
504
512
|
end
|
@@ -761,3 +769,28 @@ context "MySQL::Dataset#complex_expression_sql" do
|
|
761
769
|
@d.literal([:x].sql_string_join(' ')).should == "x"
|
762
770
|
end
|
763
771
|
end
|
772
|
+
|
773
|
+
context "MySQL Stored Procedures" do
|
774
|
+
teardown do
|
775
|
+
MYSQL_DB.execute('DROP PROCEDURE test_sproc')
|
776
|
+
end
|
777
|
+
|
778
|
+
specify "should be callable on the database object" do
|
779
|
+
MYSQL_DB.execute('CREATE PROCEDURE test_sproc() BEGIN DELETE FROM items; END')
|
780
|
+
MYSQL_DB[:items].delete
|
781
|
+
MYSQL_DB[:items].insert(:value=>1)
|
782
|
+
MYSQL_DB[:items].count.should == 1
|
783
|
+
MYSQL_DB.call_sproc(:test_sproc)
|
784
|
+
MYSQL_DB[:items].count.should == 0
|
785
|
+
end
|
786
|
+
|
787
|
+
specify "should be callable on the dataset object" do
|
788
|
+
MYSQL_DB.execute('CREATE PROCEDURE test_sproc(a INTEGER) BEGIN SELECT *, a AS b FROM items; END')
|
789
|
+
@d = MYSQL_DB[:items]
|
790
|
+
@d.call_sproc(:select, :test_sproc, 3).should == []
|
791
|
+
@d.insert(:value=>1)
|
792
|
+
@d.call_sproc(:select, :test_sproc, 4).should == [{:id=>nil, :value=>1, :b=>4}]
|
793
|
+
@d.row_proc = proc{|r| r.keys.each{|k| r[k] *= 2 if r[k].is_a?(Integer)}; r}
|
794
|
+
@d.call_sproc(:select, :test_sproc, 3).should == [{:id=>nil, :value=>2, :b=>6}]
|
795
|
+
end
|
796
|
+
end
|
@@ -71,7 +71,7 @@ context "An Oracle dataset" do
|
|
71
71
|
{:name => 'def'}
|
72
72
|
]
|
73
73
|
|
74
|
-
@d.order(:value.
|
74
|
+
@d.order(:value.desc).limit(1).to_a.should == [
|
75
75
|
{:name => 'def', :value => 789}
|
76
76
|
]
|
77
77
|
|
@@ -80,7 +80,7 @@ context "An Oracle dataset" do
|
|
80
80
|
{:name => 'abc', :value => 456}
|
81
81
|
]
|
82
82
|
|
83
|
-
@d.order(:value.
|
83
|
+
@d.order(:value.desc).filter(:name => 'abc').to_a.should == [
|
84
84
|
{:name => 'abc', :value => 456},
|
85
85
|
{:name => 'abc', :value => 123}
|
86
86
|
]
|
@@ -89,7 +89,7 @@ context "An Oracle dataset" do
|
|
89
89
|
{:name => 'abc', :value => 123}
|
90
90
|
]
|
91
91
|
|
92
|
-
@d.filter(:name => 'abc').order(:value.
|
92
|
+
@d.filter(:name => 'abc').order(:value.desc).limit(1).to_a.should == [
|
93
93
|
{:name => 'abc', :value => 456}
|
94
94
|
]
|
95
95
|
|
@@ -214,9 +214,31 @@ context "Joined Oracle dataset" do
|
|
214
214
|
{:id => 4, :title => 'ddd', :cat_name => nil}
|
215
215
|
]
|
216
216
|
|
217
|
-
@d1.left_outer_join(:categories, :id => :category_id).select(:books__id, :title, :cat_name).order(:books__id.
|
217
|
+
@d1.left_outer_join(:categories, :id => :category_id).select(:books__id, :title, :cat_name).order(:books__id.desc).limit(2, 0).to_a.should == [
|
218
218
|
{:id => 4, :title => 'ddd', :cat_name => nil},
|
219
219
|
{:id => 3, :title => 'ccc', :cat_name => 'rails'}
|
220
220
|
]
|
221
221
|
end
|
222
222
|
end
|
223
|
+
|
224
|
+
context "Oracle aliasing" do
|
225
|
+
setup do
|
226
|
+
@d1 = ORACLE_DB[:books]
|
227
|
+
@d1.delete # remove all records
|
228
|
+
@d1 << {:id => 1, :title => 'aaa', :category_id => 100}
|
229
|
+
@d1 << {:id => 2, :title => 'bbb', :category_id => 100}
|
230
|
+
@d1 << {:id => 3, :title => 'bbb', :category_id => 100}
|
231
|
+
end
|
232
|
+
|
233
|
+
specify "should allow columns to be renamed" do
|
234
|
+
@d1.select(:title.as(:name)).order_by(:id).to_a.should == [
|
235
|
+
{ :name => 'aaa' },
|
236
|
+
{ :name => 'bbb' },
|
237
|
+
{ :name => 'bbb' },
|
238
|
+
]
|
239
|
+
end
|
240
|
+
|
241
|
+
specify "nested queries should work" do
|
242
|
+
@d1.select(:title).group_by(:title).count.should == 2
|
243
|
+
end
|
244
|
+
end
|
@@ -415,6 +415,13 @@ context "A SQLite database" do
|
|
415
415
|
@db[:test2].first.should == {:name => 'mmm'}
|
416
416
|
end
|
417
417
|
|
418
|
+
specify "should support drop_column operations in a transaction" do
|
419
|
+
@db.transaction{@db.drop_column :test2, :value}
|
420
|
+
@db[:test2].columns.should == [:name]
|
421
|
+
@db[:test2] << {:name => 'mmm'}
|
422
|
+
@db[:test2].first.should == {:name => 'mmm'}
|
423
|
+
end
|
424
|
+
|
418
425
|
specify "should not support rename_column operations" do
|
419
426
|
proc {@db.rename_column :test2, :value, :zyx}.should raise_error(Sequel::Error)
|
420
427
|
end
|
@@ -24,6 +24,30 @@ describe "Prepared Statements and Bound Arguments" do
|
|
24
24
|
@ds.filter(:number=>@ds.ba(:$n)).call(:first, :n=>10).should == {:id=>1, :number=>10}
|
25
25
|
end
|
26
26
|
|
27
|
+
specify "should support placeholder literal strings" do
|
28
|
+
@ds.filter("number = ?", @ds.ba(:$n)).call(:select, :n=>10).should == [{:id=>1, :number=>10}]
|
29
|
+
end
|
30
|
+
|
31
|
+
specify "should support datasets with static sql and placeholders" do
|
32
|
+
INTEGRATION_DB["SELECT * FROM items WHERE number = ?", @ds.ba(:$n)].call(:select, :n=>10).should == [{:id=>1, :number=>10}]
|
33
|
+
end
|
34
|
+
|
35
|
+
specify "should support subselects" do
|
36
|
+
@ds.filter(:id=>:$i).filter(:number=>@ds.select(:number).filter(:number=>@ds.ba(:$n))).filter(:id=>:$j).call(:select, :n=>10, :i=>1, :j=>1).should == [{:id=>1, :number=>10}]
|
37
|
+
end
|
38
|
+
|
39
|
+
specify "should support subselects with literal strings" do
|
40
|
+
@ds.filter(:id=>:$i, :number=>@ds.select(:number).filter("number = ?", @ds.ba(:$n))).call(:select, :n=>10, :i=>1).should == [{:id=>1, :number=>10}]
|
41
|
+
end
|
42
|
+
|
43
|
+
specify "should support subselects with static sql and placeholders" do
|
44
|
+
@ds.filter(:id=>:$i, :number=>INTEGRATION_DB["SELECT number FROM items WHERE number = ?", @ds.ba(:$n)]).call(:select, :n=>10, :i=>1).should == [{:id=>1, :number=>10}]
|
45
|
+
end
|
46
|
+
|
47
|
+
specify "should support subselects of subselects" do
|
48
|
+
@ds.filter(:id=>:$i).filter(:number=>@ds.select(:number).filter(:number=>@ds.select(:number).filter(:number=>@ds.ba(:$n)))).filter(:id=>:$j).call(:select, :n=>10, :i=>1, :j=>1).should == [{:id=>1, :number=>10}]
|
49
|
+
end
|
50
|
+
|
27
51
|
specify "should support bound variables with insert" do
|
28
52
|
@ds.call(:insert, {:n=>20, :i=>100}, :id=>@ds.ba(:$i), :number=>@ds.ba(:$n))
|
29
53
|
@ds.count.should == 2
|
@@ -9,7 +9,7 @@ describe "Database schema parser" do
|
|
9
9
|
INTEGRATION_DB.create_table!(:items){integer :number}
|
10
10
|
schema = INTEGRATION_DB.schema(nil, :reload=>true)
|
11
11
|
schema.should be_a_kind_of(Hash)
|
12
|
-
schema.
|
12
|
+
schema[:items].should_not == nil
|
13
13
|
end
|
14
14
|
|
15
15
|
specify "should not issue an sql query if the schema has been loaded unless :reload is true" do
|
@@ -23,7 +23,7 @@ end
|
|
23
23
|
context "A connection pool handling connections" do
|
24
24
|
setup do
|
25
25
|
@max_size = 2
|
26
|
-
@cpool = Sequel::ConnectionPool.new(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>@max_size)) {:got_connection}
|
26
|
+
@cpool = Sequel::ConnectionPool.new(CONNECTION_POOL_DEFAULTS.merge(:disconnection_proc=>proc{|c| @max_size=3}, :max_connections=>@max_size)) {:got_connection}
|
27
27
|
end
|
28
28
|
|
29
29
|
specify "#hold should increment #created_count" do
|
@@ -61,6 +61,31 @@ context "A connection pool handling connections" do
|
|
61
61
|
@cpool.send(:make_new, :default).should == nil
|
62
62
|
@cpool.created_count.should == 2
|
63
63
|
end
|
64
|
+
|
65
|
+
specify ":disconnection_proc option should set the disconnection proc to use" do
|
66
|
+
@max_size.should == 2
|
67
|
+
proc{@cpool.hold{raise Sequel::DatabaseDisconnectError}}.should raise_error(Sequel::DatabaseDisconnectError)
|
68
|
+
@max_size.should == 3
|
69
|
+
end
|
70
|
+
|
71
|
+
specify "#disconnection_proc= should set the disconnection proc to use" do
|
72
|
+
a = 1
|
73
|
+
@cpool.disconnection_proc = proc{|c| a += 1}
|
74
|
+
proc{@cpool.hold{raise Sequel::DatabaseDisconnectError}}.should raise_error(Sequel::DatabaseDisconnectError)
|
75
|
+
a.should == 2
|
76
|
+
end
|
77
|
+
|
78
|
+
specify "#hold should remove the connection if a DatabaseDisconnectError is raised" do
|
79
|
+
@cpool.created_count.should == 0
|
80
|
+
@cpool.hold{Thread.new{@cpool.hold{}}; sleep 0.01}
|
81
|
+
@cpool.created_count.should == 2
|
82
|
+
proc{@cpool.hold{raise Sequel::DatabaseDisconnectError}}.should raise_error(Sequel::DatabaseDisconnectError)
|
83
|
+
@cpool.created_count.should == 1
|
84
|
+
proc{@cpool.hold{raise Sequel::DatabaseDisconnectError}}.should raise_error(Sequel::DatabaseDisconnectError)
|
85
|
+
@cpool.created_count.should == 0
|
86
|
+
proc{@cpool.hold{raise Sequel::DatabaseDisconnectError}}.should raise_error(Sequel::DatabaseDisconnectError)
|
87
|
+
@cpool.created_count.should == 0
|
88
|
+
end
|
64
89
|
end
|
65
90
|
|
66
91
|
class DummyConnection
|
@@ -423,7 +448,8 @@ end
|
|
423
448
|
|
424
449
|
context "A single threaded pool with multiple servers" do
|
425
450
|
setup do
|
426
|
-
@
|
451
|
+
@max_size=2
|
452
|
+
@pool = Sequel::SingleThreadedPool.new(CONNECTION_POOL_DEFAULTS.merge(:disconnection_proc=>proc{|c| @max_size=3}, :servers=>{:read_only=>{}})){|server| server}
|
427
453
|
end
|
428
454
|
|
429
455
|
specify "should use the :default server by default" do
|
@@ -462,4 +488,25 @@ context "A single threaded pool with multiple servers" do
|
|
462
488
|
@pool.conn.should == nil
|
463
489
|
@pool.conn(:read_only).should == nil
|
464
490
|
end
|
491
|
+
|
492
|
+
specify ":disconnection_proc option should set the disconnection proc to use" do
|
493
|
+
@max_size.should == 2
|
494
|
+
proc{@pool.hold{raise Sequel::DatabaseDisconnectError}}.should raise_error(Sequel::DatabaseDisconnectError)
|
495
|
+
@max_size.should == 3
|
496
|
+
end
|
497
|
+
|
498
|
+
specify "#disconnection_proc= should set the disconnection proc to use" do
|
499
|
+
a = 1
|
500
|
+
@pool.disconnection_proc = proc{|c| a += 1}
|
501
|
+
proc{@pool.hold{raise Sequel::DatabaseDisconnectError}}.should raise_error(Sequel::DatabaseDisconnectError)
|
502
|
+
a.should == 2
|
503
|
+
end
|
504
|
+
|
505
|
+
specify "#hold should remove the connection if a DatabaseDisconnectError is raised" do
|
506
|
+
@pool.instance_variable_get(:@conns).length.should == 0
|
507
|
+
@pool.hold{}
|
508
|
+
@pool.instance_variable_get(:@conns).length.should == 1
|
509
|
+
proc{@pool.hold{raise Sequel::DatabaseDisconnectError}}.should raise_error(Sequel::DatabaseDisconnectError)
|
510
|
+
@pool.instance_variable_get(:@conns).length.should == 0
|
511
|
+
end
|
465
512
|
end
|
@@ -259,12 +259,18 @@ context "Symbol#*" do
|
|
259
259
|
:xyz.*(3).to_s(@ds).should == '(xyz * 3)'
|
260
260
|
:abc.*(5).to_s(@ds).should == '(abc * 5)'
|
261
261
|
end
|
262
|
+
|
263
|
+
specify "should support qualified symbols if no argument" do
|
264
|
+
:xyz__abc.*.to_s(@ds).should == 'xyz.abc.*'
|
265
|
+
end
|
266
|
+
|
262
267
|
end
|
263
268
|
|
264
269
|
context "Symbol" do
|
265
270
|
before do
|
266
271
|
@ds = Sequel::Dataset.new(nil)
|
267
272
|
@ds.quote_identifiers = true
|
273
|
+
@ds.upcase_identifiers = true
|
268
274
|
end
|
269
275
|
|
270
276
|
specify "#identifier should format an identifier" do
|
@@ -276,11 +282,12 @@ context "Symbol" do
|
|
276
282
|
end
|
277
283
|
|
278
284
|
specify "should be able to qualify an identifier" do
|
279
|
-
@ds.literal(:xyz.identifier.qualify(:xyz__abc)).should == '"
|
285
|
+
@ds.literal(:xyz.identifier.qualify(:xyz__abc)).should == '"XYZ"."ABC"."XYZ"'
|
280
286
|
end
|
281
287
|
|
282
288
|
specify "should be able to specify a schema.table.column" do
|
283
|
-
@ds.literal(:column.qualify(:
|
289
|
+
@ds.literal(:column.qualify(:table.qualify(:schema))).should == '"SCHEMA"."TABLE"."COLUMN"'
|
290
|
+
@ds.literal(:column.qualify(:table__name.identifier.qualify(:schema))).should == '"SCHEMA"."TABLE__NAME"."COLUMN"'
|
284
291
|
end
|
285
292
|
end
|
286
293
|
|
@@ -6,6 +6,7 @@ context "A new Database" do
|
|
6
6
|
end
|
7
7
|
teardown do
|
8
8
|
Sequel.quote_identifiers = false
|
9
|
+
Sequel.upcase_identifiers = false
|
9
10
|
end
|
10
11
|
|
11
12
|
specify "should receive options" do
|
@@ -39,15 +40,41 @@ context "A new Database" do
|
|
39
40
|
cc.should == 1234
|
40
41
|
end
|
41
42
|
|
42
|
-
specify "should respect the :
|
43
|
-
db = Sequel::Database.new(:
|
44
|
-
db.quote_identifiers?.should == false
|
43
|
+
specify "should respect the :single_threaded option" do
|
44
|
+
db = Sequel::Database.new(:single_threaded=>true)
|
45
45
|
db.pool.should be_a_kind_of(Sequel::SingleThreadedPool)
|
46
|
-
db = Sequel::Database.new(:
|
47
|
-
db.quote_identifiers?.should == true
|
46
|
+
db = Sequel::Database.new(:single_threaded=>false)
|
48
47
|
db.pool.should be_a_kind_of(Sequel::ConnectionPool)
|
49
48
|
end
|
50
49
|
|
50
|
+
specify "should respect the :quote_identifiers option" do
|
51
|
+
db = Sequel::Database.new(:quote_identifiers=>false)
|
52
|
+
db.quote_identifiers?.should == false
|
53
|
+
db = Sequel::Database.new(:quote_identifiers=>true)
|
54
|
+
db.quote_identifiers?.should == true
|
55
|
+
end
|
56
|
+
|
57
|
+
specify "should respect the :upcase_identifiers option" do
|
58
|
+
Sequel.upcase_identifiers = false
|
59
|
+
db = Sequel::Database.new(:upcase_identifiers=>false)
|
60
|
+
db.upcase_identifiers?.should == false
|
61
|
+
db.upcase_identifiers = true
|
62
|
+
db.upcase_identifiers?.should == true
|
63
|
+
db = Sequel::Database.new(:upcase_identifiers=>true)
|
64
|
+
db.upcase_identifiers?.should == true
|
65
|
+
db.upcase_identifiers = false
|
66
|
+
db.upcase_identifiers?.should == false
|
67
|
+
Sequel.upcase_identifiers = true
|
68
|
+
db = Sequel::Database.new(:upcase_identifiers=>false)
|
69
|
+
db.upcase_identifiers?.should == false
|
70
|
+
db.upcase_identifiers = true
|
71
|
+
db.upcase_identifiers?.should == true
|
72
|
+
db = Sequel::Database.new(:upcase_identifiers=>true)
|
73
|
+
db.upcase_identifiers?.should == true
|
74
|
+
db.upcase_identifiers = false
|
75
|
+
db.upcase_identifiers?.should == false
|
76
|
+
end
|
77
|
+
|
51
78
|
specify "should use the default Sequel.quote_identifiers value" do
|
52
79
|
Sequel.quote_identifiers = true
|
53
80
|
Sequel::Database.new({}).quote_identifiers?.should == true
|
@@ -59,6 +86,26 @@ context "A new Database" do
|
|
59
86
|
Sequel::Database.new({}).quote_identifiers?.should == false
|
60
87
|
end
|
61
88
|
|
89
|
+
specify "should use the default Sequel.upcase_identifiers value" do
|
90
|
+
Sequel.upcase_identifiers = true
|
91
|
+
Sequel::Database.new({}).upcase_identifiers?.should == true
|
92
|
+
Sequel.upcase_identifiers = false
|
93
|
+
Sequel::Database.new({}).upcase_identifiers?.should == false
|
94
|
+
Sequel::Database.upcase_identifiers = true
|
95
|
+
Sequel::Database.new({}).upcase_identifiers?.should == true
|
96
|
+
Sequel::Database.upcase_identifiers = false
|
97
|
+
Sequel::Database.new({}).upcase_identifiers?.should == false
|
98
|
+
end
|
99
|
+
|
100
|
+
specify "should respect the upcase_indentifiers_default method if Sequel.upcase_identifiers = nil" do
|
101
|
+
Sequel.upcase_identifiers = nil
|
102
|
+
Sequel::Database.new({}).upcase_identifiers?.should == true
|
103
|
+
x = Class.new(Sequel::Database){def upcase_identifiers_default; false end}
|
104
|
+
x.new({}).upcase_identifiers?.should == false
|
105
|
+
y = Class.new(Sequel::Database){def upcase_identifiers_default; true end}
|
106
|
+
y.new({}).upcase_identifiers?.should == true
|
107
|
+
end
|
108
|
+
|
62
109
|
specify "should just use a :uri option for jdbc with the full connection string" do
|
63
110
|
Sequel::Database.should_receive(:adapter_class).once.with(:jdbc).and_return(Sequel::Database)
|
64
111
|
db = Sequel.connect('jdbc:test://host/db_name')
|
@@ -67,15 +114,20 @@ context "A new Database" do
|
|
67
114
|
end
|
68
115
|
end
|
69
116
|
|
70
|
-
context "Database#
|
71
|
-
specify "should
|
72
|
-
|
117
|
+
context "Database#disconnect" do
|
118
|
+
specify "should call pool.disconnect" do
|
119
|
+
d = Sequel::Database.new
|
120
|
+
p = d.pool
|
121
|
+
a = 1
|
122
|
+
p.meta_def(:disconnect){a += 1}
|
123
|
+
d.disconnect.should == 2
|
124
|
+
a.should == 2
|
73
125
|
end
|
74
126
|
end
|
75
127
|
|
76
|
-
context "Database#
|
128
|
+
context "Database#connect" do
|
77
129
|
specify "should raise Sequel::Error::NotImplemented" do
|
78
|
-
proc {Sequel::Database.new.
|
130
|
+
proc {Sequel::Database.new.connect}.should raise_error(NotImplementedError)
|
79
131
|
end
|
80
132
|
end
|
81
133
|
|
@@ -437,14 +489,12 @@ end
|
|
437
489
|
context "Database#table_exists?" do
|
438
490
|
setup do
|
439
491
|
@db = DummyDatabase.new
|
440
|
-
@db.
|
492
|
+
@db.instance_variable_set(:@schemas, {:a=>[]})
|
441
493
|
@db2 = DummyDatabase.new
|
442
494
|
end
|
443
495
|
|
444
|
-
specify "should use
|
496
|
+
specify "should use schema information if available" do
|
445
497
|
@db.table_exists?(:a).should be_true
|
446
|
-
@db.table_exists?(:b).should be_true
|
447
|
-
@db.table_exists?(:c).should be_false
|
448
498
|
end
|
449
499
|
|
450
500
|
specify "should otherwise try to select the first record from the table's dataset" do
|
@@ -191,6 +191,26 @@ context "A dataset with multiple tables in its FROM clause" do
|
|
191
191
|
end
|
192
192
|
end
|
193
193
|
|
194
|
+
context "Dataset#exists" do
|
195
|
+
setup do
|
196
|
+
@ds1 = Sequel::Dataset.new(nil).from(:test)
|
197
|
+
@ds2 = @ds1.filter(:price < 100)
|
198
|
+
@ds3 = @ds1.filter(:price > 50)
|
199
|
+
end
|
200
|
+
|
201
|
+
specify "should work in filters" do
|
202
|
+
@ds1.filter(@ds2.exists).sql.should ==
|
203
|
+
'SELECT * FROM test WHERE (EXISTS (SELECT * FROM test WHERE (price < 100)))'
|
204
|
+
@ds1.filter(@ds2.exists & @ds3.exists).sql.should ==
|
205
|
+
'SELECT * FROM test WHERE (EXISTS (SELECT * FROM test WHERE (price < 100)) AND EXISTS (SELECT * FROM test WHERE (price > 50)))'
|
206
|
+
end
|
207
|
+
|
208
|
+
specify "should work in select" do
|
209
|
+
@ds1.select(@ds2.exists.as(:a), @ds3.exists.as(:b)).sql.should ==
|
210
|
+
'SELECT EXISTS (SELECT * FROM test WHERE (price < 100)) AS a, EXISTS (SELECT * FROM test WHERE (price > 50)) AS b FROM test'
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
194
214
|
context "Dataset#where" do
|
195
215
|
setup do
|
196
216
|
@dataset = Sequel::Dataset.new(nil).from(:test)
|
@@ -1309,6 +1329,23 @@ context "Dataset#join_table" do
|
|
1309
1329
|
'SELECT * FROM "foo" AS "f" INNER JOIN "bar" ON ("bar"."id" = "f"."bar_id")'
|
1310
1330
|
end
|
1311
1331
|
|
1332
|
+
specify "should support implicit schemas in from table symbols" do
|
1333
|
+
@d.from(:s__t).join(:u__v, {:id => :player_id}).sql.should ==
|
1334
|
+
'SELECT * FROM "s"."t" INNER JOIN "u"."v" ON ("u"."v"."id" = "s"."t"."player_id")'
|
1335
|
+
end
|
1336
|
+
|
1337
|
+
specify "should support implicit aliases in from table symbols" do
|
1338
|
+
@d.from(:t___z).join(:v___y, {:id => :player_id}).sql.should ==
|
1339
|
+
'SELECT * FROM "t" AS "z" INNER JOIN "v" AS "y" ON ("y"."id" = "z"."player_id")'
|
1340
|
+
@d.from(:s__t___z).join(:u__v___y, {:id => :player_id}).sql.should ==
|
1341
|
+
'SELECT * FROM "s"."t" AS "z" INNER JOIN "u"."v" AS "y" ON ("y"."id" = "z"."player_id")'
|
1342
|
+
end
|
1343
|
+
|
1344
|
+
specify "should support AliasedExpressions" do
|
1345
|
+
@d.from(:s.as(:t)).join(:u.as(:v), {:id => :player_id}).sql.should ==
|
1346
|
+
'SELECT * FROM "s" AS "t" INNER JOIN "u" AS "v" ON ("v"."id" = "t"."player_id")'
|
1347
|
+
end
|
1348
|
+
|
1312
1349
|
specify "should support the :implicit_qualifier option" do
|
1313
1350
|
@d.from('stats').join('players', {:id => :player_id}, :implicit_qualifier=>:p).sql.should ==
|
1314
1351
|
'SELECT * FROM "stats" INNER JOIN "players" ON ("players"."id" = "p"."player_id")'
|
@@ -2874,14 +2911,12 @@ end
|
|
2874
2911
|
context "Dataset#table_exists?" do
|
2875
2912
|
setup do
|
2876
2913
|
@db = DummyMummyDatabase.new
|
2877
|
-
@db.
|
2914
|
+
@db.instance_variable_set(:@schemas, {:a=>[]})
|
2878
2915
|
@db2 = DummyMummyDatabase.new
|
2879
2916
|
end
|
2880
2917
|
|
2881
|
-
specify "should use
|
2918
|
+
specify "should use the database schema if available" do
|
2882
2919
|
@db[:a].table_exists?.should be_true
|
2883
|
-
@db[:b].table_exists?.should be_true
|
2884
|
-
@db[:c].table_exists?.should be_false
|
2885
2920
|
end
|
2886
2921
|
|
2887
2922
|
specify "should otherwise try to select the first record from the table's dataset" do
|
@@ -3053,10 +3088,14 @@ context "Dataset prepared statements and bound variables " do
|
|
3053
3088
|
def @db.execute(sql, opts={})
|
3054
3089
|
@sqls << sql
|
3055
3090
|
end
|
3056
|
-
|
3057
|
-
|
3058
|
-
|
3091
|
+
def @db.dataset
|
3092
|
+
ds = super()
|
3093
|
+
def ds.fetch_rows(sql, &block)
|
3094
|
+
execute(sql)
|
3095
|
+
end
|
3096
|
+
ds
|
3059
3097
|
end
|
3098
|
+
@ds = @db[:items]
|
3060
3099
|
end
|
3061
3100
|
|
3062
3101
|
specify "#call should take a type and bind hash and interpolate it" do
|
@@ -3097,6 +3136,36 @@ context "Dataset prepared statements and bound variables " do
|
|
3097
3136
|
@ds.filter(:num=>:$n).prepare(:select, :sn).inspect.should == \
|
3098
3137
|
'<Sequel::Dataset/PreparedStatement "SELECT * FROM items WHERE (num = $n)">'
|
3099
3138
|
end
|
3139
|
+
|
3140
|
+
specify "should handle literal strings" do
|
3141
|
+
@ds.filter("num = ?", :$n).call(:select, :n=>1)
|
3142
|
+
@db.sqls.should == ['SELECT * FROM items WHERE (num = 1)']
|
3143
|
+
end
|
3144
|
+
|
3145
|
+
specify "should handle datasets using static sql and placeholders" do
|
3146
|
+
@db["SELECT * FROM items WHERE (num = ?)", :$n].call(:select, :n=>1)
|
3147
|
+
@db.sqls.should == ['SELECT * FROM items WHERE (num = 1)']
|
3148
|
+
end
|
3149
|
+
|
3150
|
+
specify "should handle subselects" do
|
3151
|
+
@ds.filter(:$b).filter(:num=>@ds.select(:num).filter(:num=>:$n)).filter(:$c).call(:select, :n=>1, :b=>0, :c=>2)
|
3152
|
+
@db.sqls.should == ['SELECT * FROM items WHERE ((0 AND (num IN (SELECT num FROM items WHERE (num = 1)))) AND 2)']
|
3153
|
+
end
|
3154
|
+
|
3155
|
+
specify "should handle subselects in subselects" do
|
3156
|
+
@ds.filter(:$b).filter(:num=>@ds.select(:num).filter(:num=>@ds.select(:num).filter(:num=>:$n))).call(:select, :n=>1, :b=>0)
|
3157
|
+
@db.sqls.should == ['SELECT * FROM items WHERE (0 AND (num IN (SELECT num FROM items WHERE (num IN (SELECT num FROM items WHERE (num = 1))))))']
|
3158
|
+
end
|
3159
|
+
|
3160
|
+
specify "should handle subselects with literal strings" do
|
3161
|
+
@ds.filter(:$b).filter(:num=>@ds.select(:num).filter("num = ?", :$n)).call(:select, :n=>1, :b=>0)
|
3162
|
+
@db.sqls.should == ['SELECT * FROM items WHERE (0 AND (num IN (SELECT num FROM items WHERE (num = 1))))']
|
3163
|
+
end
|
3164
|
+
|
3165
|
+
specify "should handle subselects with static sql and placeholders" do
|
3166
|
+
@ds.filter(:$b).filter(:num=>@db["SELECT num FROM items WHERE (num = ?)", :$n]).call(:select, :n=>1, :b=>0)
|
3167
|
+
@db.sqls.should == ['SELECT * FROM items WHERE (0 AND (num IN (SELECT num FROM items WHERE (num = 1))))']
|
3168
|
+
end
|
3100
3169
|
end
|
3101
3170
|
|
3102
3171
|
context Sequel::Dataset::UnnumberedArgumentMapper do
|
@@ -3233,3 +3302,32 @@ context "Sequel::Dataset#each" do
|
|
3233
3302
|
end
|
3234
3303
|
end
|
3235
3304
|
end
|
3305
|
+
|
3306
|
+
context Sequel::Dataset::UnsupportedIntersectExcept do
|
3307
|
+
before do
|
3308
|
+
@ds = Sequel::Dataset.new(nil).from(:items)
|
3309
|
+
@ds2 = Sequel::Dataset.new(nil).from(:i)
|
3310
|
+
@ds.extend(Sequel::Dataset::UnsupportedIntersectExcept)
|
3311
|
+
end
|
3312
|
+
|
3313
|
+
specify "should raise an error if INTERSECT or EXCEPT is USED" do
|
3314
|
+
@ds.union(@ds2).sql.should == 'SELECT * FROM items UNION SELECT * FROM i'
|
3315
|
+
proc{@ds.intersect(@ds2)}.should raise_error(Sequel::Error)
|
3316
|
+
proc{@ds.except(@ds2)}.should raise_error(Sequel::Error)
|
3317
|
+
end
|
3318
|
+
end
|
3319
|
+
|
3320
|
+
context Sequel::Dataset::UnsupportedIntersectExceptAll do
|
3321
|
+
before do
|
3322
|
+
@ds = Sequel::Dataset.new(nil).from(:items)
|
3323
|
+
@ds2 = Sequel::Dataset.new(nil).from(:i)
|
3324
|
+
@ds.extend(Sequel::Dataset::UnsupportedIntersectExceptAll)
|
3325
|
+
end
|
3326
|
+
|
3327
|
+
specify "should raise an error if INTERSECT or EXCEPT is USED" do
|
3328
|
+
@ds.intersect(@ds2).sql.should == 'SELECT * FROM items INTERSECT SELECT * FROM i'
|
3329
|
+
@ds.except(@ds2).sql.should == 'SELECT * FROM items EXCEPT SELECT * FROM i'
|
3330
|
+
proc{@ds.intersect(@ds2, true)}.should raise_error(Sequel::Error)
|
3331
|
+
proc{@ds.except(@ds2, true)}.should raise_error(Sequel::Error)
|
3332
|
+
end
|
3333
|
+
end
|