sequel 3.1.0 → 3.2.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 +76 -0
- data/Rakefile +2 -2
- data/bin/sequel +9 -4
- data/doc/opening_databases.rdoc +279 -0
- data/doc/release_notes/3.2.0.txt +268 -0
- data/doc/virtual_rows.rdoc +42 -51
- data/lib/sequel/adapters/ado.rb +2 -5
- data/lib/sequel/adapters/db2.rb +5 -0
- data/lib/sequel/adapters/do.rb +3 -0
- data/lib/sequel/adapters/firebird.rb +6 -4
- data/lib/sequel/adapters/informix.rb +5 -3
- data/lib/sequel/adapters/jdbc.rb +10 -8
- data/lib/sequel/adapters/jdbc/h2.rb +17 -4
- data/lib/sequel/adapters/mysql.rb +6 -19
- data/lib/sequel/adapters/odbc.rb +14 -18
- data/lib/sequel/adapters/openbase.rb +8 -0
- data/lib/sequel/adapters/shared/mssql.rb +14 -8
- data/lib/sequel/adapters/shared/mysql.rb +53 -28
- data/lib/sequel/adapters/shared/oracle.rb +21 -12
- data/lib/sequel/adapters/shared/postgres.rb +46 -26
- data/lib/sequel/adapters/shared/progress.rb +10 -5
- data/lib/sequel/adapters/shared/sqlite.rb +28 -12
- data/lib/sequel/adapters/sqlite.rb +4 -3
- data/lib/sequel/adapters/utils/stored_procedures.rb +18 -9
- data/lib/sequel/connection_pool.rb +4 -3
- data/lib/sequel/database.rb +110 -10
- data/lib/sequel/database/schema_sql.rb +12 -3
- data/lib/sequel/dataset.rb +40 -3
- data/lib/sequel/dataset/convenience.rb +0 -11
- data/lib/sequel/dataset/graph.rb +25 -11
- data/lib/sequel/dataset/sql.rb +176 -68
- data/lib/sequel/extensions/migration.rb +37 -21
- data/lib/sequel/extensions/schema_dumper.rb +8 -61
- data/lib/sequel/model.rb +3 -3
- data/lib/sequel/model/associations.rb +9 -1
- data/lib/sequel/model/base.rb +8 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/sql.rb +125 -18
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/ado_spec.rb +1 -0
- data/spec/adapters/firebird_spec.rb +1 -0
- data/spec/adapters/informix_spec.rb +1 -0
- data/spec/adapters/mysql_spec.rb +23 -8
- data/spec/adapters/oracle_spec.rb +1 -0
- data/spec/adapters/postgres_spec.rb +52 -4
- data/spec/adapters/spec_helper.rb +2 -2
- data/spec/adapters/sqlite_spec.rb +2 -1
- data/spec/core/connection_pool_spec.rb +16 -0
- data/spec/core/database_spec.rb +174 -0
- data/spec/core/dataset_spec.rb +121 -26
- data/spec/core/expression_filters_spec.rb +156 -0
- data/spec/core/object_graph_spec.rb +20 -1
- data/spec/core/schema_spec.rb +5 -5
- data/spec/extensions/migration_spec.rb +140 -74
- data/spec/extensions/schema_dumper_spec.rb +3 -69
- data/spec/extensions/single_table_inheritance_spec.rb +6 -0
- data/spec/integration/dataset_test.rb +84 -2
- data/spec/integration/schema_test.rb +24 -5
- data/spec/integration/spec_helper.rb +8 -6
- data/spec/model/eager_loading_spec.rb +9 -0
- data/spec/model/record_spec.rb +35 -8
- metadata +8 -7
- data/lib/sequel/adapters/utils/date_format.rb +0 -21
- data/lib/sequel/adapters/utils/savepoint_transactions.rb +0 -80
- data/lib/sequel/adapters/utils/unsupported.rb +0 -50
@@ -3,6 +3,7 @@ require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
|
3
3
|
unless defined?(ORACLE_DB)
|
4
4
|
ORACLE_DB = Sequel.connect('oracle://hr:hr@localhost/XE')
|
5
5
|
end
|
6
|
+
INTEGRATION_DB = ORACLE_DB unless defined?(INTEGRATION_DB)
|
6
7
|
|
7
8
|
if ORACLE_DB.table_exists?(:items)
|
8
9
|
ORACLE_DB.drop_table :items
|
@@ -4,6 +4,7 @@ unless defined?(POSTGRES_DB)
|
|
4
4
|
POSTGRES_URL = 'postgres://postgres:postgres@localhost:5432/reality_spec' unless defined? POSTGRES_URL
|
5
5
|
POSTGRES_DB = Sequel.connect(ENV['SEQUEL_PG_SPEC_DB']||POSTGRES_URL)
|
6
6
|
end
|
7
|
+
INTEGRATION_DB = POSTGRES_DB unless defined?(INTEGRATION_DB)
|
7
8
|
|
8
9
|
def POSTGRES_DB.sqls
|
9
10
|
(@sqls ||= [])
|
@@ -43,12 +44,12 @@ context "A PostgreSQL database" do
|
|
43
44
|
|
44
45
|
specify "should correctly parse the schema" do
|
45
46
|
@db.schema(:test3, :reload=>true).should == [
|
46
|
-
[:value, {:type=>:integer, :allow_null=>true, :default=>nil, :db_type=>"integer", :primary_key=>false}],
|
47
|
-
[:time, {:type=>:datetime, :allow_null=>true, :default=>nil, :db_type=>"timestamp without time zone", :primary_key=>false}]
|
47
|
+
[:value, {:type=>:integer, :allow_null=>true, :default=>nil, :ruby_default=>nil, :db_type=>"integer", :primary_key=>false}],
|
48
|
+
[:time, {:type=>:datetime, :allow_null=>true, :default=>nil, :ruby_default=>nil, :db_type=>"timestamp without time zone", :primary_key=>false}]
|
48
49
|
]
|
49
50
|
@db.schema(:test4, :reload=>true).should == [
|
50
|
-
[:name, {:type=>:string, :allow_null=>true, :default=>nil, :db_type=>"character varying(20)", :primary_key=>false}],
|
51
|
-
[:value, {:type=>:blob, :allow_null=>true, :default=>nil, :db_type=>"bytea", :primary_key=>false}]
|
51
|
+
[:name, {:type=>:string, :allow_null=>true, :default=>nil, :ruby_default=>nil, :db_type=>"character varying(20)", :primary_key=>false}],
|
52
|
+
[:value, {:type=>:blob, :allow_null=>true, :default=>nil, :ruby_default=>nil, :db_type=>"bytea", :primary_key=>false}]
|
52
53
|
]
|
53
54
|
end
|
54
55
|
end
|
@@ -144,6 +145,14 @@ context "A PostgreSQL dataset with a timestamp field" do
|
|
144
145
|
end
|
145
146
|
end
|
146
147
|
|
148
|
+
context "PostgreSQL's EXPLAIN and ANALYZE" do
|
149
|
+
specify "should not raise errors" do
|
150
|
+
@d = POSTGRES_DB[:test3]
|
151
|
+
proc{@d.explain}.should_not raise_error
|
152
|
+
proc{@d.analyze}.should_not raise_error
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
147
156
|
context "A PostgreSQL database" do
|
148
157
|
before do
|
149
158
|
@db = POSTGRES_DB
|
@@ -208,6 +217,15 @@ context "A PostgreSQL database" do
|
|
208
217
|
@db.reset_primary_key_sequence(:posts).should == nil
|
209
218
|
end
|
210
219
|
|
220
|
+
|
221
|
+
specify "should support opclass specification" do
|
222
|
+
@db.create_table(:posts){text :title; text :body; integer :user_id; index(:user_id, :opclass => :int4_ops, :type => :btree)}
|
223
|
+
@db.sqls.should == [
|
224
|
+
"CREATE TABLE posts (title text, body text, user_id integer)",
|
225
|
+
"CREATE INDEX posts_user_id_index ON posts USING btree (user_id int4_ops)"
|
226
|
+
]
|
227
|
+
end
|
228
|
+
|
211
229
|
specify "should support fulltext indexes and searching" do
|
212
230
|
@db.create_table(:posts){text :title; text :body; full_text_index [:title, :body]; full_text_index :title, :language => 'french'}
|
213
231
|
@db.sqls.should == [
|
@@ -515,6 +533,36 @@ if POSTGRES_DB.server_version >= 80300
|
|
515
533
|
end
|
516
534
|
end
|
517
535
|
|
536
|
+
if POSTGRES_DB.dataset.supports_window_functions?
|
537
|
+
context "Postgres::Dataset named windows" do
|
538
|
+
before do
|
539
|
+
@db = POSTGRES_DB
|
540
|
+
@db.create_table!(:i1){Integer :id; Integer :group_id; Integer :amount}
|
541
|
+
@ds = @db[:i1].order(:id)
|
542
|
+
@ds.insert(:id=>1, :group_id=>1, :amount=>1)
|
543
|
+
@ds.insert(:id=>2, :group_id=>1, :amount=>10)
|
544
|
+
@ds.insert(:id=>3, :group_id=>1, :amount=>100)
|
545
|
+
@ds.insert(:id=>4, :group_id=>2, :amount=>1000)
|
546
|
+
@ds.insert(:id=>5, :group_id=>2, :amount=>10000)
|
547
|
+
@ds.insert(:id=>6, :group_id=>2, :amount=>100000)
|
548
|
+
end
|
549
|
+
after do
|
550
|
+
@db.drop_table(:i1)
|
551
|
+
end
|
552
|
+
|
553
|
+
specify "should give correct results for window functions" do
|
554
|
+
@ds.window(:win, :partition=>:group_id, :order=>:id).select(:id){sum(:over, :args=>amount, :window=>win){}}.all.should ==
|
555
|
+
[{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1000, :id=>4}, {:sum=>11000, :id=>5}, {:sum=>111000, :id=>6}]
|
556
|
+
@ds.window(:win, :partition=>:group_id).select(:id){sum(:over, :args=>amount, :window=>win, :order=>id){}}.all.should ==
|
557
|
+
[{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1000, :id=>4}, {:sum=>11000, :id=>5}, {:sum=>111000, :id=>6}]
|
558
|
+
@ds.window(:win, {}).select(:id){sum(:over, :args=>amount, :window=>:win, :order=>id){}}.all.should ==
|
559
|
+
[{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1111, :id=>4}, {:sum=>11111, :id=>5}, {:sum=>111111, :id=>6}]
|
560
|
+
@ds.window(:win, :partition=>:group_id).select(:id){sum(:over, :args=>amount, :window=>:win, :order=>id, :frame=>:all){}}.all.should ==
|
561
|
+
[{:sum=>111, :id=>1}, {:sum=>111, :id=>2}, {:sum=>111, :id=>3}, {:sum=>111000, :id=>4}, {:sum=>111000, :id=>5}, {:sum=>111000, :id=>6}]
|
562
|
+
end
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
518
566
|
context "Postgres::Database functions, languages, and triggers" do
|
519
567
|
before do
|
520
568
|
@d = POSTGRES_DB
|
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
unless Object.const_defined?('Sequel')
|
3
3
|
$:.unshift(File.join(File.dirname(__FILE__), "../../lib/"))
|
4
|
-
require 'sequel
|
4
|
+
require 'sequel'
|
5
5
|
Sequel.quote_identifiers = false
|
6
6
|
end
|
7
7
|
begin
|
8
|
-
require File.join(File.dirname(__FILE__), '
|
8
|
+
require File.join(File.dirname(File.dirname(__FILE__)), 'spec_config.rb')
|
9
9
|
rescue LoadError
|
10
10
|
end
|
@@ -4,6 +4,7 @@ unless defined?(SQLITE_DB)
|
|
4
4
|
SQLITE_URL = 'sqlite:/' unless defined? SQLITE_URL
|
5
5
|
SQLITE_DB = Sequel.connect(ENV['SEQUEL_SQLITE_SPEC_DB']||SQLITE_URL)
|
6
6
|
end
|
7
|
+
INTEGRATION_DB = SQLITE_DB unless defined?(INTEGRATION_DB)
|
7
8
|
|
8
9
|
context "An SQLite database" do
|
9
10
|
before do
|
@@ -94,7 +95,7 @@ context "An SQLite database" do
|
|
94
95
|
|
95
96
|
specify "should correctly parse the schema" do
|
96
97
|
@db.create_table!(:time2) {timestamp :t}
|
97
|
-
@db.schema(:time2, :reload=>true).should == [[:t, {:type=>:datetime, :allow_null=>true, :default=>nil, :db_type=>"timestamp", :primary_key=>false}]]
|
98
|
+
@db.schema(:time2, :reload=>true).should == [[:t, {:type=>:datetime, :allow_null=>true, :default=>nil, :ruby_default=>nil, :db_type=>"timestamp", :primary_key=>false}]]
|
98
99
|
end
|
99
100
|
end
|
100
101
|
|
@@ -20,6 +20,22 @@ context "An empty ConnectionPool" do
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
+
context "ConnectionPool options" do
|
24
|
+
specify "should support string option values" do
|
25
|
+
cpool = Sequel::ConnectionPool.new({:max_connections=>'5', :pool_timeout=>'3', :pool_sleep_time=>'0.01'})
|
26
|
+
cpool.max_size.should == 5
|
27
|
+
cpool.instance_variable_get(:@timeout).should == 3
|
28
|
+
cpool.instance_variable_get(:@sleep_time).should == 0.01
|
29
|
+
end
|
30
|
+
|
31
|
+
specify "should raise an error unless size is positive" do
|
32
|
+
lambda{Sequel::ConnectionPool.new(:max_connections=>0)}.should raise_error(Sequel::Error)
|
33
|
+
lambda{Sequel::ConnectionPool.new(:max_connections=>-10)}.should raise_error(Sequel::Error)
|
34
|
+
lambda{Sequel::ConnectionPool.new(:max_connections=>'-10')}.should raise_error(Sequel::Error)
|
35
|
+
lambda{Sequel::ConnectionPool.new(:max_connections=>'0')}.should raise_error(Sequel::Error)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
23
39
|
context "A connection pool handling connections" do
|
24
40
|
before do
|
25
41
|
@max_size = 2
|
data/spec/core/database_spec.rb
CHANGED
@@ -39,8 +39,16 @@ context "A new Database" do
|
|
39
39
|
specify "should respect the :single_threaded option" do
|
40
40
|
db = Sequel::Database.new(:single_threaded=>true)
|
41
41
|
db.pool.should be_a_kind_of(Sequel::SingleThreadedPool)
|
42
|
+
db = Sequel::Database.new(:single_threaded=>'t')
|
43
|
+
db.pool.should be_a_kind_of(Sequel::SingleThreadedPool)
|
44
|
+
db = Sequel::Database.new(:single_threaded=>'1')
|
45
|
+
db.pool.should be_a_kind_of(Sequel::SingleThreadedPool)
|
42
46
|
db = Sequel::Database.new(:single_threaded=>false)
|
43
47
|
db.pool.should be_a_kind_of(Sequel::ConnectionPool)
|
48
|
+
db = Sequel::Database.new(:single_threaded=>'f')
|
49
|
+
db.pool.should be_a_kind_of(Sequel::ConnectionPool)
|
50
|
+
db = Sequel::Database.new(:single_threaded=>'0')
|
51
|
+
db.pool.should be_a_kind_of(Sequel::ConnectionPool)
|
44
52
|
end
|
45
53
|
|
46
54
|
specify "should respect the :quote_identifiers option" do
|
@@ -640,6 +648,106 @@ context "Database#transaction" do
|
|
640
648
|
end
|
641
649
|
end
|
642
650
|
|
651
|
+
context "Database#transaction with savepoints" do
|
652
|
+
before do
|
653
|
+
@db = Dummy3Database.new
|
654
|
+
@db.meta_def(:supports_savepoints?){true}
|
655
|
+
@db.pool.connection_proc = proc {Dummy3Database::DummyConnection.new(@db)}
|
656
|
+
end
|
657
|
+
|
658
|
+
specify "should wrap the supplied block with BEGIN + COMMIT statements" do
|
659
|
+
@db.transaction {@db.execute 'DROP TABLE test;'}
|
660
|
+
@db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
|
661
|
+
end
|
662
|
+
|
663
|
+
specify "should use savepoints if given the :savepoint option" do
|
664
|
+
@db.transaction{@db.transaction(:savepoint=>true){@db.execute 'DROP TABLE test;'}}
|
665
|
+
@db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test;', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT']
|
666
|
+
end
|
667
|
+
|
668
|
+
specify "should not use a savepoints if no transaction is in progress" do
|
669
|
+
@db.transaction(:savepoint=>true){@db.execute 'DROP TABLE test;'}
|
670
|
+
@db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
|
671
|
+
end
|
672
|
+
|
673
|
+
specify "should reuse the current transaction if no :savepoint option is given" do
|
674
|
+
@db.transaction{@db.transaction{@db.execute 'DROP TABLE test;'}}
|
675
|
+
@db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
|
676
|
+
end
|
677
|
+
|
678
|
+
specify "should handle returning inside of the block by committing" do
|
679
|
+
def @db.ret_commit
|
680
|
+
transaction do
|
681
|
+
execute 'DROP TABLE test;'
|
682
|
+
return
|
683
|
+
execute 'DROP TABLE test2;';
|
684
|
+
end
|
685
|
+
end
|
686
|
+
@db.ret_commit
|
687
|
+
@db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
|
688
|
+
end
|
689
|
+
|
690
|
+
specify "should handle returning inside of a savepoint by committing" do
|
691
|
+
def @db.ret_commit
|
692
|
+
transaction do
|
693
|
+
transaction(:savepoint=>true) do
|
694
|
+
execute 'DROP TABLE test;'
|
695
|
+
return
|
696
|
+
execute 'DROP TABLE test2;';
|
697
|
+
end
|
698
|
+
end
|
699
|
+
end
|
700
|
+
@db.ret_commit
|
701
|
+
@db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test;', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT']
|
702
|
+
end
|
703
|
+
|
704
|
+
specify "should issue ROLLBACK if an exception is raised, and re-raise" do
|
705
|
+
@db.transaction {@db.execute 'DROP TABLE test'; raise RuntimeError} rescue nil
|
706
|
+
@db.sql.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
|
707
|
+
|
708
|
+
proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
|
709
|
+
end
|
710
|
+
|
711
|
+
specify "should issue ROLLBACK SAVEPOINT if an exception is raised inside a savepoint, and re-raise" do
|
712
|
+
@db.transaction{@db.transaction(:savepoint=>true){@db.execute 'DROP TABLE test'; raise RuntimeError}} rescue nil
|
713
|
+
@db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test', 'ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK']
|
714
|
+
|
715
|
+
proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
|
716
|
+
end
|
717
|
+
|
718
|
+
specify "should issue ROLLBACK if Sequel::Rollback is called in the transaction" do
|
719
|
+
@db.transaction do
|
720
|
+
@db.drop_table(:a)
|
721
|
+
raise Sequel::Rollback
|
722
|
+
@db.drop_table(:b)
|
723
|
+
end
|
724
|
+
|
725
|
+
@db.sql.should == ['BEGIN', 'DROP TABLE a', 'ROLLBACK']
|
726
|
+
end
|
727
|
+
|
728
|
+
specify "should issue ROLLBACK SAVEPOINT if Sequel::Rollback is called in a savepoint" do
|
729
|
+
@db.transaction do
|
730
|
+
@db.transaction(:savepoint=>true) do
|
731
|
+
@db.drop_table(:a)
|
732
|
+
raise Sequel::Rollback
|
733
|
+
end
|
734
|
+
@db.drop_table(:b)
|
735
|
+
end
|
736
|
+
|
737
|
+
@db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE a', 'ROLLBACK TO SAVEPOINT autopoint_1', 'DROP TABLE b', 'COMMIT']
|
738
|
+
end
|
739
|
+
|
740
|
+
specify "should raise database errors when commiting a transaction as Sequel::DatabaseError" do
|
741
|
+
@db.meta_def(:commit_transaction){raise ArgumentError}
|
742
|
+
lambda{@db.transaction{}}.should raise_error(ArgumentError)
|
743
|
+
lambda{@db.transaction{@db.transaction(:savepoint=>true){}}}.should raise_error(ArgumentError)
|
744
|
+
|
745
|
+
@db.meta_def(:database_error_classes){[ArgumentError]}
|
746
|
+
lambda{@db.transaction{}}.should raise_error(Sequel::DatabaseError)
|
747
|
+
lambda{@db.transaction{@db.transaction(:savepoint=>true){}}}.should raise_error(Sequel::DatabaseError)
|
748
|
+
end
|
749
|
+
end
|
750
|
+
|
643
751
|
context "A Database adapter with a scheme" do
|
644
752
|
before do
|
645
753
|
class CCC < Sequel::Database
|
@@ -740,6 +848,13 @@ context "A Database adapter with a scheme" do
|
|
740
848
|
|
741
849
|
end
|
742
850
|
|
851
|
+
context "Sequel::Database.connect" do
|
852
|
+
specify "should raise an Error if not given a String or Hash" do
|
853
|
+
proc{Sequel::Database.connect(nil)}.should raise_error(Sequel::Error)
|
854
|
+
proc{Sequel::Database.connect(Object.new)}.should raise_error(Sequel::Error)
|
855
|
+
end
|
856
|
+
end
|
857
|
+
|
743
858
|
context "An unknown database scheme" do
|
744
859
|
specify "should raise an error in Sequel::Database.connect" do
|
745
860
|
proc {Sequel::Database.connect('ddd://localhost/db')}.should raise_error(Sequel::AdapterNotFound)
|
@@ -1181,3 +1296,62 @@ context "Database#metadata_dataset" do
|
|
1181
1296
|
ds.send(:output_identifier, 'A').should == :a
|
1182
1297
|
end
|
1183
1298
|
end
|
1299
|
+
|
1300
|
+
context "Database#column_schema_to_ruby_default" do
|
1301
|
+
specify "should handle converting many default formats" do
|
1302
|
+
db = Sequel::Database.new
|
1303
|
+
m = db.method(:column_schema_to_ruby_default)
|
1304
|
+
p = lambda{|d,t| m.call(d,t)}
|
1305
|
+
p[nil, :integer].should == nil
|
1306
|
+
p['1', :integer].should == 1
|
1307
|
+
p['-1', :integer].should == -1
|
1308
|
+
p['1.0', :float].should == 1.0
|
1309
|
+
p['-1.0', :float].should == -1.0
|
1310
|
+
p['1.0', :decimal].should == BigDecimal.new('1.0')
|
1311
|
+
p['-1.0', :decimal].should == BigDecimal.new('-1.0')
|
1312
|
+
p['1', :boolean].should == true
|
1313
|
+
p['0', :boolean].should == false
|
1314
|
+
p['true', :boolean].should == true
|
1315
|
+
p['false', :boolean].should == false
|
1316
|
+
p["'t'", :boolean].should == true
|
1317
|
+
p["'f'", :boolean].should == false
|
1318
|
+
p["'a'", :string].should == 'a'
|
1319
|
+
p["'a'", :blob].should == 'a'.to_sequel_blob
|
1320
|
+
p["'a'", :blob].should be_a_kind_of(Sequel::SQL::Blob)
|
1321
|
+
p["''", :string].should == ''
|
1322
|
+
p["'\\a''b'", :string].should == "\\a'b"
|
1323
|
+
p["'NULL'", :string].should == "NULL"
|
1324
|
+
p["'2009-10-29'", :date].should == Date.new(2009,10,29)
|
1325
|
+
p["CURRENT_TIMESTAMP", :date].should == nil
|
1326
|
+
p["today()", :date].should == nil
|
1327
|
+
p["'2009-10-29T10:20:30-07:00'", :datetime].should == DateTime.parse('2009-10-29T10:20:30-07:00')
|
1328
|
+
p["'2009-10-29 10:20:30'", :datetime].should == DateTime.parse('2009-10-29 10:20:30')
|
1329
|
+
p["'10:20:30'", :time].should == Time.parse('10:20:30')
|
1330
|
+
p["NaN", :float].should == nil
|
1331
|
+
|
1332
|
+
db.meta_def(:database_type){:postgres}
|
1333
|
+
p["''::text", :string].should == ""
|
1334
|
+
p["'\\a''b'::character varying", :string].should == "\\a'b"
|
1335
|
+
p["'a'::bpchar", :string].should == "a"
|
1336
|
+
p["(-1)", :integer].should == -1
|
1337
|
+
p["(-1.0)", :float].should == -1.0
|
1338
|
+
p['(-1.0)', :decimal].should == BigDecimal.new('-1.0')
|
1339
|
+
p["'a'::bytea", :blob].should == 'a'.to_sequel_blob
|
1340
|
+
p["'a'::bytea", :blob].should be_a_kind_of(Sequel::SQL::Blob)
|
1341
|
+
p["'2009-10-29'::date", :date].should == Date.new(2009,10,29)
|
1342
|
+
p["'2009-10-29 10:20:30.241343'::timestamp without time zone", :datetime].should == DateTime.parse('2009-10-29 10:20:30.241343')
|
1343
|
+
p["'10:20:30'::time without time zone", :time].should == Time.parse('10:20:30')
|
1344
|
+
|
1345
|
+
db.meta_def(:database_type){:mysql}
|
1346
|
+
p["\\a'b", :string].should == "\\a'b"
|
1347
|
+
p["a", :string].should == "a"
|
1348
|
+
p["NULL", :string].should == "NULL"
|
1349
|
+
p["-1", :float].should == -1.0
|
1350
|
+
p['-1', :decimal].should == BigDecimal.new('-1.0')
|
1351
|
+
p["2009-10-29", :date].should == Date.new(2009,10,29)
|
1352
|
+
p["2009-10-29 10:20:30", :datetime].should == DateTime.parse('2009-10-29 10:20:30')
|
1353
|
+
p["10:20:30", :time].should == Time.parse('10:20:30')
|
1354
|
+
p["CURRENT_DATE", :date].should == nil
|
1355
|
+
p["CURRENT_TIMESTAMP", :datetime].should == nil
|
1356
|
+
end
|
1357
|
+
end
|
data/spec/core/dataset_spec.rb
CHANGED
@@ -758,6 +758,22 @@ context "Dataset#literal" do
|
|
758
758
|
s = d.strftime("'%Y-%m-%d'")
|
759
759
|
@dataset.literal(d).should == s
|
760
760
|
end
|
761
|
+
|
762
|
+
specify "should literalize Time, DateTime, Date properly if SQL standard format is required" do
|
763
|
+
@dataset.meta_def(:requires_sql_standard_datetimes?){true}
|
764
|
+
|
765
|
+
t = Time.now
|
766
|
+
s = t.strftime("TIMESTAMP '%Y-%m-%d %H:%M:%S'")
|
767
|
+
@dataset.literal(t).should == s
|
768
|
+
|
769
|
+
t = DateTime.now
|
770
|
+
s = t.strftime("TIMESTAMP '%Y-%m-%d %H:%M:%S'")
|
771
|
+
@dataset.literal(t).should == s
|
772
|
+
|
773
|
+
d = Date.today
|
774
|
+
s = d.strftime("DATE '%Y-%m-%d'")
|
775
|
+
@dataset.literal(d).should == s
|
776
|
+
end
|
761
777
|
|
762
778
|
specify "should not modify literal strings" do
|
763
779
|
@dataset.literal('col1 + 2'.lit).should == 'col1 + 2'
|
@@ -1249,6 +1265,12 @@ context "Dataset#distinct" do
|
|
1249
1265
|
@dataset.distinct.sql.should == 'SELECT DISTINCT name FROM test'
|
1250
1266
|
end
|
1251
1267
|
|
1268
|
+
specify "should raise an error if columns given and distinct on not supported" do
|
1269
|
+
@dataset.meta_def(:supports_distinct_on?){false}
|
1270
|
+
proc{@dataset.distinct}.should_not raise_error
|
1271
|
+
proc{@dataset.distinct(:a)}.should raise_error(Sequel::InvalidOperation)
|
1272
|
+
end
|
1273
|
+
|
1252
1274
|
specify "should accept an expression list" do
|
1253
1275
|
@dataset.distinct(:a, :b).sql.should == 'SELECT DISTINCT ON (a, b) name FROM test'
|
1254
1276
|
@dataset.distinct(:stamp.cast(:integer), :node_id=>nil).sql.should == 'SELECT DISTINCT ON (CAST(stamp AS integer), (node_id IS NULL)) name FROM test'
|
@@ -1505,6 +1527,9 @@ context "Dataset#join_table" do
|
|
1505
1527
|
|
1506
1528
|
@d.join_table(:left_outer, ds, :item_id => :id).sql.should ==
|
1507
1529
|
'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories WHERE (active IS TRUE)) AS "t1" ON ("t1"."item_id" = "items"."id")'
|
1530
|
+
|
1531
|
+
@d.from_self.join_table(:left_outer, ds, :item_id => :id).sql.should ==
|
1532
|
+
'SELECT * FROM (SELECT * FROM "items") AS "t1" LEFT OUTER JOIN (SELECT * FROM categories WHERE (active IS TRUE)) AS "t2" ON ("t2"."item_id" = "t1"."id")'
|
1508
1533
|
end
|
1509
1534
|
|
1510
1535
|
specify "should support joining datasets and aliasing the join" do
|
@@ -1861,6 +1886,8 @@ context "Dataset compound operations" do
|
|
1861
1886
|
"SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)) AS t1"
|
1862
1887
|
@b.union(@a, true).sql.should == \
|
1863
1888
|
"SELECT * FROM (SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1"
|
1889
|
+
@b.union(@a, :all=>true).sql.should == \
|
1890
|
+
"SELECT * FROM (SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1"
|
1864
1891
|
end
|
1865
1892
|
|
1866
1893
|
specify "should support INTERSECT and INTERSECT ALL" do
|
@@ -1868,6 +1895,8 @@ context "Dataset compound operations" do
|
|
1868
1895
|
"SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT SELECT * FROM b WHERE (z = 2)) AS t1"
|
1869
1896
|
@b.intersect(@a, true).sql.should == \
|
1870
1897
|
"SELECT * FROM (SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
|
1898
|
+
@b.intersect(@a, :all=>true).sql.should == \
|
1899
|
+
"SELECT * FROM (SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
|
1871
1900
|
end
|
1872
1901
|
|
1873
1902
|
specify "should support EXCEPT and EXCEPT ALL" do
|
@@ -1875,6 +1904,40 @@ context "Dataset compound operations" do
|
|
1875
1904
|
"SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2)) AS t1"
|
1876
1905
|
@b.except(@a, true).sql.should == \
|
1877
1906
|
"SELECT * FROM (SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
|
1907
|
+
@b.except(@a, :all=>true).sql.should == \
|
1908
|
+
"SELECT * FROM (SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
|
1909
|
+
end
|
1910
|
+
|
1911
|
+
specify "should support :from_self=>false option to not wrap the compound in a SELECT * FROM (...)" do
|
1912
|
+
@b.union(@a, :from_self=>false).sql.should == \
|
1913
|
+
"SELECT * FROM b WHERE (z = 2) UNION SELECT * FROM a WHERE (z = 1)"
|
1914
|
+
@b.intersect(@a, :from_self=>false).sql.should == \
|
1915
|
+
"SELECT * FROM b WHERE (z = 2) INTERSECT SELECT * FROM a WHERE (z = 1)"
|
1916
|
+
@b.except(@a, :from_self=>false).sql.should == \
|
1917
|
+
"SELECT * FROM b WHERE (z = 2) EXCEPT SELECT * FROM a WHERE (z = 1)"
|
1918
|
+
|
1919
|
+
@b.union(@a, :from_self=>false, :all=>true).sql.should == \
|
1920
|
+
"SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)"
|
1921
|
+
@b.intersect(@a, :from_self=>false, :all=>true).sql.should == \
|
1922
|
+
"SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)"
|
1923
|
+
@b.except(@a, :from_self=>false, :all=>true).sql.should == \
|
1924
|
+
"SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)"
|
1925
|
+
end
|
1926
|
+
|
1927
|
+
specify "should raise an InvalidOperation if INTERSECT or EXCEPT is used and they are not supported" do
|
1928
|
+
@a.meta_def(:supports_intersect_except?){false}
|
1929
|
+
proc{@a.intersect(@b)}.should raise_error(Sequel::InvalidOperation)
|
1930
|
+
proc{@a.intersect(@b, true)}.should raise_error(Sequel::InvalidOperation)
|
1931
|
+
proc{@a.except(@b)}.should raise_error(Sequel::InvalidOperation)
|
1932
|
+
proc{@a.except(@b, true)}.should raise_error(Sequel::InvalidOperation)
|
1933
|
+
end
|
1934
|
+
|
1935
|
+
specify "should raise an InvalidOperation if INTERSECT ALL or EXCEPT ALL is used and they are not supported" do
|
1936
|
+
@a.meta_def(:supports_intersect_except_all?){false}
|
1937
|
+
proc{@a.intersect(@b)}.should_not raise_error
|
1938
|
+
proc{@a.intersect(@b, true)}.should raise_error(Sequel::InvalidOperation)
|
1939
|
+
proc{@a.except(@b)}.should_not raise_error
|
1940
|
+
proc{@a.except(@b, true)}.should raise_error(Sequel::InvalidOperation)
|
1878
1941
|
end
|
1879
1942
|
|
1880
1943
|
specify "should handle chained compound operations" do
|
@@ -2449,29 +2512,6 @@ class DummyMummyDatabase < Sequel::Database
|
|
2449
2512
|
end
|
2450
2513
|
end
|
2451
2514
|
|
2452
|
-
context "Dataset#table_exists?" do
|
2453
|
-
before do
|
2454
|
-
@db = DummyMummyDatabase.new
|
2455
|
-
end
|
2456
|
-
|
2457
|
-
specify "should otherwise try to select the first record from the table's dataset" do
|
2458
|
-
@db[:a].table_exists?.should be_false
|
2459
|
-
@db[:b].table_exists?.should be_true
|
2460
|
-
end
|
2461
|
-
|
2462
|
-
specify "should raise Sequel::Error if dataset references more than one table" do
|
2463
|
-
proc {@db.from(:a, :b).table_exists?}.should raise_error(Sequel::Error)
|
2464
|
-
end
|
2465
|
-
|
2466
|
-
specify "should raise Sequel::Error if dataset is from a subquery" do
|
2467
|
-
proc {@db.from(@db[:a]).table_exists?}.should raise_error(Sequel::Error)
|
2468
|
-
end
|
2469
|
-
|
2470
|
-
specify "should raise Sequel::Error if dataset has fixed sql" do
|
2471
|
-
proc {@db['select * from blah'].table_exists?}.should raise_error(Sequel::Error)
|
2472
|
-
end
|
2473
|
-
end
|
2474
|
-
|
2475
2515
|
context "Dataset#inspect" do
|
2476
2516
|
before do
|
2477
2517
|
@ds = Sequel::Dataset.new(nil).from(:blah)
|
@@ -2782,8 +2822,18 @@ context "Sequel::Dataset #set_overrides" do
|
|
2782
2822
|
end
|
2783
2823
|
end
|
2784
2824
|
|
2825
|
+
context "Sequel::Dataset#qualify" do
|
2826
|
+
specify "should qualify to the given table" do
|
2827
|
+
MockDatabase.new[:t].filter{a<b}.qualify(:e).sql.should == 'SELECT e.* FROM t WHERE (e.a < e.b)'
|
2828
|
+
end
|
2829
|
+
|
2830
|
+
specify "should qualify to the first source if no table if given" do
|
2831
|
+
MockDatabase.new[:t].filter{a<b}.qualify.sql.should == 'SELECT t.* FROM t WHERE (t.a < t.b)'
|
2832
|
+
end
|
2833
|
+
end
|
2834
|
+
|
2785
2835
|
context "Sequel::Dataset#qualify_to" do
|
2786
|
-
specify "should
|
2836
|
+
specify "should qualify to the given table" do
|
2787
2837
|
MockDatabase.new[:t].filter{a<b}.qualify_to(:e).sql.should == 'SELECT e.* FROM t WHERE (e.a < e.b)'
|
2788
2838
|
end
|
2789
2839
|
end
|
@@ -2861,8 +2911,53 @@ context "Sequel::Dataset#qualify_to_first_source" do
|
|
2861
2911
|
@ds.filter{a.sql_subscript(b,3)}.qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE t.a[t.b, 3]'
|
2862
2912
|
end
|
2863
2913
|
|
2914
|
+
specify "should handle SQL::PlaceholderLiteralStrings" do
|
2915
|
+
@ds.filter('? > ?', :a, 1).qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE (t.a > 1)'
|
2916
|
+
end
|
2917
|
+
|
2918
|
+
specify "should handle SQL::WindowFunctions" do
|
2919
|
+
@ds.meta_def(:supports_window_functions?){true}
|
2920
|
+
@ds.select{sum(:over, :args=>:a, :partition=>:b, :order=>:c){}}.qualify_to_first_source.sql.should == 'SELECT sum(t.a) OVER (PARTITION BY t.b ORDER BY t.c) FROM t'
|
2921
|
+
end
|
2922
|
+
|
2864
2923
|
specify "should handle all other objects by returning them unchanged" do
|
2865
|
-
@ds.select("a").filter{a(3)}.filter('blah').order('true'.lit).group('?'.lit(
|
2866
|
-
"SELECT 'a' FROM t WHERE (a(3) AND (blah)) GROUP BY a HAVING 'f' ORDER BY true"
|
2924
|
+
@ds.select("a").filter{a(3)}.filter('blah').order('true'.lit).group('a > ?'.lit(1)).having(false).qualify_to_first_source.sql.should == \
|
2925
|
+
"SELECT 'a' FROM t WHERE (a(3) AND (blah)) GROUP BY a > 1 HAVING 'f' ORDER BY true"
|
2867
2926
|
end
|
2868
2927
|
end
|
2928
|
+
|
2929
|
+
context "Sequel::Dataset #with and #with_recursive" do
|
2930
|
+
before do
|
2931
|
+
@db = MockDatabase.new
|
2932
|
+
@ds = @db[:t]
|
2933
|
+
end
|
2934
|
+
|
2935
|
+
specify "#with should take a name and dataset and use a WITH clause" do
|
2936
|
+
@ds.with(:t, @db[:x]).sql.should == 'WITH t AS (SELECT * FROM x) SELECT * FROM t'
|
2937
|
+
end
|
2938
|
+
|
2939
|
+
specify "#with_recursive should take a name, nonrecursive dataset, and recursive dataset, and use a WITH clause" do
|
2940
|
+
@ds.with_recursive(:t, @db[:x], @db[:t]).sql.should == 'WITH t AS (SELECT * FROM x UNION ALL SELECT * FROM t) SELECT * FROM t'
|
2941
|
+
end
|
2942
|
+
|
2943
|
+
specify "#with and #with_recursive should add to existing WITH clause if called multiple times" do
|
2944
|
+
@ds.with(:t, @db[:x]).with(:j, @db[:y]).sql.should == 'WITH t AS (SELECT * FROM x), j AS (SELECT * FROM y) SELECT * FROM t'
|
2945
|
+
@ds.with_recursive(:t, @db[:x], @db[:t]).with_recursive(:j, @db[:y], @db[:j]).sql.should == 'WITH t AS (SELECT * FROM x UNION ALL SELECT * FROM t), j AS (SELECT * FROM y UNION ALL SELECT * FROM j) SELECT * FROM t'
|
2946
|
+
@ds.with(:t, @db[:x]).with_recursive(:j, @db[:y], @db[:j]).sql.should == 'WITH t AS (SELECT * FROM x), j AS (SELECT * FROM y UNION ALL SELECT * FROM j) SELECT * FROM t'
|
2947
|
+
end
|
2948
|
+
|
2949
|
+
specify "#with and #with_recursive should take an :args option" do
|
2950
|
+
@ds.with(:t, @db[:x], :args=>[:b]).sql.should == 'WITH t(b) AS (SELECT * FROM x) SELECT * FROM t'
|
2951
|
+
@ds.with_recursive(:t, @db[:x], @db[:t], :args=>[:b, :c]).sql.should == 'WITH t(b, c) AS (SELECT * FROM x UNION ALL SELECT * FROM t) SELECT * FROM t'
|
2952
|
+
end
|
2953
|
+
|
2954
|
+
specify "#with_recursive should take an :union_all=>false option" do
|
2955
|
+
@ds.with_recursive(:t, @db[:x], @db[:t], :union_all=>false).sql.should == 'WITH t AS (SELECT * FROM x UNION SELECT * FROM t) SELECT * FROM t'
|
2956
|
+
end
|
2957
|
+
|
2958
|
+
specify "#with and #with_recursive should raise an error unless the dataset supports CTEs" do
|
2959
|
+
@ds.meta_def(:supports_cte?){false}
|
2960
|
+
proc{@ds.with(:t, @db[:x], :args=>[:b])}.should raise_error(Sequel::Error)
|
2961
|
+
proc{@ds.with_recursive(:t, @db[:x], @db[:t], :args=>[:b, :c])}.should raise_error(Sequel::Error)
|
2962
|
+
end
|
2963
|
+
end
|