sequel 3.46.0 → 3.47.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.
- 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
|