sequel 3.21.0 → 3.28.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 +294 -0
- data/README.rdoc +20 -6
- data/Rakefile +20 -15
- data/doc/association_basics.rdoc +210 -43
- data/doc/dataset_basics.rdoc +4 -4
- data/doc/mass_assignment.rdoc +54 -0
- data/doc/migration.rdoc +15 -538
- data/doc/model_hooks.rdoc +64 -27
- data/doc/opening_databases.rdoc +37 -10
- data/doc/prepared_statements.rdoc +16 -10
- data/doc/reflection.rdoc +8 -2
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/schema_modification.rdoc +547 -0
- data/doc/testing.rdoc +106 -0
- data/doc/transactions.rdoc +97 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado.rb +12 -1
- data/lib/sequel/adapters/amalgalite.rb +4 -0
- data/lib/sequel/adapters/db2.rb +95 -58
- data/lib/sequel/adapters/do.rb +12 -0
- data/lib/sequel/adapters/firebird.rb +25 -203
- data/lib/sequel/adapters/ibmdb.rb +440 -0
- data/lib/sequel/adapters/informix.rb +4 -19
- data/lib/sequel/adapters/jdbc/as400.rb +0 -7
- data/lib/sequel/adapters/jdbc/db2.rb +49 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +34 -0
- data/lib/sequel/adapters/jdbc/h2.rb +16 -5
- data/lib/sequel/adapters/jdbc/informix.rb +31 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
- data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +2 -27
- data/lib/sequel/adapters/jdbc/postgresql.rb +6 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +34 -0
- data/lib/sequel/adapters/jdbc.rb +62 -29
- data/lib/sequel/adapters/mysql.rb +22 -139
- data/lib/sequel/adapters/mysql2.rb +9 -14
- data/lib/sequel/adapters/odbc/db2.rb +21 -0
- data/lib/sequel/adapters/odbc.rb +15 -3
- data/lib/sequel/adapters/oracle.rb +17 -1
- data/lib/sequel/adapters/postgres.rb +111 -16
- data/lib/sequel/adapters/shared/access.rb +21 -0
- data/lib/sequel/adapters/shared/db2.rb +290 -0
- data/lib/sequel/adapters/shared/firebird.rb +214 -0
- data/lib/sequel/adapters/shared/informix.rb +45 -0
- data/lib/sequel/adapters/shared/mssql.rb +85 -47
- data/lib/sequel/adapters/shared/mysql.rb +50 -7
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +143 -0
- data/lib/sequel/adapters/shared/oracle.rb +0 -4
- data/lib/sequel/adapters/shared/postgres.rb +75 -43
- data/lib/sequel/adapters/shared/sqlite.rb +56 -8
- data/lib/sequel/adapters/sqlite.rb +12 -11
- data/lib/sequel/adapters/swift/mysql.rb +9 -0
- data/lib/sequel/adapters/tinytds.rb +139 -7
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +55 -0
- data/lib/sequel/ast_transformer.rb +190 -0
- data/lib/sequel/connection_pool/threaded.rb +3 -2
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core.rb +6 -5
- data/lib/sequel/database/connecting.rb +5 -5
- data/lib/sequel/database/dataset.rb +1 -1
- data/lib/sequel/database/dataset_defaults.rb +1 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +38 -17
- data/lib/sequel/database/query.rb +50 -19
- data/lib/sequel/database/schema_generator.rb +8 -5
- data/lib/sequel/database/schema_methods.rb +52 -27
- data/lib/sequel/dataset/actions.rb +167 -48
- data/lib/sequel/dataset/features.rb +57 -8
- data/lib/sequel/dataset/graph.rb +1 -1
- data/lib/sequel/dataset/misc.rb +39 -20
- data/lib/sequel/dataset/mutation.rb +3 -3
- data/lib/sequel/dataset/prepared_statements.rb +29 -14
- data/lib/sequel/dataset/query.rb +182 -32
- data/lib/sequel/dataset/sql.rb +31 -58
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/extensions/migration.rb +6 -4
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +893 -309
- data/lib/sequel/model/base.rb +302 -105
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +13 -7
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/identity_map.rb +113 -6
- data/lib/sequel/plugins/many_through_many.rb +67 -5
- data/lib/sequel/plugins/prepared_statements.rb +140 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +12 -20
- data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
- data/lib/sequel/plugins/update_primary_key.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +107 -51
- data/lib/sequel/timezones.rb +12 -3
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +36 -0
- data/spec/adapters/mysql_spec.rb +36 -19
- data/spec/adapters/postgres_spec.rb +115 -28
- data/spec/adapters/spec_helper.rb +6 -0
- data/spec/adapters/sqlite_spec.rb +11 -0
- data/spec/core/connection_pool_spec.rb +62 -77
- data/spec/core/database_spec.rb +244 -287
- data/spec/core/dataset_spec.rb +383 -34
- data/spec/core/expression_filters_spec.rb +159 -41
- data/spec/core/schema_spec.rb +326 -3
- data/spec/core/spec_helper.rb +45 -0
- data/spec/extensions/association_pks_spec.rb +38 -0
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/identity_map_spec.rb +162 -0
- data/spec/extensions/many_through_many_spec.rb +195 -20
- data/spec/extensions/migration_spec.rb +17 -17
- data/spec/extensions/nested_attributes_spec.rb +1 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
- data/spec/extensions/prepared_statements_spec.rb +72 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
- data/spec/extensions/schema_dumper_spec.rb +2 -2
- data/spec/extensions/schema_spec.rb +12 -20
- data/spec/extensions/serialization_modification_detection_spec.rb +36 -0
- data/spec/extensions/single_table_inheritance_spec.rb +11 -0
- data/spec/extensions/spec_helper.rb +3 -1
- data/spec/extensions/to_dot_spec.rb +3 -5
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +405 -15
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +240 -20
- data/spec/integration/plugin_test.rb +142 -5
- data/spec/integration/prepared_statement_test.rb +174 -95
- data/spec/integration/schema_test.rb +128 -16
- data/spec/integration/spec_helper.rb +15 -0
- data/spec/integration/transaction_test.rb +40 -0
- data/spec/integration/type_test.rb +16 -2
- data/spec/model/association_reflection_spec.rb +91 -0
- data/spec/model/associations_spec.rb +476 -5
- data/spec/model/base_spec.rb +91 -1
- data/spec/model/eager_loading_spec.rb +519 -31
- data/spec/model/hooks_spec.rb +161 -0
- data/spec/model/model_spec.rb +89 -2
- data/spec/model/plugins_spec.rb +17 -0
- data/spec/model/record_spec.rb +184 -12
- data/spec/model/spec_helper.rb +5 -0
- data/spec/model/validations_spec.rb +11 -0
- metadata +85 -34
data/spec/core/dataset_spec.rb
CHANGED
|
@@ -538,34 +538,55 @@ describe "Dataset#where" do
|
|
|
538
538
|
|
|
539
539
|
specify "should handle IN/NOT IN queries with multiple columns and a dataset where the database doesn't support it" do
|
|
540
540
|
@dataset.meta_def(:supports_multiple_column_in?){false}
|
|
541
|
-
|
|
541
|
+
db = MockDatabase.new()
|
|
542
|
+
d1 = db[:test].select(:id1, :id2).filter(:region=>'Asia')
|
|
543
|
+
d1.instance_variable_set(:@columns, [:id1, :id2])
|
|
542
544
|
def d1.fetch_rows(sql)
|
|
543
|
-
|
|
545
|
+
db << sql
|
|
544
546
|
@columns = [:id1, :id2]
|
|
545
547
|
yield(:id1=>1, :id2=>2)
|
|
546
548
|
yield(:id1=>3, :id2=>4)
|
|
547
549
|
end
|
|
548
|
-
d1.instance_variable_get(:@sql_used).should == nil
|
|
549
550
|
@dataset.filter([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (((id1 = 1) AND (id2 = 2)) OR ((id1 = 3) AND (id2 = 4)))"
|
|
550
|
-
|
|
551
|
-
|
|
551
|
+
db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
|
|
552
|
+
db.sqls.clear
|
|
552
553
|
@dataset.exclude([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (((id1 != 1) OR (id2 != 2)) AND ((id1 != 3) OR (id2 != 4)))"
|
|
553
|
-
|
|
554
|
+
db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
|
|
554
555
|
end
|
|
555
556
|
|
|
556
557
|
specify "should handle IN/NOT IN queries with multiple columns and an empty dataset where the database doesn't support it" do
|
|
557
558
|
@dataset.meta_def(:supports_multiple_column_in?){false}
|
|
558
|
-
|
|
559
|
+
db = MockDatabase.new()
|
|
560
|
+
d1 = db[:test].select(:id1, :id2).filter(:region=>'Asia')
|
|
561
|
+
d1.instance_variable_set(:@columns, [:id1, :id2])
|
|
559
562
|
def d1.fetch_rows(sql)
|
|
560
|
-
|
|
563
|
+
db << sql
|
|
561
564
|
@columns = [:id1, :id2]
|
|
562
565
|
end
|
|
563
|
-
d1.instance_variable_get(:@sql_used).should == nil
|
|
564
566
|
@dataset.filter([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE ((id1 != id1) AND (id2 != id2))"
|
|
565
|
-
|
|
566
|
-
|
|
567
|
+
db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
|
|
568
|
+
db.sqls.clear
|
|
567
569
|
@dataset.exclude([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (1 = 1)"
|
|
568
|
-
|
|
570
|
+
db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
specify "should handle IN/NOT IN queries for datasets with row_procs" do
|
|
574
|
+
@dataset.meta_def(:supports_multiple_column_in?){false}
|
|
575
|
+
db = MockDatabase.new()
|
|
576
|
+
d1 = db[:test].select(:id1, :id2).filter(:region=>'Asia')
|
|
577
|
+
d1.row_proc = proc{|h| Object.new}
|
|
578
|
+
d1.instance_variable_set(:@columns, [:id1, :id2])
|
|
579
|
+
def d1.fetch_rows(sql)
|
|
580
|
+
db << sql
|
|
581
|
+
@columns = [:id1, :id2]
|
|
582
|
+
yield(:id1=>1, :id2=>2)
|
|
583
|
+
yield(:id1=>3, :id2=>4)
|
|
584
|
+
end
|
|
585
|
+
@dataset.filter([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (((id1 = 1) AND (id2 = 2)) OR ((id1 = 3) AND (id2 = 4)))"
|
|
586
|
+
db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
|
|
587
|
+
db.sqls.clear
|
|
588
|
+
@dataset.exclude([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (((id1 != 1) OR (id2 != 2)) AND ((id1 != 3) OR (id2 != 4)))"
|
|
589
|
+
db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
|
|
569
590
|
end
|
|
570
591
|
|
|
571
592
|
specify "should accept a subquery for an EXISTS clause" do
|
|
@@ -601,10 +622,18 @@ describe "Dataset#where" do
|
|
|
601
622
|
end
|
|
602
623
|
|
|
603
624
|
specify "should accept true and false as arguments" do
|
|
604
|
-
@dataset.filter(true).sql.should ==
|
|
605
|
-
|
|
606
|
-
@dataset.filter(false).sql.should ==
|
|
607
|
-
|
|
625
|
+
@dataset.filter(true).sql.should == "SELECT * FROM test WHERE 't'"
|
|
626
|
+
@dataset.filter(Sequel::SQLTRUE).sql.should == "SELECT * FROM test WHERE 't'"
|
|
627
|
+
@dataset.filter(false).sql.should == "SELECT * FROM test WHERE 'f'"
|
|
628
|
+
@dataset.filter(Sequel::SQLFALSE).sql.should == "SELECT * FROM test WHERE 'f'"
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
specify "should use boolean expression if dataset does not support where true/false" do
|
|
632
|
+
def @dataset.supports_where_true?() false end
|
|
633
|
+
@dataset.filter(true).sql.should == "SELECT * FROM test WHERE (1 = 1)"
|
|
634
|
+
@dataset.filter(Sequel::SQLTRUE).sql.should == "SELECT * FROM test WHERE (1 = 1)"
|
|
635
|
+
@dataset.filter(false).sql.should == "SELECT * FROM test WHERE (1 = 0)"
|
|
636
|
+
@dataset.filter(Sequel::SQLFALSE).sql.should == "SELECT * FROM test WHERE (1 = 0)"
|
|
608
637
|
end
|
|
609
638
|
|
|
610
639
|
specify "should allow the use of multiple arguments" do
|
|
@@ -738,6 +767,11 @@ describe "Dataset#exclude" do
|
|
|
738
767
|
"SELECT * FROM test WHERE (region != 'Asia')"
|
|
739
768
|
end
|
|
740
769
|
|
|
770
|
+
specify "should affect the having clause if having clause is already used" do
|
|
771
|
+
@dataset.group_and_count(:name).having{count > 2}.exclude{count > 5}.sql.should ==
|
|
772
|
+
"SELECT name, count(*) AS count FROM test GROUP BY name HAVING ((count > 2) AND (count <= 5))"
|
|
773
|
+
end
|
|
774
|
+
|
|
741
775
|
specify "should take multiple conditions as a hash and express the logic correctly in SQL" do
|
|
742
776
|
@dataset.exclude(:region => 'Asia', :name => 'Japan').select_sql.
|
|
743
777
|
should match(Regexp.union(/WHERE \(\(region != 'Asia'\) OR \(name != 'Japan'\)\)/,
|
|
@@ -772,6 +806,28 @@ describe "Dataset#exclude" do
|
|
|
772
806
|
end
|
|
773
807
|
end
|
|
774
808
|
|
|
809
|
+
describe "Dataset#exclude_where" do
|
|
810
|
+
before do
|
|
811
|
+
@dataset = Sequel::Dataset.new(nil).from(:test)
|
|
812
|
+
end
|
|
813
|
+
|
|
814
|
+
specify "should correctly negate the expression and add it to the where clause" do
|
|
815
|
+
@dataset.exclude_where(:region=>'Asia').sql.should == "SELECT * FROM test WHERE (region != 'Asia')"
|
|
816
|
+
@dataset.exclude_where(:region=>'Asia').exclude_where(:region=>'NA').sql.should == "SELECT * FROM test WHERE ((region != 'Asia') AND (region != 'NA'))"
|
|
817
|
+
end
|
|
818
|
+
|
|
819
|
+
specify "should affect the where clause even if having clause is already used" do
|
|
820
|
+
@dataset.group_and_count(:name).having{count > 2}.exclude_where(:region=>'Asia').sql.should ==
|
|
821
|
+
"SELECT name, count(*) AS count FROM test WHERE (region != 'Asia') GROUP BY name HAVING (count > 2)"
|
|
822
|
+
end
|
|
823
|
+
end
|
|
824
|
+
|
|
825
|
+
describe "Dataset#exclude_having" do
|
|
826
|
+
specify "should correctly negate the expression and add it to the having clause" do
|
|
827
|
+
Sequel::Dataset.new(nil).from(:test).exclude_having{count > 2}.exclude_having{count < 0}.sql.should == "SELECT * FROM test HAVING ((count <= 2) AND (count >= 0))"
|
|
828
|
+
end
|
|
829
|
+
end
|
|
830
|
+
|
|
775
831
|
describe "Dataset#invert" do
|
|
776
832
|
before do
|
|
777
833
|
@d = Sequel::Dataset.new(nil).from(:test)
|
|
@@ -876,7 +932,7 @@ describe "Dataset#group_by" do
|
|
|
876
932
|
"SELECT * FROM test GROUP BY (type_id IS NULL)"
|
|
877
933
|
end
|
|
878
934
|
|
|
879
|
-
specify "should ungroup when passed nil
|
|
935
|
+
specify "should ungroup when passed nil or no arguments" do
|
|
880
936
|
@dataset.group_by.select_sql.should ==
|
|
881
937
|
"SELECT * FROM test"
|
|
882
938
|
@dataset.group_by(nil).select_sql.should ==
|
|
@@ -894,6 +950,13 @@ describe "Dataset#group_by" do
|
|
|
894
950
|
@dataset.group(:type_id=>nil).select_sql.should ==
|
|
895
951
|
"SELECT * FROM test GROUP BY (type_id IS NULL)"
|
|
896
952
|
end
|
|
953
|
+
|
|
954
|
+
specify "should take a virtual row block" do
|
|
955
|
+
@dataset.group{type_id > 1}.sql.should == "SELECT * FROM test GROUP BY (type_id > 1)"
|
|
956
|
+
@dataset.group_by{type_id > 1}.sql.should == "SELECT * FROM test GROUP BY (type_id > 1)"
|
|
957
|
+
@dataset.group{[type_id > 1, type_id < 2]}.sql.should == "SELECT * FROM test GROUP BY (type_id > 1), (type_id < 2)"
|
|
958
|
+
@dataset.group(:foo){type_id > 1}.sql.should == "SELECT * FROM test GROUP BY foo, (type_id > 1)"
|
|
959
|
+
end
|
|
897
960
|
end
|
|
898
961
|
|
|
899
962
|
describe "Dataset#as" do
|
|
@@ -965,6 +1028,12 @@ describe "Dataset#literal" do
|
|
|
965
1028
|
d.literal(d).should == "(#{d.sql})"
|
|
966
1029
|
end
|
|
967
1030
|
|
|
1031
|
+
specify "should literalize Sequel::SQLTime properly" do
|
|
1032
|
+
t = Sequel::SQLTime.now
|
|
1033
|
+
s = t.strftime("'%H:%M:%S")
|
|
1034
|
+
@dataset.literal(t).should == "#{s}.#{sprintf('%06i', t.usec)}'"
|
|
1035
|
+
end
|
|
1036
|
+
|
|
968
1037
|
specify "should literalize Time properly" do
|
|
969
1038
|
t = Time.now
|
|
970
1039
|
s = t.strftime("'%Y-%m-%d %H:%M:%S")
|
|
@@ -1198,6 +1267,27 @@ describe "Dataset#select" do
|
|
|
1198
1267
|
end
|
|
1199
1268
|
end
|
|
1200
1269
|
|
|
1270
|
+
describe "Dataset#select_group" do
|
|
1271
|
+
before do
|
|
1272
|
+
@d = Sequel::Dataset.new(nil).from(:test)
|
|
1273
|
+
end
|
|
1274
|
+
|
|
1275
|
+
specify "should set both SELECT and GROUP" do
|
|
1276
|
+
@d.select_group(:name).sql.should == 'SELECT name FROM test GROUP BY name'
|
|
1277
|
+
@d.select_group(:a, :b__c, :d___e).sql.should == 'SELECT a, b.c, d AS e FROM test GROUP BY a, b.c, d'
|
|
1278
|
+
end
|
|
1279
|
+
|
|
1280
|
+
specify "should remove from both SELECT and GROUP if no arguments" do
|
|
1281
|
+
@d.select_group(:name).select_group.sql.should == 'SELECT * FROM test'
|
|
1282
|
+
end
|
|
1283
|
+
|
|
1284
|
+
specify "should accept virtual row blocks" do
|
|
1285
|
+
@d.select_group{name}.sql.should == 'SELECT name FROM test GROUP BY name'
|
|
1286
|
+
@d.select_group{[name, f(v).as(a)]}.sql.should == 'SELECT name, f(v) AS a FROM test GROUP BY name, f(v)'
|
|
1287
|
+
@d.select_group(:name){f(v).as(a)}.sql.should == 'SELECT name, f(v) AS a FROM test GROUP BY name, f(v)'
|
|
1288
|
+
end
|
|
1289
|
+
end
|
|
1290
|
+
|
|
1201
1291
|
describe "Dataset#select_all" do
|
|
1202
1292
|
before do
|
|
1203
1293
|
@d = Sequel::Dataset.new(nil).from(:test)
|
|
@@ -1210,6 +1300,40 @@ describe "Dataset#select_all" do
|
|
|
1210
1300
|
specify "should overrun the previous select option" do
|
|
1211
1301
|
@d.select!(:a, :b, :c).select_all.sql.should == 'SELECT * FROM test'
|
|
1212
1302
|
end
|
|
1303
|
+
|
|
1304
|
+
specify "should select all columns in a table if given an argument" do
|
|
1305
|
+
@d.select_all(:test).sql.should == 'SELECT test.* FROM test'
|
|
1306
|
+
end
|
|
1307
|
+
|
|
1308
|
+
specify "should select all columns all tables if given a multiple arguments" do
|
|
1309
|
+
@d.select_all(:test, :foo).sql.should == 'SELECT test.*, foo.* FROM test'
|
|
1310
|
+
end
|
|
1311
|
+
|
|
1312
|
+
specify "should work correctly with qualified symbols" do
|
|
1313
|
+
@d.select_all(:sch__test).sql.should == 'SELECT sch.test.* FROM test'
|
|
1314
|
+
end
|
|
1315
|
+
|
|
1316
|
+
specify "should work correctly with aliased symbols" do
|
|
1317
|
+
@d.select_all(:test___al).sql.should == 'SELECT al.* FROM test'
|
|
1318
|
+
@d.select_all(:sch__test___al).sql.should == 'SELECT al.* FROM test'
|
|
1319
|
+
end
|
|
1320
|
+
|
|
1321
|
+
specify "should work correctly with SQL::Identifiers" do
|
|
1322
|
+
@d.select_all(:test.identifier).sql.should == 'SELECT test.* FROM test'
|
|
1323
|
+
end
|
|
1324
|
+
|
|
1325
|
+
specify "should work correctly with SQL::QualifiedIdentifier" do
|
|
1326
|
+
@d.select_all(:test.qualify(:sch)).sql.should == 'SELECT sch.test.* FROM test'
|
|
1327
|
+
end
|
|
1328
|
+
|
|
1329
|
+
specify "should work correctly with SQL::AliasedExpressions" do
|
|
1330
|
+
@d.select_all(:test.as(:al)).sql.should == 'SELECT al.* FROM test'
|
|
1331
|
+
end
|
|
1332
|
+
|
|
1333
|
+
specify "should work correctly with SQL::JoinClauses" do
|
|
1334
|
+
d = @d.cross_join(:foo).cross_join(:test___al)
|
|
1335
|
+
@d.select_all(*d.opts[:join]).sql.should == 'SELECT foo.*, al.* FROM test'
|
|
1336
|
+
end
|
|
1213
1337
|
end
|
|
1214
1338
|
|
|
1215
1339
|
describe "Dataset#select_more" do
|
|
@@ -1254,6 +1378,14 @@ describe "Dataset#select_append" do
|
|
|
1254
1378
|
@d.select(:a).select_append{|o| o.b}.sql.should == 'SELECT a, b FROM test'
|
|
1255
1379
|
@d.select(:a.*).select_append(:b.*){b(1)}.sql.should == 'SELECT a.*, b.*, b(1) FROM test'
|
|
1256
1380
|
end
|
|
1381
|
+
|
|
1382
|
+
specify "should select from all from and join tables if SELECT *, column not supported" do
|
|
1383
|
+
@d.meta_def(:supports_select_all_and_column?){false}
|
|
1384
|
+
@d.select_append(:b).sql.should == 'SELECT test.*, b FROM test'
|
|
1385
|
+
@d.from(:test, :c).select_append(:b).sql.should == 'SELECT test.*, c.*, b FROM test, c'
|
|
1386
|
+
@d.cross_join(:c).select_append(:b).sql.should == 'SELECT test.*, c.*, b FROM test CROSS JOIN c'
|
|
1387
|
+
@d.cross_join(:c).cross_join(:d).select_append(:b).sql.should == 'SELECT test.*, c.*, d.*, b FROM test CROSS JOIN c CROSS JOIN d'
|
|
1388
|
+
end
|
|
1257
1389
|
end
|
|
1258
1390
|
|
|
1259
1391
|
describe "Dataset#order" do
|
|
@@ -1355,6 +1487,13 @@ describe "Dataset#with_sql" do
|
|
|
1355
1487
|
specify "should keep row_proc" do
|
|
1356
1488
|
@dataset.with_sql('SELECT 1 FROM test').row_proc.should == @dataset.row_proc
|
|
1357
1489
|
end
|
|
1490
|
+
|
|
1491
|
+
specify "should work with method symbols and arguments" do
|
|
1492
|
+
@dataset.with_sql(:delete_sql).sql.should == 'DELETE FROM test'
|
|
1493
|
+
@dataset.with_sql(:insert_sql, :b=>1).sql.should == 'INSERT INTO test (b) VALUES (1)'
|
|
1494
|
+
@dataset.with_sql(:update_sql, :b=>1).sql.should == 'UPDATE test SET b = 1'
|
|
1495
|
+
end
|
|
1496
|
+
|
|
1358
1497
|
end
|
|
1359
1498
|
|
|
1360
1499
|
describe "Dataset#order_by" do
|
|
@@ -1571,17 +1710,6 @@ describe "Dataset#qualified_column_name" do
|
|
|
1571
1710
|
end
|
|
1572
1711
|
end
|
|
1573
1712
|
|
|
1574
|
-
class DummyDataset < Sequel::Dataset
|
|
1575
|
-
VALUES = [
|
|
1576
|
-
{:a => 1, :b => 2},
|
|
1577
|
-
{:a => 3, :b => 4},
|
|
1578
|
-
{:a => 5, :b => 6}
|
|
1579
|
-
]
|
|
1580
|
-
def fetch_rows(sql, &block)
|
|
1581
|
-
VALUES.each(&block)
|
|
1582
|
-
end
|
|
1583
|
-
end
|
|
1584
|
-
|
|
1585
1713
|
describe "Dataset#map" do
|
|
1586
1714
|
before do
|
|
1587
1715
|
@d = DummyDataset.new(nil).from(:items)
|
|
@@ -1595,6 +1723,10 @@ describe "Dataset#map" do
|
|
|
1595
1723
|
@d.map(:a).should == [1, 3, 5]
|
|
1596
1724
|
end
|
|
1597
1725
|
|
|
1726
|
+
specify "should support multiple column names if an array of column names is given" do
|
|
1727
|
+
@d.map([:a, :b]).should == [[1, 2], [3, 4], [5, 6]]
|
|
1728
|
+
end
|
|
1729
|
+
|
|
1598
1730
|
specify "should return the complete dataset values if nothing is given" do
|
|
1599
1731
|
@d.map.to_a.should == DummyDataset::VALUES
|
|
1600
1732
|
end
|
|
@@ -1614,6 +1746,13 @@ describe "Dataset#to_hash" do
|
|
|
1614
1746
|
@d.to_hash(:a).should == {1 => {:a => 1, :b => 2}, 3 => {:a => 3, :b => 4}, 5 => {:a => 5, :b => 6}}
|
|
1615
1747
|
@d.to_hash(:b).should == {2 => {:a => 1, :b => 2}, 4 => {:a => 3, :b => 4}, 6 => {:a => 5, :b => 6}}
|
|
1616
1748
|
end
|
|
1749
|
+
|
|
1750
|
+
specify "should support using an array of columns as either the key or the value" do
|
|
1751
|
+
@d.to_hash([:a, :b], :b).should == {[1, 2] => 2, [3, 4] => 4, [5, 6] => 6}
|
|
1752
|
+
@d.to_hash(:b, [:a, :b]).should == {2 => [1, 2], 4 => [3, 4], 6 => [5, 6]}
|
|
1753
|
+
@d.to_hash([:b, :a], [:a, :b]).should == {[2, 1] => [1, 2], [4, 3] => [3, 4], [6, 5] => [5, 6]}
|
|
1754
|
+
@d.to_hash([:a, :b]).should == {[1, 2] => {:a => 1, :b => 2}, [3, 4] => {:a => 3, :b => 4}, [5, 6] => {:a => 5, :b => 6}}
|
|
1755
|
+
end
|
|
1617
1756
|
end
|
|
1618
1757
|
|
|
1619
1758
|
describe "Dataset#distinct" do
|
|
@@ -1753,6 +1892,12 @@ describe "Dataset#group_and_count" do
|
|
|
1753
1892
|
@ds.group_and_count(:name.identifier.as(:n)).sql.should ==
|
|
1754
1893
|
"SELECT name AS n, count(*) AS count FROM test GROUP BY name"
|
|
1755
1894
|
end
|
|
1895
|
+
|
|
1896
|
+
specify "should take a virtual row block" do
|
|
1897
|
+
@ds.group_and_count{(type_id > 1).as(t)}.sql.should == "SELECT (type_id > 1) AS t, count(*) AS count FROM test GROUP BY (type_id > 1)"
|
|
1898
|
+
@ds.group_and_count{[(type_id > 1).as(t), type_id < 2]}.sql.should == "SELECT (type_id > 1) AS t, (type_id < 2), count(*) AS count FROM test GROUP BY (type_id > 1), (type_id < 2)"
|
|
1899
|
+
@ds.group_and_count(:foo){type_id > 1}.sql.should == "SELECT foo, (type_id > 1), count(*) AS count FROM test GROUP BY foo, (type_id > 1)"
|
|
1900
|
+
end
|
|
1756
1901
|
end
|
|
1757
1902
|
|
|
1758
1903
|
describe "Dataset#empty?" do
|
|
@@ -2081,6 +2226,12 @@ describe "Dataset#join_table" do
|
|
|
2081
2226
|
@d.join(:categories, [:id]).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."id" = "items"."id")'
|
|
2082
2227
|
end
|
|
2083
2228
|
|
|
2229
|
+
specify "should hoist WITH clauses from subqueries if the dataset doesn't support CTEs in subselects" do
|
|
2230
|
+
@d.meta_def(:supports_cte?){true}
|
|
2231
|
+
@d.meta_def(:supports_cte_in_subselect?){false}
|
|
2232
|
+
@d.join(Sequel::Dataset.new(nil).from(:categories).with(:a, Sequel::Dataset.new(nil).from(:b)), [:id]).sql.should == 'WITH "a" AS (SELECT * FROM b) SELECT * FROM "items" INNER JOIN (SELECT * FROM categories) AS "t1" USING ("id")'
|
|
2233
|
+
end
|
|
2234
|
+
|
|
2084
2235
|
specify "should raise an error if using an array of symbols with a block" do
|
|
2085
2236
|
proc{@d.join(:categories, [:id]){|j,lj,js|}}.should raise_error(Sequel::Error)
|
|
2086
2237
|
end
|
|
@@ -2155,7 +2306,6 @@ describe "Dataset#join_table" do
|
|
|
2155
2306
|
'SELECT * FROM "items" AS "i" INNER JOIN "categories" AS "c2" ON ("c2"."category_id" = "i2"."id")'
|
|
2156
2307
|
end
|
|
2157
2308
|
|
|
2158
|
-
|
|
2159
2309
|
specify "should not allow insert, update, delete, or truncate" do
|
|
2160
2310
|
proc{@d.join(:categories, :a=>:d).insert_sql}.should raise_error(Sequel::InvalidOperation)
|
|
2161
2311
|
proc{@d.join(:categories, :a=>:d).update_sql(:a=>1)}.should raise_error(Sequel::InvalidOperation)
|
|
@@ -3283,6 +3433,7 @@ describe "Dataset prepared statements and bound variables " do
|
|
|
3283
3433
|
ds
|
|
3284
3434
|
end
|
|
3285
3435
|
@ds = @db[:items]
|
|
3436
|
+
@ds.meta_def(:insert_sql){|*v| "#{super(*v)}#{' RETURNING *' if opts.has_key?(:returning)}" }
|
|
3286
3437
|
end
|
|
3287
3438
|
|
|
3288
3439
|
specify "#call should take a type and bind hash and interpolate it" do
|
|
@@ -3291,11 +3442,13 @@ describe "Dataset prepared statements and bound variables " do
|
|
|
3291
3442
|
@ds.filter(:num=>:$n).call(:delete, :n=>1)
|
|
3292
3443
|
@ds.filter(:num=>:$n).call(:update, {:n=>1, :n2=>2}, :num=>:$n2)
|
|
3293
3444
|
@ds.call(:insert, {:n=>1}, :num=>:$n)
|
|
3445
|
+
@ds.call(:insert_select, {:n=>1}, :num=>:$n)
|
|
3294
3446
|
@db.sqls.should == ['SELECT * FROM items WHERE (num = 1)',
|
|
3295
3447
|
'SELECT * FROM items WHERE (num = 1) LIMIT 1',
|
|
3296
3448
|
'DELETE FROM items WHERE (num = 1)',
|
|
3297
3449
|
'UPDATE items SET num = 2 WHERE (num = 1)',
|
|
3298
|
-
'INSERT INTO items (num) VALUES (1)'
|
|
3450
|
+
'INSERT INTO items (num) VALUES (1)',
|
|
3451
|
+
'INSERT INTO items (num) VALUES (1) RETURNING *']
|
|
3299
3452
|
end
|
|
3300
3453
|
|
|
3301
3454
|
specify "#prepare should take a type and name and store it in the database for later use with call" do
|
|
@@ -3305,18 +3458,21 @@ describe "Dataset prepared statements and bound variables " do
|
|
|
3305
3458
|
pss << @ds.filter(:num=>:$n).prepare(:delete, :dn)
|
|
3306
3459
|
pss << @ds.filter(:num=>:$n).prepare(:update, :un, :num=>:$n2)
|
|
3307
3460
|
pss << @ds.prepare(:insert, :in, :num=>:$n)
|
|
3308
|
-
@
|
|
3309
|
-
[:
|
|
3461
|
+
pss << @ds.prepare(:insert_select, :ins, :num=>:$n)
|
|
3462
|
+
@db.prepared_statements.keys.sort_by{|k| k.to_s}.should == [:dn, :fn, :in, :ins, :sn, :un]
|
|
3463
|
+
[:sn, :fn, :dn, :un, :in, :ins].each_with_index{|x, i| @db.prepared_statements[x].should == pss[i]}
|
|
3310
3464
|
@db.call(:sn, :n=>1)
|
|
3311
3465
|
@db.call(:fn, :n=>1)
|
|
3312
3466
|
@db.call(:dn, :n=>1)
|
|
3313
3467
|
@db.call(:un, :n=>1, :n2=>2)
|
|
3314
3468
|
@db.call(:in, :n=>1)
|
|
3469
|
+
@db.call(:ins, :n=>1)
|
|
3315
3470
|
@db.sqls.should == ['SELECT * FROM items WHERE (num = 1)',
|
|
3316
3471
|
'SELECT * FROM items WHERE (num = 1) LIMIT 1',
|
|
3317
3472
|
'DELETE FROM items WHERE (num = 1)',
|
|
3318
3473
|
'UPDATE items SET num = 2 WHERE (num = 1)',
|
|
3319
|
-
'INSERT INTO items (num) VALUES (1)'
|
|
3474
|
+
'INSERT INTO items (num) VALUES (1)',
|
|
3475
|
+
'INSERT INTO items (num) VALUES (1) RETURNING *']
|
|
3320
3476
|
end
|
|
3321
3477
|
|
|
3322
3478
|
specify "#inspect should indicate it is a prepared statement with the prepared SQL" do
|
|
@@ -3599,6 +3755,76 @@ describe "Sequel::Dataset#qualify_to_first_source" do
|
|
|
3599
3755
|
end
|
|
3600
3756
|
end
|
|
3601
3757
|
|
|
3758
|
+
describe "Sequel::Dataset#unbind" do
|
|
3759
|
+
before do
|
|
3760
|
+
@ds = MockDatabase.new[:t]
|
|
3761
|
+
@u = proc{|ds| ds, bv = ds.unbind; [ds.sql, bv]}
|
|
3762
|
+
end
|
|
3763
|
+
|
|
3764
|
+
specify "should unbind values assigned to equality and inequality statements" do
|
|
3765
|
+
@ds.filter(:foo=>1).unbind.first.sql.should == "SELECT * FROM t WHERE (foo = $foo)"
|
|
3766
|
+
@ds.exclude(:foo=>1).unbind.first.sql.should == "SELECT * FROM t WHERE (foo != $foo)"
|
|
3767
|
+
@ds.filter{foo > 1}.unbind.first.sql.should == "SELECT * FROM t WHERE (foo > $foo)"
|
|
3768
|
+
@ds.filter{foo >= 1}.unbind.first.sql.should == "SELECT * FROM t WHERE (foo >= $foo)"
|
|
3769
|
+
@ds.filter{foo < 1}.unbind.first.sql.should == "SELECT * FROM t WHERE (foo < $foo)"
|
|
3770
|
+
@ds.filter{foo <= 1}.unbind.first.sql.should == "SELECT * FROM t WHERE (foo <= $foo)"
|
|
3771
|
+
end
|
|
3772
|
+
|
|
3773
|
+
specify "should return variables that could be used bound to recreate the previous query" do
|
|
3774
|
+
@ds.filter(:foo=>1).unbind.last.should == {:foo=>1}
|
|
3775
|
+
@ds.exclude(:foo=>1).unbind.last.should == {:foo=>1}
|
|
3776
|
+
end
|
|
3777
|
+
|
|
3778
|
+
specify "should handle numerics, strings, dates, times, and datetimes" do
|
|
3779
|
+
@u[@ds.filter(:foo=>1)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>1}]
|
|
3780
|
+
@u[@ds.filter(:foo=>1.0)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>1.0}]
|
|
3781
|
+
@u[@ds.filter(:foo=>BigDecimal.new('1.0'))].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>BigDecimal.new('1.0')}]
|
|
3782
|
+
@u[@ds.filter(:foo=>'a')].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>'a'}]
|
|
3783
|
+
@u[@ds.filter(:foo=>Date.today)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>Date.today}]
|
|
3784
|
+
t = Time.now
|
|
3785
|
+
@u[@ds.filter(:foo=>t)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>t}]
|
|
3786
|
+
dt = DateTime.now
|
|
3787
|
+
@u[@ds.filter(:foo=>dt)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>dt}]
|
|
3788
|
+
end
|
|
3789
|
+
|
|
3790
|
+
specify "should not unbind literal strings" do
|
|
3791
|
+
@u[@ds.filter(:foo=>'a'.lit)].should == ["SELECT * FROM t WHERE (foo = a)", {}]
|
|
3792
|
+
end
|
|
3793
|
+
|
|
3794
|
+
specify "should not unbind Identifiers, QualifiedIdentifiers, or Symbols used as booleans" do
|
|
3795
|
+
@u[@ds.filter(:foo).filter{bar}.filter{foo__bar}].should == ["SELECT * FROM t WHERE (foo AND bar AND foo.bar)", {}]
|
|
3796
|
+
end
|
|
3797
|
+
|
|
3798
|
+
specify "should not unbind for values it doesn't understand" do
|
|
3799
|
+
@u[@ds.filter(:foo=>Class.new{def sql_literal(ds) 'bar' end}.new)].should == ["SELECT * FROM t WHERE (foo = bar)", {}]
|
|
3800
|
+
end
|
|
3801
|
+
|
|
3802
|
+
specify "should handle QualifiedIdentifiers" do
|
|
3803
|
+
@u[@ds.filter{foo__bar > 1}].should == ["SELECT * FROM t WHERE (foo.bar > $foo.bar)", {:"foo.bar"=>1}]
|
|
3804
|
+
end
|
|
3805
|
+
|
|
3806
|
+
specify "should handle deep nesting" do
|
|
3807
|
+
@u[@ds.filter{foo > 1}.and{bar < 2}.or(:baz=>3).and({~{:x=>4}=>true}.case(false))].should == ["SELECT * FROM t WHERE ((((foo > $foo) AND (bar < $bar)) OR (baz = $baz)) AND (CASE WHEN (x != $x) THEN 't' ELSE 'f' END))", {:foo=>1, :bar=>2, :baz=>3, :x=>4}]
|
|
3808
|
+
end
|
|
3809
|
+
|
|
3810
|
+
specify "should handle JOIN ON" do
|
|
3811
|
+
@u[@ds.cross_join(:x).join(:a, [:u]).join(:b, [[:c, :d], [:e,1]])].should == ["SELECT * FROM t CROSS JOIN x INNER JOIN a USING (u) INNER JOIN b ON ((b.c = a.d) AND (b.e = $b.e))", {:"b.e"=>1}]
|
|
3812
|
+
end
|
|
3813
|
+
|
|
3814
|
+
specify "should raise an UnbindDuplicate exception if same variable is used with multiple different values" do
|
|
3815
|
+
proc{@ds.filter(:foo=>1).or(:foo=>2).unbind}.should raise_error(Sequel::UnbindDuplicate)
|
|
3816
|
+
end
|
|
3817
|
+
|
|
3818
|
+
specify "should handle case where the same variable has the same value in multiple places " do
|
|
3819
|
+
@u[@ds.filter(:foo=>1).or(:foo=>1)].should == ["SELECT * FROM t WHERE ((foo = $foo) OR (foo = $foo))", {:foo=>1}]
|
|
3820
|
+
end
|
|
3821
|
+
|
|
3822
|
+
specify "should raise Error for unhandled objects inside Identifiers and QualifiedIndentifiers" do
|
|
3823
|
+
proc{@ds.filter(Sequel::SQL::Identifier.new([]) > 1).unbind}.should raise_error(Sequel::Error)
|
|
3824
|
+
proc{@ds.filter{foo.qualify({}) > 1}.unbind}.should raise_error(Sequel::Error)
|
|
3825
|
+
end
|
|
3826
|
+
end
|
|
3827
|
+
|
|
3602
3828
|
describe "Sequel::Dataset #with and #with_recursive" do
|
|
3603
3829
|
before do
|
|
3604
3830
|
@db = MockDatabase.new
|
|
@@ -3633,6 +3859,15 @@ describe "Sequel::Dataset #with and #with_recursive" do
|
|
|
3633
3859
|
proc{@ds.with(:t, @db[:x], :args=>[:b])}.should raise_error(Sequel::Error)
|
|
3634
3860
|
proc{@ds.with_recursive(:t, @db[:x], @db[:t], :args=>[:b, :c])}.should raise_error(Sequel::Error)
|
|
3635
3861
|
end
|
|
3862
|
+
|
|
3863
|
+
specify "#with should work on insert, update, and delete statements if they support it" do
|
|
3864
|
+
[:insert, :update, :delete].each do |m|
|
|
3865
|
+
@ds.meta_def(:"#{m}_clause_methods"){super() + [:"#{m}_with_sql"]}
|
|
3866
|
+
end
|
|
3867
|
+
@ds.with(:t, @db[:x]).insert_sql(1).should == 'WITH t AS (SELECT * FROM x) INSERT INTO t VALUES (1)'
|
|
3868
|
+
@ds.with(:t, @db[:x]).update_sql(:foo=>1).should == 'WITH t AS (SELECT * FROM x) UPDATE t SET foo = 1'
|
|
3869
|
+
@ds.with(:t, @db[:x]).delete_sql.should == 'WITH t AS (SELECT * FROM x) DELETE FROM t'
|
|
3870
|
+
end
|
|
3636
3871
|
end
|
|
3637
3872
|
|
|
3638
3873
|
describe Sequel::SQL::Constants do
|
|
@@ -3860,6 +4095,14 @@ describe "Sequel::Dataset#select_map" do
|
|
|
3860
4095
|
@ds.select_map{a(t__c)}.should == [1, 2]
|
|
3861
4096
|
@ds.db.sqls.should == ['SELECT a(t.c) FROM t']
|
|
3862
4097
|
end
|
|
4098
|
+
|
|
4099
|
+
specify "should handle an array of columns" do
|
|
4100
|
+
@ds.select_map([:c, :c]).should == [[1, 1], [2, 2]]
|
|
4101
|
+
@ds.db.sqls.should == ['SELECT c, c FROM t']
|
|
4102
|
+
@ds.db.reset
|
|
4103
|
+
@ds.select_order_map([:d.as(:c), :c.qualify(:b), :c.identifier, :c.identifier.qualify(:b), :a__c, :a__d___c]).should == [[1, 1, 1, 1, 1, 1], [2, 2, 2, 2, 2, 2]]
|
|
4104
|
+
@ds.db.sqls.should == ['SELECT d AS c, b.c, c, b.c, a.c, a.d AS c FROM t ORDER BY d, b.c, c, b.c, a.c, a.d']
|
|
4105
|
+
end
|
|
3863
4106
|
end
|
|
3864
4107
|
|
|
3865
4108
|
describe "Sequel::Dataset#select_order_map" do
|
|
@@ -3898,10 +4141,23 @@ describe "Sequel::Dataset#select_order_map" do
|
|
|
3898
4141
|
@ds.db.sqls.should == ['SELECT a AS b FROM t ORDER BY a']
|
|
3899
4142
|
end
|
|
3900
4143
|
|
|
4144
|
+
specify "should handle OrderedExpressions" do
|
|
4145
|
+
@ds.select_order_map(:a.desc).should == [1, 2]
|
|
4146
|
+
@ds.db.sqls.should == ['SELECT a FROM t ORDER BY a DESC']
|
|
4147
|
+
end
|
|
4148
|
+
|
|
3901
4149
|
specify "should accept a block" do
|
|
3902
4150
|
@ds.select_order_map{a(t__c)}.should == [1, 2]
|
|
3903
4151
|
@ds.db.sqls.should == ['SELECT a(t.c) FROM t ORDER BY a(t.c)']
|
|
3904
4152
|
end
|
|
4153
|
+
|
|
4154
|
+
specify "should handle an array of columns" do
|
|
4155
|
+
@ds.select_order_map([:c, :c]).should == [[1, 1], [2, 2]]
|
|
4156
|
+
@ds.db.sqls.should == ['SELECT c, c FROM t ORDER BY c, c']
|
|
4157
|
+
@ds.db.reset
|
|
4158
|
+
@ds.select_order_map([:d.as(:c), :c.qualify(:b), :c.identifier, :c.identifier.qualify(:b), :c.identifier.qualify(:b).desc, :a__c, :a__d___c.desc]).should == [[1, 1, 1, 1, 1, 1, 1], [2, 2, 2, 2, 2, 2, 2]]
|
|
4159
|
+
@ds.db.sqls.should == ['SELECT d AS c, b.c, c, b.c, b.c, a.c, a.d AS c FROM t ORDER BY d, b.c, c, b.c, b.c DESC, a.c, a.d DESC']
|
|
4160
|
+
end
|
|
3905
4161
|
end
|
|
3906
4162
|
|
|
3907
4163
|
describe "Sequel::Dataset#select_hash" do
|
|
@@ -3917,7 +4173,7 @@ describe "Sequel::Dataset#select_hash" do
|
|
|
3917
4173
|
@ds.db.reset
|
|
3918
4174
|
end
|
|
3919
4175
|
|
|
3920
|
-
specify "should do select and
|
|
4176
|
+
specify "should do select and to_hash in one step" do
|
|
3921
4177
|
@ds.set_fr_yield([{:a=>1, :b=>2}, {:a=>3, :b=>4}])
|
|
3922
4178
|
@ds.select_hash(:a, :b).should == {1=>2, 3=>4}
|
|
3923
4179
|
@ds.db.sqls.should == ['SELECT a, b FROM t']
|
|
@@ -3940,6 +4196,36 @@ describe "Sequel::Dataset#select_hash" do
|
|
|
3940
4196
|
@ds.select_hash(:t__c___a, :t__d___b).should == {1=>2, 3=>4}
|
|
3941
4197
|
@ds.db.sqls.should == ['SELECT t.c AS a, t.d AS b FROM t']
|
|
3942
4198
|
end
|
|
4199
|
+
|
|
4200
|
+
specify "should handle SQL::Identifiers in arguments" do
|
|
4201
|
+
@ds.set_fr_yield([{:a=>1, :b=>2}, {:a=>3, :b=>4}])
|
|
4202
|
+
@ds.select_hash(:a.identifier, :b.identifier).should == {1=>2, 3=>4}
|
|
4203
|
+
@ds.db.sqls.should == ['SELECT a, b FROM t']
|
|
4204
|
+
end
|
|
4205
|
+
|
|
4206
|
+
specify "should handle SQL::QualifiedIdentifiers in arguments" do
|
|
4207
|
+
@ds.set_fr_yield([{:a=>1, :b=>2}, {:a=>3, :b=>4}])
|
|
4208
|
+
@ds.select_hash(:a.qualify(:t), :b.identifier.qualify(:t)).should == {1=>2, 3=>4}
|
|
4209
|
+
@ds.db.sqls.should == ['SELECT t.a, t.b FROM t']
|
|
4210
|
+
end
|
|
4211
|
+
|
|
4212
|
+
specify "should handle SQL::AliasedExpressions in arguments" do
|
|
4213
|
+
@ds.set_fr_yield([{:a=>1, :b=>2}, {:a=>3, :b=>4}])
|
|
4214
|
+
@ds.select_hash(:c.as(:a), :t.as(:b)).should == {1=>2, 3=>4}
|
|
4215
|
+
@ds.db.sqls.should == ['SELECT c AS a, t AS b FROM t']
|
|
4216
|
+
end
|
|
4217
|
+
|
|
4218
|
+
specify "should work with arrays of columns" do
|
|
4219
|
+
@ds.set_fr_yield([{:a=>1, :b=>2, :c=>3}, {:a=>4, :b=>5, :c=>6}])
|
|
4220
|
+
@ds.select_hash([:a, :c], :b).should == {[1, 3]=>2, [4, 6]=>5}
|
|
4221
|
+
@ds.db.sqls.should == ['SELECT a, c, b FROM t']
|
|
4222
|
+
@ds.select_hash(:a, [:b, :c]).should == {1=>[2, 3], 4=>[5, 6]}
|
|
4223
|
+
@ds.select_hash([:a, :b], [:b, :c]).should == {[1, 2]=>[2, 3], [4, 5]=>[5, 6]}
|
|
4224
|
+
end
|
|
4225
|
+
|
|
4226
|
+
specify "should raise an error if the resulting symbol cannot be determined" do
|
|
4227
|
+
proc{@ds.select_hash(:c.as(:a), 'foo')}.should raise_error(Sequel::Error)
|
|
4228
|
+
end
|
|
3943
4229
|
end
|
|
3944
4230
|
|
|
3945
4231
|
describe "Modifying joined datasets" do
|
|
@@ -3977,3 +4263,66 @@ describe "Dataset#lock_style and for_update" do
|
|
|
3977
4263
|
@ds.lock_style("FOR SHARE").sql.should == "SELECT * FROM t FOR SHARE"
|
|
3978
4264
|
end
|
|
3979
4265
|
end
|
|
4266
|
+
|
|
4267
|
+
describe "Custom ASTTransformer" do
|
|
4268
|
+
specify "should transform given objects" do
|
|
4269
|
+
c = Class.new(Sequel::ASTTransformer) do
|
|
4270
|
+
def v(s)
|
|
4271
|
+
(s.is_a?(Symbol) || s.is_a?(String)) ? :"#{s}#{s}" : super
|
|
4272
|
+
end
|
|
4273
|
+
end.new
|
|
4274
|
+
ds = MockDatabase.new[:t].cross_join(:a___g).join(:b___h, [:c]).join(:d___i, :e=>:f)
|
|
4275
|
+
ds.sql.should == 'SELECT * FROM t CROSS JOIN a AS g INNER JOIN b AS h USING (c) INNER JOIN d AS i ON (i.e = h.f)'
|
|
4276
|
+
ds.clone(:from=>c.transform(ds.opts[:from]), :join=>c.transform(ds.opts[:join])).sql.should ==
|
|
4277
|
+
'SELECT * FROM tt CROSS JOIN aa AS gg INNER JOIN bb AS hh USING (cc) INNER JOIN dd AS ii ON (ii.ee = hh.ff)'
|
|
4278
|
+
end
|
|
4279
|
+
end
|
|
4280
|
+
|
|
4281
|
+
describe "Dataset#returning" do
|
|
4282
|
+
before do
|
|
4283
|
+
@ds = Sequel::Database.new[:t].returning(:foo)
|
|
4284
|
+
@pr = proc do
|
|
4285
|
+
[:insert, :update, :delete].each do |m|
|
|
4286
|
+
@ds.meta_def(:"#{m}_clause_methods"){super() + [:"#{m}_returning_sql"]}
|
|
4287
|
+
end
|
|
4288
|
+
end
|
|
4289
|
+
end
|
|
4290
|
+
|
|
4291
|
+
specify "should use RETURNING clause in the SQL if the dataset supports it" do
|
|
4292
|
+
@pr.call
|
|
4293
|
+
@ds.delete_sql.should == "DELETE FROM t RETURNING foo"
|
|
4294
|
+
@ds.insert_sql(1).should == "INSERT INTO t VALUES (1) RETURNING foo"
|
|
4295
|
+
@ds.update_sql(:foo=>1).should == "UPDATE t SET foo = 1 RETURNING foo"
|
|
4296
|
+
end
|
|
4297
|
+
|
|
4298
|
+
specify "should not use RETURNING clause in the SQL if the dataset does not support it" do
|
|
4299
|
+
@ds.delete_sql.should == "DELETE FROM t"
|
|
4300
|
+
@ds.insert_sql(1).should == "INSERT INTO t VALUES (1)"
|
|
4301
|
+
@ds.update_sql(:foo=>1).should == "UPDATE t SET foo = 1"
|
|
4302
|
+
end
|
|
4303
|
+
|
|
4304
|
+
specify "should have insert, update, and delete yield to blocks if RETURNING is used" do
|
|
4305
|
+
@pr.call
|
|
4306
|
+
def @ds.fetch_rows(sql)
|
|
4307
|
+
yield(:foo=>sql)
|
|
4308
|
+
end
|
|
4309
|
+
h = {}
|
|
4310
|
+
@ds.delete{|r| h = r}
|
|
4311
|
+
h.should == {:foo=>"DELETE FROM t RETURNING foo"}
|
|
4312
|
+
@ds.insert(1){|r| h = r}
|
|
4313
|
+
h.should == {:foo=>"INSERT INTO t VALUES (1) RETURNING foo"}
|
|
4314
|
+
@ds.update(:foo=>1){|r| h = r}
|
|
4315
|
+
h.should == {:foo=>"UPDATE t SET foo = 1 RETURNING foo"}
|
|
4316
|
+
end
|
|
4317
|
+
|
|
4318
|
+
specify "should have insert, update, and delete return arrays of hashes if RETURNING is used and a block is not given" do
|
|
4319
|
+
@pr.call
|
|
4320
|
+
def @ds.fetch_rows(sql)
|
|
4321
|
+
yield(:foo=>sql)
|
|
4322
|
+
end
|
|
4323
|
+
h = {}
|
|
4324
|
+
@ds.delete.should == [{:foo=>"DELETE FROM t RETURNING foo"}]
|
|
4325
|
+
@ds.insert(1).should == [{:foo=>"INSERT INTO t VALUES (1) RETURNING foo"}]
|
|
4326
|
+
@ds.update(:foo=>1).should == [{:foo=>"UPDATE t SET foo = 1 RETURNING foo"}]
|
|
4327
|
+
end
|
|
4328
|
+
end
|