sequel 3.46.0 → 3.47.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +96 -0
- data/Rakefile +7 -1
- data/bin/sequel +6 -4
- data/doc/active_record.rdoc +1 -1
- data/doc/advanced_associations.rdoc +14 -35
- data/doc/association_basics.rdoc +66 -4
- data/doc/migration.rdoc +4 -0
- data/doc/opening_databases.rdoc +6 -0
- data/doc/postgresql.rdoc +302 -0
- data/doc/release_notes/3.47.0.txt +270 -0
- data/doc/security.rdoc +6 -0
- data/lib/sequel/adapters/ibmdb.rb +9 -9
- data/lib/sequel/adapters/jdbc.rb +22 -7
- data/lib/sequel/adapters/jdbc/postgresql.rb +7 -2
- data/lib/sequel/adapters/mock.rb +2 -0
- data/lib/sequel/adapters/postgres.rb +44 -13
- data/lib/sequel/adapters/shared/mssql.rb +1 -1
- data/lib/sequel/adapters/shared/mysql.rb +2 -2
- data/lib/sequel/adapters/shared/postgres.rb +94 -55
- data/lib/sequel/adapters/shared/sqlite.rb +3 -1
- data/lib/sequel/adapters/sqlite.rb +2 -2
- data/lib/sequel/adapters/utils/pg_types.rb +1 -14
- data/lib/sequel/adapters/utils/split_alter_table.rb +3 -3
- data/lib/sequel/connection_pool/threaded.rb +1 -1
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database/connecting.rb +2 -2
- data/lib/sequel/database/features.rb +5 -0
- data/lib/sequel/database/misc.rb +47 -5
- data/lib/sequel/database/query.rb +2 -2
- data/lib/sequel/dataset/actions.rb +4 -2
- data/lib/sequel/dataset/misc.rb +1 -1
- data/lib/sequel/dataset/prepared_statements.rb +1 -1
- data/lib/sequel/dataset/query.rb +8 -6
- data/lib/sequel/dataset/sql.rb +8 -6
- data/lib/sequel/extensions/constraint_validations.rb +5 -2
- data/lib/sequel/extensions/migration.rb +10 -8
- data/lib/sequel/extensions/pagination.rb +3 -0
- data/lib/sequel/extensions/pg_array.rb +85 -25
- data/lib/sequel/extensions/pg_hstore.rb +8 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +4 -1
- data/lib/sequel/extensions/pg_inet.rb +16 -13
- data/lib/sequel/extensions/pg_interval.rb +6 -2
- data/lib/sequel/extensions/pg_json.rb +18 -11
- data/lib/sequel/extensions/pg_range.rb +17 -2
- data/lib/sequel/extensions/pg_range_ops.rb +7 -5
- data/lib/sequel/extensions/pg_row.rb +29 -12
- data/lib/sequel/extensions/pretty_table.rb +3 -0
- data/lib/sequel/extensions/query.rb +3 -0
- data/lib/sequel/extensions/schema_caching.rb +2 -0
- data/lib/sequel/extensions/schema_dumper.rb +3 -1
- data/lib/sequel/extensions/select_remove.rb +3 -0
- data/lib/sequel/model.rb +8 -2
- data/lib/sequel/model/associations.rb +39 -27
- data/lib/sequel/model/base.rb +99 -38
- data/lib/sequel/model/plugins.rb +25 -0
- data/lib/sequel/plugins/association_autoreloading.rb +27 -22
- data/lib/sequel/plugins/association_dependencies.rb +1 -7
- data/lib/sequel/plugins/auto_validations.rb +110 -0
- data/lib/sequel/plugins/boolean_readers.rb +1 -6
- data/lib/sequel/plugins/caching.rb +6 -13
- data/lib/sequel/plugins/class_table_inheritance.rb +1 -0
- data/lib/sequel/plugins/composition.rb +14 -7
- data/lib/sequel/plugins/constraint_validations.rb +2 -13
- data/lib/sequel/plugins/defaults_setter.rb +1 -6
- data/lib/sequel/plugins/dirty.rb +8 -0
- data/lib/sequel/plugins/error_splitter.rb +54 -0
- data/lib/sequel/plugins/force_encoding.rb +1 -5
- data/lib/sequel/plugins/hook_class_methods.rb +1 -6
- data/lib/sequel/plugins/input_transformer.rb +79 -0
- data/lib/sequel/plugins/instance_filters.rb +7 -1
- data/lib/sequel/plugins/instance_hooks.rb +7 -1
- data/lib/sequel/plugins/json_serializer.rb +5 -10
- data/lib/sequel/plugins/lazy_attributes.rb +20 -7
- data/lib/sequel/plugins/list.rb +1 -6
- data/lib/sequel/plugins/many_through_many.rb +1 -2
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +23 -39
- data/lib/sequel/plugins/optimistic_locking.rb +1 -5
- data/lib/sequel/plugins/pg_row.rb +4 -2
- data/lib/sequel/plugins/pg_typecast_on_load.rb +3 -7
- data/lib/sequel/plugins/prepared_statements.rb +1 -5
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -11
- data/lib/sequel/plugins/rcte_tree.rb +2 -2
- data/lib/sequel/plugins/serialization.rb +11 -13
- data/lib/sequel/plugins/serialization_modification_detection.rb +13 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/static_cache.rb +67 -19
- data/lib/sequel/plugins/string_stripper.rb +7 -27
- data/lib/sequel/plugins/subclasses.rb +3 -5
- data/lib/sequel/plugins/tactical_eager_loading.rb +2 -2
- data/lib/sequel/plugins/timestamps.rb +2 -7
- data/lib/sequel/plugins/touch.rb +5 -8
- data/lib/sequel/plugins/tree.rb +1 -6
- data/lib/sequel/plugins/typecast_on_load.rb +1 -5
- data/lib/sequel/plugins/update_primary_key.rb +26 -14
- data/lib/sequel/plugins/validation_class_methods.rb +31 -16
- data/lib/sequel/plugins/validation_helpers.rb +50 -26
- data/lib/sequel/plugins/xml_serializer.rb +3 -6
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +131 -15
- data/spec/adapters/sqlite_spec.rb +1 -1
- data/spec/core/connection_pool_spec.rb +16 -17
- data/spec/core/database_spec.rb +111 -40
- data/spec/core/dataset_spec.rb +65 -74
- data/spec/core/expression_filters_spec.rb +6 -5
- data/spec/core/object_graph_spec.rb +0 -1
- data/spec/core/schema_spec.rb +23 -23
- data/spec/core/spec_helper.rb +5 -1
- data/spec/extensions/association_dependencies_spec.rb +1 -1
- data/spec/extensions/association_proxies_spec.rb +1 -1
- data/spec/extensions/auto_validations_spec.rb +90 -0
- data/spec/extensions/caching_spec.rb +6 -0
- data/spec/extensions/class_table_inheritance_spec.rb +8 -1
- data/spec/extensions/composition_spec.rb +12 -5
- data/spec/extensions/constraint_validations_spec.rb +4 -4
- data/spec/extensions/core_refinements_spec.rb +29 -79
- data/spec/extensions/dirty_spec.rb +14 -0
- data/spec/extensions/error_splitter_spec.rb +18 -0
- data/spec/extensions/identity_map_spec.rb +0 -1
- data/spec/extensions/input_transformer_spec.rb +54 -0
- data/spec/extensions/instance_filters_spec.rb +6 -0
- data/spec/extensions/instance_hooks_spec.rb +12 -1
- data/spec/extensions/json_serializer_spec.rb +0 -1
- data/spec/extensions/lazy_attributes_spec.rb +64 -55
- data/spec/extensions/looser_typecasting_spec.rb +1 -1
- data/spec/extensions/many_through_many_spec.rb +3 -4
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +53 -15
- data/spec/extensions/migration_spec.rb +16 -0
- data/spec/extensions/null_dataset_spec.rb +1 -1
- data/spec/extensions/pg_array_spec.rb +48 -1
- data/spec/extensions/pg_hstore_ops_spec.rb +10 -2
- data/spec/extensions/pg_hstore_spec.rb +5 -0
- data/spec/extensions/pg_inet_spec.rb +5 -0
- data/spec/extensions/pg_interval_spec.rb +7 -3
- data/spec/extensions/pg_json_spec.rb +6 -1
- data/spec/extensions/pg_range_ops_spec.rb +4 -1
- data/spec/extensions/pg_range_spec.rb +5 -0
- data/spec/extensions/pg_row_plugin_spec.rb +13 -0
- data/spec/extensions/pg_row_spec.rb +28 -19
- data/spec/extensions/pg_typecast_on_load_spec.rb +6 -1
- data/spec/extensions/prepared_statements_associations_spec.rb +1 -1
- data/spec/extensions/query_literals_spec.rb +1 -1
- data/spec/extensions/rcte_tree_spec.rb +2 -2
- data/spec/extensions/schema_spec.rb +2 -2
- data/spec/extensions/serialization_modification_detection_spec.rb +8 -0
- data/spec/extensions/serialization_spec.rb +15 -1
- data/spec/extensions/sharding_spec.rb +1 -1
- data/spec/extensions/single_table_inheritance_spec.rb +1 -1
- data/spec/extensions/static_cache_spec.rb +59 -9
- data/spec/extensions/tactical_eager_loading_spec.rb +19 -4
- data/spec/extensions/update_primary_key_spec.rb +17 -1
- data/spec/extensions/validation_class_methods_spec.rb +25 -0
- data/spec/extensions/validation_helpers_spec.rb +59 -3
- data/spec/integration/associations_test.rb +5 -5
- data/spec/integration/eager_loader_test.rb +32 -63
- data/spec/integration/model_test.rb +2 -2
- data/spec/integration/plugin_test.rb +88 -56
- data/spec/integration/prepared_statement_test.rb +1 -1
- data/spec/integration/schema_test.rb +1 -1
- data/spec/integration/timezone_test.rb +0 -1
- data/spec/integration/transaction_test.rb +0 -1
- data/spec/model/association_reflection_spec.rb +1 -1
- data/spec/model/associations_spec.rb +106 -84
- data/spec/model/base_spec.rb +4 -4
- data/spec/model/eager_loading_spec.rb +8 -8
- data/spec/model/model_spec.rb +27 -9
- data/spec/model/plugins_spec.rb +71 -0
- data/spec/model/record_spec.rb +99 -13
- metadata +12 -2
data/spec/core/dataset_spec.rb
CHANGED
@@ -122,7 +122,7 @@ describe "Dataset#clone" do
|
|
122
122
|
clone.row_proc.should == @dataset.row_proc
|
123
123
|
end
|
124
124
|
|
125
|
-
specify "should
|
125
|
+
specify "should copy the dataset opts" do
|
126
126
|
clone = @dataset.clone
|
127
127
|
|
128
128
|
clone.opts.should_not equal(@dataset.opts)
|
@@ -223,7 +223,7 @@ describe "A simple dataset" do
|
|
223
223
|
end
|
224
224
|
|
225
225
|
specify "should format a truncate statement with multiple tables if supported" do
|
226
|
-
@dataset
|
226
|
+
meta_def(@dataset, :check_truncation_allowed!){}
|
227
227
|
@dataset.from(:test, :test2).truncate_sql.should == 'TRUNCATE TABLE test, test2'
|
228
228
|
end
|
229
229
|
|
@@ -232,8 +232,8 @@ describe "A simple dataset" do
|
|
232
232
|
end
|
233
233
|
|
234
234
|
specify "should use a single column with a default value when the dataset doesn't support using insert statement with default values" do
|
235
|
-
@dataset
|
236
|
-
@dataset
|
235
|
+
meta_def(@dataset, :insert_supports_empty_values?){false}
|
236
|
+
meta_def(@dataset, :columns){[:a, :b]}
|
237
237
|
@dataset.insert_sql.should == 'INSERT INTO test (b) VALUES (DEFAULT)'
|
238
238
|
end
|
239
239
|
|
@@ -425,36 +425,18 @@ describe "Dataset#where" do
|
|
425
425
|
@d3.select_sql.should == "SELECT * FROM test WHERE (a = 1)"
|
426
426
|
@d3.delete_sql.should == "DELETE FROM test WHERE (a = 1)"
|
427
427
|
@d3.update_sql(:GDP => 0).should == "UPDATE test SET GDP = 0 WHERE (a = 1)"
|
428
|
-
|
429
428
|
end
|
430
429
|
|
431
430
|
specify "should be composable using AND operator (for scoping)" do
|
432
|
-
# hashes are merged, no problem
|
433
431
|
@d1.where(:size => 'big').select_sql.should == "SELECT * FROM test WHERE ((region = 'Asia') AND (size = 'big'))"
|
434
|
-
|
435
|
-
# hash and string
|
436
432
|
@d1.where('population > 1000').select_sql.should == "SELECT * FROM test WHERE ((region = 'Asia') AND (population > 1000))"
|
437
433
|
@d1.where('(a > 1) OR (b < 2)').select_sql.should == "SELECT * FROM test WHERE ((region = 'Asia') AND ((a > 1) OR (b < 2)))"
|
438
|
-
|
439
|
-
# hash and array
|
440
434
|
@d1.where('GDP > ?', 1000).select_sql.should == "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > 1000))"
|
441
|
-
|
442
|
-
# array and array
|
443
435
|
@d2.where('GDP > ?', 1000).select_sql.should == "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > 1000))"
|
444
|
-
|
445
|
-
# array and hash
|
446
436
|
@d2.where(:name => ['Japan', 'China']).select_sql.should == "SELECT * FROM test WHERE ((region = 'Asia') AND (name IN ('Japan', 'China')))"
|
447
|
-
|
448
|
-
# array and string
|
449
437
|
@d2.where('GDP > ?').select_sql.should == "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > ?))"
|
450
|
-
|
451
|
-
# string and string
|
452
438
|
@d3.where('b = 2').select_sql.should == "SELECT * FROM test WHERE ((a = 1) AND (b = 2))"
|
453
|
-
|
454
|
-
# string and hash
|
455
439
|
@d3.where(:c => 3).select_sql.should == "SELECT * FROM test WHERE ((a = 1) AND (c = 3))"
|
456
|
-
|
457
|
-
# string and array
|
458
440
|
@d3.where('d = ?', 4).select_sql.should == "SELECT * FROM test WHERE ((a = 1) AND (d = 4))"
|
459
441
|
end
|
460
442
|
|
@@ -485,7 +467,7 @@ describe "Dataset#where" do
|
|
485
467
|
@dataset.exclude([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE ((id1 = id1) AND (id2 = id2))"
|
486
468
|
end
|
487
469
|
|
488
|
-
specify "should handle all types of IN/NOT IN queries with empty arrays" do
|
470
|
+
specify "should handle all types of IN/NOT IN queries with empty arrays when empty_array_handle_nulls is false" do
|
489
471
|
begin
|
490
472
|
Sequel.empty_array_handle_nulls = false
|
491
473
|
@dataset.filter(:id => []).sql.should == "SELECT * FROM test WHERE (1 = 0)"
|
@@ -512,7 +494,7 @@ describe "Dataset#where" do
|
|
512
494
|
end
|
513
495
|
|
514
496
|
specify "should handle IN/NOT IN queries with multiple columns and an array where the database doesn't support it" do
|
515
|
-
@dataset
|
497
|
+
meta_def(@dataset, :supports_multiple_column_in?){false}
|
516
498
|
@dataset.filter([:id1, :id2] => [[1, 2], [3,4]]).sql.should == "SELECT * FROM test WHERE (((id1 = 1) AND (id2 = 2)) OR ((id1 = 3) AND (id2 = 4)))"
|
517
499
|
@dataset.exclude([:id1, :id2] => [[1, 2], [3,4]]).sql.should == "SELECT * FROM test WHERE (((id1 != 1) OR (id2 != 2)) AND ((id1 != 3) OR (id2 != 4)))"
|
518
500
|
@dataset.filter([:id1, :id2] => Sequel.value_list([[1, 2], [3,4]])).sql.should == "SELECT * FROM test WHERE (((id1 = 1) AND (id2 = 2)) OR ((id1 = 3) AND (id2 = 4)))"
|
@@ -520,7 +502,7 @@ describe "Dataset#where" do
|
|
520
502
|
end
|
521
503
|
|
522
504
|
specify "should handle IN/NOT IN queries with multiple columns and a dataset where the database doesn't support it" do
|
523
|
-
@dataset
|
505
|
+
meta_def(@dataset, :supports_multiple_column_in?){false}
|
524
506
|
db = Sequel.mock(:fetch=>[{:id1=>1, :id2=>2}, {:id1=>3, :id2=>4}])
|
525
507
|
d1 = db[:test].select(:id1, :id2).filter(:region=>'Asia').columns(:id1, :id2)
|
526
508
|
@dataset.filter([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (((id1 = 1) AND (id2 = 2)) OR ((id1 = 3) AND (id2 = 4)))"
|
@@ -530,7 +512,7 @@ describe "Dataset#where" do
|
|
530
512
|
end
|
531
513
|
|
532
514
|
specify "should handle IN/NOT IN queries with multiple columns and an empty dataset where the database doesn't support it" do
|
533
|
-
@dataset
|
515
|
+
meta_def(@dataset, :supports_multiple_column_in?){false}
|
534
516
|
db = Sequel.mock
|
535
517
|
d1 = db[:test].select(:id1, :id2).filter(:region=>'Asia').columns(:id1, :id2)
|
536
518
|
@dataset.filter([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE ((id1 != id1) AND (id2 != id2))"
|
@@ -539,10 +521,10 @@ describe "Dataset#where" do
|
|
539
521
|
db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
|
540
522
|
end
|
541
523
|
|
542
|
-
specify "should handle IN/NOT IN queries with multiple columns and an empty dataset where the database doesn't support it
|
524
|
+
specify "should handle IN/NOT IN queries with multiple columns and an empty dataset where the database doesn't support it when empty_array_handle nulls is true" do
|
543
525
|
begin
|
544
526
|
Sequel.empty_array_handle_nulls = false
|
545
|
-
@dataset
|
527
|
+
meta_def(@dataset, :supports_multiple_column_in?){false}
|
546
528
|
db = Sequel.mock
|
547
529
|
d1 = db[:test].select(:id1, :id2).filter(:region=>'Asia').columns(:id1, :id2)
|
548
530
|
@dataset.filter([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (1 = 0)"
|
@@ -555,7 +537,7 @@ describe "Dataset#where" do
|
|
555
537
|
end
|
556
538
|
|
557
539
|
specify "should handle IN/NOT IN queries for datasets with row_procs" do
|
558
|
-
@dataset
|
540
|
+
meta_def(@dataset, :supports_multiple_column_in?){false}
|
559
541
|
db = Sequel.mock(:fetch=>[{:id1=>1, :id2=>2}, {:id1=>3, :id2=>4}])
|
560
542
|
d1 = db[:test].select(:id1, :id2).filter(:region=>'Asia').columns(:id1, :id2)
|
561
543
|
d1.row_proc = proc{|h| Object.new}
|
@@ -878,19 +860,19 @@ describe "Dataset#group_by" do
|
|
878
860
|
end
|
879
861
|
|
880
862
|
specify "should support a #group_rollup method if the database supports it" do
|
881
|
-
@dataset
|
863
|
+
meta_def(@dataset, :supports_group_rollup?){true}
|
882
864
|
@dataset.group(:type_id).group_rollup.select_sql.should == "SELECT * FROM test GROUP BY ROLLUP(type_id)"
|
883
865
|
@dataset.group(:type_id, :b).group_rollup.select_sql.should == "SELECT * FROM test GROUP BY ROLLUP(type_id, b)"
|
884
|
-
@dataset
|
866
|
+
meta_def(@dataset, :uses_with_rollup?){true}
|
885
867
|
@dataset.group(:type_id).group_rollup.select_sql.should == "SELECT * FROM test GROUP BY type_id WITH ROLLUP"
|
886
868
|
@dataset.group(:type_id, :b).group_rollup.select_sql.should == "SELECT * FROM test GROUP BY type_id, b WITH ROLLUP"
|
887
869
|
end
|
888
870
|
|
889
871
|
specify "should support a #group_cube method if the database supports it" do
|
890
|
-
@dataset
|
872
|
+
meta_def(@dataset, :supports_group_cube?){true}
|
891
873
|
@dataset.group(:type_id).group_cube.select_sql.should == "SELECT * FROM test GROUP BY CUBE(type_id)"
|
892
874
|
@dataset.group(:type_id, :b).group_cube.select_sql.should == "SELECT * FROM test GROUP BY CUBE(type_id, b)"
|
893
|
-
@dataset
|
875
|
+
meta_def(@dataset, :uses_with_rollup?){true}
|
894
876
|
@dataset.group(:type_id).group_cube.select_sql.should == "SELECT * FROM test GROUP BY type_id WITH CUBE"
|
895
877
|
@dataset.group(:type_id, :b).group_cube.select_sql.should == "SELECT * FROM test GROUP BY type_id, b WITH CUBE"
|
896
878
|
end
|
@@ -1011,10 +993,6 @@ describe "Dataset#literal" do
|
|
1011
993
|
@dataset.literal(@a.new).should == "called ds"
|
1012
994
|
end
|
1013
995
|
|
1014
|
-
specify "should raise an error for unsupported types with no sql_literal method" do
|
1015
|
-
proc {@dataset.literal(Object.new)}.should raise_error
|
1016
|
-
end
|
1017
|
-
|
1018
996
|
specify "should literalize datasets as subqueries" do
|
1019
997
|
d = @dataset.from(:test)
|
1020
998
|
d.literal(d).should == "(#{d.sql})"
|
@@ -1052,7 +1030,7 @@ describe "Dataset#literal" do
|
|
1052
1030
|
end
|
1053
1031
|
|
1054
1032
|
specify "should literalize Time, DateTime, Date properly if SQL standard format is required" do
|
1055
|
-
@dataset
|
1033
|
+
meta_def(@dataset, :requires_sql_standard_datetimes?){true}
|
1056
1034
|
|
1057
1035
|
t = Time.now
|
1058
1036
|
s = t.strftime("TIMESTAMP '%Y-%m-%d %H:%M:%S")
|
@@ -1068,7 +1046,7 @@ describe "Dataset#literal" do
|
|
1068
1046
|
end
|
1069
1047
|
|
1070
1048
|
specify "should literalize Time and DateTime properly if the database support timezones in timestamps" do
|
1071
|
-
@dataset
|
1049
|
+
meta_def(@dataset, :supports_timestamp_timezones?){true}
|
1072
1050
|
|
1073
1051
|
t = Time.now.utc
|
1074
1052
|
s = t.strftime("'%Y-%m-%d %H:%M:%S")
|
@@ -1080,7 +1058,7 @@ describe "Dataset#literal" do
|
|
1080
1058
|
end
|
1081
1059
|
|
1082
1060
|
specify "should literalize Time and DateTime properly if the database doesn't support usecs in timestamps" do
|
1083
|
-
@dataset
|
1061
|
+
meta_def(@dataset, :supports_timestamp_usecs?){false}
|
1084
1062
|
|
1085
1063
|
t = Time.now.utc
|
1086
1064
|
s = t.strftime("'%Y-%m-%d %H:%M:%S")
|
@@ -1090,7 +1068,7 @@ describe "Dataset#literal" do
|
|
1090
1068
|
s = t.strftime("'%Y-%m-%d %H:%M:%S")
|
1091
1069
|
@dataset.literal(t).should == "#{s}'"
|
1092
1070
|
|
1093
|
-
@dataset
|
1071
|
+
meta_def(@dataset, :supports_timestamp_timezones?){true}
|
1094
1072
|
|
1095
1073
|
t = Time.now.utc
|
1096
1074
|
s = t.strftime("'%Y-%m-%d %H:%M:%S")
|
@@ -1198,8 +1176,8 @@ describe "Dataset#from" do
|
|
1198
1176
|
end
|
1199
1177
|
|
1200
1178
|
specify "should hoist WITH clauses from subqueries if the dataset doesn't support CTEs in subselects" do
|
1201
|
-
@dataset
|
1202
|
-
@dataset
|
1179
|
+
meta_def(@dataset, :supports_cte?){true}
|
1180
|
+
meta_def(@dataset, :supports_cte_in_subselect?){false}
|
1203
1181
|
@dataset.from(@dataset.from(:a).with(:a, @dataset.from(:b))).sql.should == 'WITH a AS (SELECT * FROM b) SELECT * FROM (SELECT * FROM a) AS t1'
|
1204
1182
|
@dataset.from(@dataset.from(:a).with(:a, @dataset.from(:b)), @dataset.from(:c).with(:c, @dataset.from(:d))).sql.should == 'WITH a AS (SELECT * FROM b), c AS (SELECT * FROM d) SELECT * FROM (SELECT * FROM a) AS t1, (SELECT * FROM c) AS t2'
|
1205
1183
|
end
|
@@ -1382,7 +1360,7 @@ describe "Dataset#select_append" do
|
|
1382
1360
|
end
|
1383
1361
|
|
1384
1362
|
specify "should select from all from and join tables if SELECT *, column not supported" do
|
1385
|
-
@d
|
1363
|
+
meta_def(@d, :supports_select_all_and_column?){false}
|
1386
1364
|
@d.select_append(:b).sql.should == 'SELECT test.*, b FROM test'
|
1387
1365
|
@d.from(:test, :c).select_append(:b).sql.should == 'SELECT test.*, c.*, b FROM test, c'
|
1388
1366
|
@d.cross_join(:c).select_append(:b).sql.should == 'SELECT test.*, c.*, b FROM test CROSS JOIN c'
|
@@ -1777,7 +1755,7 @@ describe "Dataset#to_hash_groups" do
|
|
1777
1755
|
@d = Sequel.mock(:fetch=>[{:a => 1, :b => 2}, {:a => 3, :b => 4}, {:a => 1, :b => 6}, {:a => 7, :b => 4}])[:items]
|
1778
1756
|
end
|
1779
1757
|
|
1780
|
-
specify "should provide a hash with the first column as key and the second as
|
1758
|
+
specify "should provide a hash with the first column as key and the second as arrays of matching values" do
|
1781
1759
|
@d.to_hash_groups(:a, :b).should == {1 => [2, 6], 3 => [4], 7 => [4]}
|
1782
1760
|
@d.to_hash_groups(:b, :a).should == {2 => [1], 4=>[3, 7], 6=>[1]}
|
1783
1761
|
end
|
@@ -1827,7 +1805,7 @@ describe "Dataset#distinct" do
|
|
1827
1805
|
end
|
1828
1806
|
|
1829
1807
|
specify "should use DISTINCT ON if columns are given and DISTINCT ON is supported" do
|
1830
|
-
@dataset
|
1808
|
+
meta_def(@dataset, :supports_distinct_on?){true}
|
1831
1809
|
@dataset.distinct(:a, :b).sql.should == 'SELECT DISTINCT ON (a, b) name FROM test'
|
1832
1810
|
@dataset.distinct(Sequel.cast(:stamp, :integer), :node_id=>nil).sql.should == 'SELECT DISTINCT ON (CAST(stamp AS integer), (node_id IS NULL)) name FROM test'
|
1833
1811
|
end
|
@@ -1952,7 +1930,7 @@ describe "Dataset#group_and_count" do
|
|
1952
1930
|
end
|
1953
1931
|
|
1954
1932
|
describe "Dataset#empty?" do
|
1955
|
-
specify "should return true if records exist in the dataset" do
|
1933
|
+
specify "should return true if no records exist in the dataset" do
|
1956
1934
|
db = Sequel.mock(:fetch=>proc{|sql| {1=>1} unless sql =~ /WHERE 'f'/})
|
1957
1935
|
db.from(:test).should_not be_empty
|
1958
1936
|
db.sqls.should == ['SELECT 1 AS one FROM test LIMIT 1']
|
@@ -1987,7 +1965,7 @@ describe "Dataset#first_source_alias" do
|
|
1987
1965
|
end
|
1988
1966
|
|
1989
1967
|
specify "should raise exception if table doesn't have a source" do
|
1990
|
-
proc{@ds.first_source_alias
|
1968
|
+
proc{@ds.first_source_alias}.should raise_error(Sequel::Error)
|
1991
1969
|
end
|
1992
1970
|
end
|
1993
1971
|
|
@@ -2010,7 +1988,7 @@ describe "Dataset#first_source_table" do
|
|
2010
1988
|
end
|
2011
1989
|
|
2012
1990
|
specify "should raise exception if table doesn't have a source" do
|
2013
|
-
proc{@ds.first_source_table
|
1991
|
+
proc{@ds.first_source_table}.should raise_error(Sequel::Error)
|
2014
1992
|
end
|
2015
1993
|
end
|
2016
1994
|
|
@@ -2042,8 +2020,8 @@ describe "Dataset#from_self" do
|
|
2042
2020
|
|
2043
2021
|
specify "should hoist WITH clauses in current dataset if dataset doesn't support WITH in subselect" do
|
2044
2022
|
ds = Sequel::Dataset.new(nil)
|
2045
|
-
|
2046
|
-
|
2023
|
+
meta_def(ds, :supports_cte?){true}
|
2024
|
+
meta_def(ds, :supports_cte_in_subselect?){false}
|
2047
2025
|
ds.from(:a).with(:a, ds.from(:b)).from_self.sql.should == 'WITH a AS (SELECT * FROM b) SELECT * FROM (SELECT * FROM a) AS t1'
|
2048
2026
|
ds.from(:a, :c).with(:a, ds.from(:b)).with(:c, ds.from(:d)).from_self.sql.should == 'WITH a AS (SELECT * FROM b), c AS (SELECT * FROM d) SELECT * FROM (SELECT * FROM a, c) AS t1'
|
2049
2027
|
end
|
@@ -2225,13 +2203,13 @@ describe "Dataset#join_table" do
|
|
2225
2203
|
end
|
2226
2204
|
|
2227
2205
|
specify "should emulate JOIN USING (poorly) if the dataset doesn't support it" do
|
2228
|
-
@d
|
2206
|
+
meta_def(@d, :supports_join_using?){false}
|
2229
2207
|
@d.join(:categories, [:id]).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."id" = "items"."id")'
|
2230
2208
|
end
|
2231
2209
|
|
2232
2210
|
specify "should hoist WITH clauses from subqueries if the dataset doesn't support CTEs in subselects" do
|
2233
|
-
@d
|
2234
|
-
@d
|
2211
|
+
meta_def(@d, :supports_cte?){true}
|
2212
|
+
meta_def(@d, :supports_cte_in_subselect?){false}
|
2235
2213
|
@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")'
|
2236
2214
|
end
|
2237
2215
|
|
@@ -2540,7 +2518,7 @@ describe "Dataset #first!" do
|
|
2540
2518
|
|
2541
2519
|
specify "should set the limit and return an array of records if the given number is > 1" do
|
2542
2520
|
i = rand(10) + 10
|
2543
|
-
|
2521
|
+
@d.order(:a).first!(i).should == [{:s=>"SELECT * FROM test ORDER BY a LIMIT #{i}"}]
|
2544
2522
|
end
|
2545
2523
|
|
2546
2524
|
specify "should return the first! matching record if a block is given without an argument" do
|
@@ -2553,7 +2531,7 @@ describe "Dataset #first!" do
|
|
2553
2531
|
|
2554
2532
|
specify "should filter and return an array of records if an Integer argument is provided and a block is given" do
|
2555
2533
|
i = rand(10) + 10
|
2556
|
-
|
2534
|
+
@d.order(:a).first!(i){z > 26}.should == [{:s=>"SELECT * FROM test WHERE (z > 26) ORDER BY a LIMIT #{i}"}]
|
2557
2535
|
end
|
2558
2536
|
|
2559
2537
|
specify "should raise NoMatchingRow exception if no rows match" do
|
@@ -2602,7 +2580,7 @@ describe "Dataset compound operations" do
|
|
2602
2580
|
end
|
2603
2581
|
|
2604
2582
|
specify "should raise an InvalidOperation if INTERSECT or EXCEPT is used and they are not supported" do
|
2605
|
-
@a
|
2583
|
+
meta_def(@a, :supports_intersect_except?){false}
|
2606
2584
|
proc{@a.intersect(@b)}.should raise_error(Sequel::InvalidOperation)
|
2607
2585
|
proc{@a.intersect(@b, true)}.should raise_error(Sequel::InvalidOperation)
|
2608
2586
|
proc{@a.except(@b)}.should raise_error(Sequel::InvalidOperation)
|
@@ -2610,7 +2588,7 @@ describe "Dataset compound operations" do
|
|
2610
2588
|
end
|
2611
2589
|
|
2612
2590
|
specify "should raise an InvalidOperation if INTERSECT ALL or EXCEPT ALL is used and they are not supported" do
|
2613
|
-
@a
|
2591
|
+
meta_def(@a, :supports_intersect_except_all?){false}
|
2614
2592
|
proc{@a.intersect(@b)}.should_not raise_error
|
2615
2593
|
proc{@a.intersect(@b, true)}.should raise_error(Sequel::InvalidOperation)
|
2616
2594
|
proc{@a.except(@b)}.should_not raise_error
|
@@ -2645,8 +2623,8 @@ describe "Dataset compound operations" do
|
|
2645
2623
|
|
2646
2624
|
specify "should hoist WITH clauses in given dataset if dataset doesn't support WITH in subselect" do
|
2647
2625
|
ds = Sequel::Dataset.new(nil)
|
2648
|
-
|
2649
|
-
|
2626
|
+
meta_def(ds, :supports_cte?){true}
|
2627
|
+
meta_def(ds, :supports_cte_in_subselect?){false}
|
2650
2628
|
ds.from(:a).union(ds.from(:c).with(:c, ds.from(:d)), :from_self=>false).sql.should == 'WITH c AS (SELECT * FROM d) SELECT * FROM a UNION SELECT * FROM c'
|
2651
2629
|
ds.from(:a).except(ds.from(:c).with(:c, ds.from(:d))).sql.should == 'WITH c AS (SELECT * FROM d) SELECT * FROM (SELECT * FROM a EXCEPT SELECT * FROM c) AS t1'
|
2652
2630
|
ds.from(:a).with(:a, ds.from(:b)).intersect(ds.from(:c).with(:c, ds.from(:d)), :from_self=>false).sql.should == 'WITH a AS (SELECT * FROM b), c AS (SELECT * FROM d) SELECT * FROM a INTERSECT SELECT * FROM c'
|
@@ -2774,6 +2752,13 @@ describe "Dataset#get" do
|
|
2774
2752
|
@d.with_sql('SELECT foo').get{[name, n__abc]}.should == [1, 2]
|
2775
2753
|
@d.db.sqls.should == ['SELECT foo'] * 2
|
2776
2754
|
end
|
2755
|
+
|
2756
|
+
specify "should handle cases where no rows are returned" do
|
2757
|
+
@d._fetch = []
|
2758
|
+
@d.get(:n).should == nil
|
2759
|
+
@d.get([:n, :a]).should == nil
|
2760
|
+
@d.db.sqls.should == ['SELECT n FROM test LIMIT 1', 'SELECT n, a FROM test LIMIT 1']
|
2761
|
+
end
|
2777
2762
|
end
|
2778
2763
|
|
2779
2764
|
describe "Dataset#set_row_proc" do
|
@@ -3239,7 +3224,7 @@ describe "Dataset#grep" do
|
|
3239
3224
|
@ds = Sequel.mock[:posts]
|
3240
3225
|
end
|
3241
3226
|
|
3242
|
-
specify "should format a
|
3227
|
+
specify "should format a filter correctly" do
|
3243
3228
|
@ds.grep(:title, 'ruby').sql.should == "SELECT * FROM posts WHERE ((title LIKE 'ruby' ESCAPE '\\'))"
|
3244
3229
|
end
|
3245
3230
|
|
@@ -3360,7 +3345,7 @@ describe "Dataset prepared statements and bound variables " do
|
|
3360
3345
|
before do
|
3361
3346
|
@db = Sequel.mock
|
3362
3347
|
@ds = @db[:items]
|
3363
|
-
@ds
|
3348
|
+
meta_def(@ds, :insert_sql){|*v| "#{super(*v)}#{' RETURNING *' if opts.has_key?(:returning)}" }
|
3364
3349
|
end
|
3365
3350
|
|
3366
3351
|
specify "#call should take a type and bind hash and interpolate it" do
|
@@ -3441,7 +3426,7 @@ describe "Dataset prepared statements and bound variables " do
|
|
3441
3426
|
|
3442
3427
|
specify "should handle columns on prepared statements correctly" do
|
3443
3428
|
@db.columns = [:num]
|
3444
|
-
@ds
|
3429
|
+
meta_def(@ds, :select_where_sql){|sql| super(sql); sql << " OR #{columns.first} = 1" if opts[:where]}
|
3445
3430
|
@ds.filter(:num=>:$n).prepare(:select, :sn).sql.should == 'SELECT * FROM items WHERE (num = $n) OR num = 1'
|
3446
3431
|
@db.sqls.should == ['SELECT * FROM items LIMIT 1']
|
3447
3432
|
end
|
@@ -3492,7 +3477,10 @@ describe Sequel::Dataset::UnnumberedArgumentMapper do
|
|
3492
3477
|
@ps << @ds.prepare(:delete, :d)
|
3493
3478
|
@ps << @ds.prepare(:insert, :i, :num=>:$n)
|
3494
3479
|
@ps << @ds.prepare(:update, :u, :num=>:$n)
|
3495
|
-
@ps.each
|
3480
|
+
@ps.each do |p|
|
3481
|
+
p.extend(Sequel::Dataset::ArgumentMapper) # Work around for old rbx
|
3482
|
+
p.extend(Sequel::Dataset::UnnumberedArgumentMapper)
|
3483
|
+
end
|
3496
3484
|
end
|
3497
3485
|
|
3498
3486
|
specify "#inspect should show the actual SQL submitted to the database" do
|
@@ -3511,6 +3499,7 @@ describe Sequel::Dataset::UnnumberedArgumentMapper do
|
|
3511
3499
|
|
3512
3500
|
specify "should handle unrecognized statement types as :all" do
|
3513
3501
|
ps = @ds.prepare(:select_all, :s)
|
3502
|
+
ps.extend(Sequel::Dataset::ArgumentMapper) # Work around for old rbx
|
3514
3503
|
ps.extend(Sequel::Dataset::UnnumberedArgumentMapper)
|
3515
3504
|
ps.prepared_sql
|
3516
3505
|
ps.call(:n=>1)
|
@@ -3688,7 +3677,7 @@ describe "Sequel::Dataset#qualify_to_first_source" do
|
|
3688
3677
|
end
|
3689
3678
|
|
3690
3679
|
specify "should handle SQL::WindowFunctions" do
|
3691
|
-
@ds
|
3680
|
+
meta_def(@ds, :supports_window_functions?){true}
|
3692
3681
|
@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'
|
3693
3682
|
end
|
3694
3683
|
|
@@ -3807,14 +3796,14 @@ describe "Sequel::Dataset #with and #with_recursive" do
|
|
3807
3796
|
end
|
3808
3797
|
|
3809
3798
|
specify "#with and #with_recursive should raise an error unless the dataset supports CTEs" do
|
3810
|
-
@ds
|
3799
|
+
meta_def(@ds, :supports_cte?){false}
|
3811
3800
|
proc{@ds.with(:t, @db[:x], :args=>[:b])}.should raise_error(Sequel::Error)
|
3812
3801
|
proc{@ds.with_recursive(:t, @db[:x], @db[:t], :args=>[:b, :c])}.should raise_error(Sequel::Error)
|
3813
3802
|
end
|
3814
3803
|
|
3815
3804
|
specify "#with should work on insert, update, and delete statements if they support it" do
|
3816
3805
|
[:insert, :update, :delete].each do |m|
|
3817
|
-
@ds
|
3806
|
+
meta_def(@ds, :"#{m}_clause_methods"){[:"#{m}_with_sql"] + super()}
|
3818
3807
|
end
|
3819
3808
|
@ds.with(:t, @db[:x]).insert_sql(1).should == 'WITH t AS (SELECT * FROM x) INSERT INTO t VALUES (1)'
|
3820
3809
|
@ds.with(:t, @db[:x]).update_sql(:foo=>1).should == 'WITH t AS (SELECT * FROM x) UPDATE t SET foo = 1'
|
@@ -3822,8 +3811,8 @@ describe "Sequel::Dataset #with and #with_recursive" do
|
|
3822
3811
|
end
|
3823
3812
|
|
3824
3813
|
specify "should hoist WITH clauses in given dataset(s) if dataset doesn't support WITH in subselect" do
|
3825
|
-
@ds
|
3826
|
-
@ds
|
3814
|
+
meta_def(@ds, :supports_cte?){true}
|
3815
|
+
meta_def(@ds, :supports_cte_in_subselect?){false}
|
3827
3816
|
@ds.with(:t, @ds.from(:s).with(:s, @ds.from(:r))).sql.should == 'WITH s AS (SELECT * FROM r), t AS (SELECT * FROM s) SELECT * FROM t'
|
3828
3817
|
@ds.with_recursive(:t, @ds.from(:s).with(:s, @ds.from(:r)), @ds.from(:q).with(:q, @ds.from(:p))).sql.should == 'WITH s AS (SELECT * FROM r), q AS (SELECT * FROM p), t AS (SELECT * FROM s UNION ALL SELECT * FROM q) SELECT * FROM t'
|
3829
3818
|
end
|
@@ -3878,8 +3867,8 @@ describe "Sequel timezone support" do
|
|
3878
3867
|
before do
|
3879
3868
|
@db = Sequel::Database.new
|
3880
3869
|
@dataset = @db.dataset
|
3881
|
-
@dataset
|
3882
|
-
@dataset
|
3870
|
+
meta_def(@dataset, :supports_timestamp_timezones?){true}
|
3871
|
+
meta_def(@dataset, :supports_timestamp_usecs?){false}
|
3883
3872
|
@offset = sprintf("%+03i%02i", *(Time.now.utc_offset/60).divmod(60))
|
3884
3873
|
end
|
3885
3874
|
after do
|
@@ -4306,7 +4295,7 @@ end
|
|
4306
4295
|
describe "Modifying joined datasets" do
|
4307
4296
|
before do
|
4308
4297
|
@ds = Sequel.mock.from(:b, :c).join(:d, [:id]).where(:id => 2)
|
4309
|
-
@ds
|
4298
|
+
meta_def(@ds, :supports_modifying_joins?){true}
|
4310
4299
|
end
|
4311
4300
|
|
4312
4301
|
specify "should allow deleting from joined datasets" do
|
@@ -4356,7 +4345,7 @@ describe "Dataset#returning" do
|
|
4356
4345
|
@ds = Sequel.mock(:fetch=>proc{|s| {:foo=>s}})[:t].returning(:foo)
|
4357
4346
|
@pr = proc do
|
4358
4347
|
[:insert, :update, :delete].each do |m|
|
4359
|
-
@ds
|
4348
|
+
meta_def(@ds, :"#{m}_clause_methods"){super() + [:"#{m}_returning_sql"]}
|
4360
4349
|
end
|
4361
4350
|
end
|
4362
4351
|
end
|
@@ -4387,7 +4376,6 @@ describe "Dataset#returning" do
|
|
4387
4376
|
|
4388
4377
|
specify "should have insert, update, and delete return arrays of hashes if RETURNING is used and a block is not given" do
|
4389
4378
|
@pr.call
|
4390
|
-
h = {}
|
4391
4379
|
@ds.delete.should == [{:foo=>"DELETE FROM t RETURNING foo"}]
|
4392
4380
|
@ds.insert(1).should == [{:foo=>"INSERT INTO t VALUES (1) RETURNING foo"}]
|
4393
4381
|
@ds.update(:foo=>1).should == [{:foo=>"UPDATE t SET foo = 1 RETURNING foo"}]
|
@@ -4424,13 +4412,16 @@ describe "Dataset extensions" do
|
|
4424
4412
|
before(:all) do
|
4425
4413
|
class << Sequel
|
4426
4414
|
alias _extension extension
|
4415
|
+
remove_method :extension
|
4427
4416
|
def extension(*)
|
4428
4417
|
end
|
4429
4418
|
end
|
4430
4419
|
end
|
4431
4420
|
after(:all) do
|
4432
4421
|
class << Sequel
|
4422
|
+
remove_method :extension
|
4433
4423
|
alias extension _extension
|
4424
|
+
remove_method :_extension
|
4434
4425
|
end
|
4435
4426
|
end
|
4436
4427
|
before do
|