sequel 3.46.0 → 3.47.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +96 -0
  3. data/Rakefile +7 -1
  4. data/bin/sequel +6 -4
  5. data/doc/active_record.rdoc +1 -1
  6. data/doc/advanced_associations.rdoc +14 -35
  7. data/doc/association_basics.rdoc +66 -4
  8. data/doc/migration.rdoc +4 -0
  9. data/doc/opening_databases.rdoc +6 -0
  10. data/doc/postgresql.rdoc +302 -0
  11. data/doc/release_notes/3.47.0.txt +270 -0
  12. data/doc/security.rdoc +6 -0
  13. data/lib/sequel/adapters/ibmdb.rb +9 -9
  14. data/lib/sequel/adapters/jdbc.rb +22 -7
  15. data/lib/sequel/adapters/jdbc/postgresql.rb +7 -2
  16. data/lib/sequel/adapters/mock.rb +2 -0
  17. data/lib/sequel/adapters/postgres.rb +44 -13
  18. data/lib/sequel/adapters/shared/mssql.rb +1 -1
  19. data/lib/sequel/adapters/shared/mysql.rb +2 -2
  20. data/lib/sequel/adapters/shared/postgres.rb +94 -55
  21. data/lib/sequel/adapters/shared/sqlite.rb +3 -1
  22. data/lib/sequel/adapters/sqlite.rb +2 -2
  23. data/lib/sequel/adapters/utils/pg_types.rb +1 -14
  24. data/lib/sequel/adapters/utils/split_alter_table.rb +3 -3
  25. data/lib/sequel/connection_pool/threaded.rb +1 -1
  26. data/lib/sequel/core.rb +1 -1
  27. data/lib/sequel/database/connecting.rb +2 -2
  28. data/lib/sequel/database/features.rb +5 -0
  29. data/lib/sequel/database/misc.rb +47 -5
  30. data/lib/sequel/database/query.rb +2 -2
  31. data/lib/sequel/dataset/actions.rb +4 -2
  32. data/lib/sequel/dataset/misc.rb +1 -1
  33. data/lib/sequel/dataset/prepared_statements.rb +1 -1
  34. data/lib/sequel/dataset/query.rb +8 -6
  35. data/lib/sequel/dataset/sql.rb +8 -6
  36. data/lib/sequel/extensions/constraint_validations.rb +5 -2
  37. data/lib/sequel/extensions/migration.rb +10 -8
  38. data/lib/sequel/extensions/pagination.rb +3 -0
  39. data/lib/sequel/extensions/pg_array.rb +85 -25
  40. data/lib/sequel/extensions/pg_hstore.rb +8 -1
  41. data/lib/sequel/extensions/pg_hstore_ops.rb +4 -1
  42. data/lib/sequel/extensions/pg_inet.rb +16 -13
  43. data/lib/sequel/extensions/pg_interval.rb +6 -2
  44. data/lib/sequel/extensions/pg_json.rb +18 -11
  45. data/lib/sequel/extensions/pg_range.rb +17 -2
  46. data/lib/sequel/extensions/pg_range_ops.rb +7 -5
  47. data/lib/sequel/extensions/pg_row.rb +29 -12
  48. data/lib/sequel/extensions/pretty_table.rb +3 -0
  49. data/lib/sequel/extensions/query.rb +3 -0
  50. data/lib/sequel/extensions/schema_caching.rb +2 -0
  51. data/lib/sequel/extensions/schema_dumper.rb +3 -1
  52. data/lib/sequel/extensions/select_remove.rb +3 -0
  53. data/lib/sequel/model.rb +8 -2
  54. data/lib/sequel/model/associations.rb +39 -27
  55. data/lib/sequel/model/base.rb +99 -38
  56. data/lib/sequel/model/plugins.rb +25 -0
  57. data/lib/sequel/plugins/association_autoreloading.rb +27 -22
  58. data/lib/sequel/plugins/association_dependencies.rb +1 -7
  59. data/lib/sequel/plugins/auto_validations.rb +110 -0
  60. data/lib/sequel/plugins/boolean_readers.rb +1 -6
  61. data/lib/sequel/plugins/caching.rb +6 -13
  62. data/lib/sequel/plugins/class_table_inheritance.rb +1 -0
  63. data/lib/sequel/plugins/composition.rb +14 -7
  64. data/lib/sequel/plugins/constraint_validations.rb +2 -13
  65. data/lib/sequel/plugins/defaults_setter.rb +1 -6
  66. data/lib/sequel/plugins/dirty.rb +8 -0
  67. data/lib/sequel/plugins/error_splitter.rb +54 -0
  68. data/lib/sequel/plugins/force_encoding.rb +1 -5
  69. data/lib/sequel/plugins/hook_class_methods.rb +1 -6
  70. data/lib/sequel/plugins/input_transformer.rb +79 -0
  71. data/lib/sequel/plugins/instance_filters.rb +7 -1
  72. data/lib/sequel/plugins/instance_hooks.rb +7 -1
  73. data/lib/sequel/plugins/json_serializer.rb +5 -10
  74. data/lib/sequel/plugins/lazy_attributes.rb +20 -7
  75. data/lib/sequel/plugins/list.rb +1 -6
  76. data/lib/sequel/plugins/many_through_many.rb +1 -2
  77. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +23 -39
  78. data/lib/sequel/plugins/optimistic_locking.rb +1 -5
  79. data/lib/sequel/plugins/pg_row.rb +4 -2
  80. data/lib/sequel/plugins/pg_typecast_on_load.rb +3 -7
  81. data/lib/sequel/plugins/prepared_statements.rb +1 -5
  82. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -11
  83. data/lib/sequel/plugins/rcte_tree.rb +2 -2
  84. data/lib/sequel/plugins/serialization.rb +11 -13
  85. data/lib/sequel/plugins/serialization_modification_detection.rb +13 -1
  86. data/lib/sequel/plugins/single_table_inheritance.rb +4 -4
  87. data/lib/sequel/plugins/static_cache.rb +67 -19
  88. data/lib/sequel/plugins/string_stripper.rb +7 -27
  89. data/lib/sequel/plugins/subclasses.rb +3 -5
  90. data/lib/sequel/plugins/tactical_eager_loading.rb +2 -2
  91. data/lib/sequel/plugins/timestamps.rb +2 -7
  92. data/lib/sequel/plugins/touch.rb +5 -8
  93. data/lib/sequel/plugins/tree.rb +1 -6
  94. data/lib/sequel/plugins/typecast_on_load.rb +1 -5
  95. data/lib/sequel/plugins/update_primary_key.rb +26 -14
  96. data/lib/sequel/plugins/validation_class_methods.rb +31 -16
  97. data/lib/sequel/plugins/validation_helpers.rb +50 -26
  98. data/lib/sequel/plugins/xml_serializer.rb +3 -6
  99. data/lib/sequel/sql.rb +1 -1
  100. data/lib/sequel/version.rb +1 -1
  101. data/spec/adapters/postgres_spec.rb +131 -15
  102. data/spec/adapters/sqlite_spec.rb +1 -1
  103. data/spec/core/connection_pool_spec.rb +16 -17
  104. data/spec/core/database_spec.rb +111 -40
  105. data/spec/core/dataset_spec.rb +65 -74
  106. data/spec/core/expression_filters_spec.rb +6 -5
  107. data/spec/core/object_graph_spec.rb +0 -1
  108. data/spec/core/schema_spec.rb +23 -23
  109. data/spec/core/spec_helper.rb +5 -1
  110. data/spec/extensions/association_dependencies_spec.rb +1 -1
  111. data/spec/extensions/association_proxies_spec.rb +1 -1
  112. data/spec/extensions/auto_validations_spec.rb +90 -0
  113. data/spec/extensions/caching_spec.rb +6 -0
  114. data/spec/extensions/class_table_inheritance_spec.rb +8 -1
  115. data/spec/extensions/composition_spec.rb +12 -5
  116. data/spec/extensions/constraint_validations_spec.rb +4 -4
  117. data/spec/extensions/core_refinements_spec.rb +29 -79
  118. data/spec/extensions/dirty_spec.rb +14 -0
  119. data/spec/extensions/error_splitter_spec.rb +18 -0
  120. data/spec/extensions/identity_map_spec.rb +0 -1
  121. data/spec/extensions/input_transformer_spec.rb +54 -0
  122. data/spec/extensions/instance_filters_spec.rb +6 -0
  123. data/spec/extensions/instance_hooks_spec.rb +12 -1
  124. data/spec/extensions/json_serializer_spec.rb +0 -1
  125. data/spec/extensions/lazy_attributes_spec.rb +64 -55
  126. data/spec/extensions/looser_typecasting_spec.rb +1 -1
  127. data/spec/extensions/many_through_many_spec.rb +3 -4
  128. data/spec/extensions/many_to_one_pk_lookup_spec.rb +53 -15
  129. data/spec/extensions/migration_spec.rb +16 -0
  130. data/spec/extensions/null_dataset_spec.rb +1 -1
  131. data/spec/extensions/pg_array_spec.rb +48 -1
  132. data/spec/extensions/pg_hstore_ops_spec.rb +10 -2
  133. data/spec/extensions/pg_hstore_spec.rb +5 -0
  134. data/spec/extensions/pg_inet_spec.rb +5 -0
  135. data/spec/extensions/pg_interval_spec.rb +7 -3
  136. data/spec/extensions/pg_json_spec.rb +6 -1
  137. data/spec/extensions/pg_range_ops_spec.rb +4 -1
  138. data/spec/extensions/pg_range_spec.rb +5 -0
  139. data/spec/extensions/pg_row_plugin_spec.rb +13 -0
  140. data/spec/extensions/pg_row_spec.rb +28 -19
  141. data/spec/extensions/pg_typecast_on_load_spec.rb +6 -1
  142. data/spec/extensions/prepared_statements_associations_spec.rb +1 -1
  143. data/spec/extensions/query_literals_spec.rb +1 -1
  144. data/spec/extensions/rcte_tree_spec.rb +2 -2
  145. data/spec/extensions/schema_spec.rb +2 -2
  146. data/spec/extensions/serialization_modification_detection_spec.rb +8 -0
  147. data/spec/extensions/serialization_spec.rb +15 -1
  148. data/spec/extensions/sharding_spec.rb +1 -1
  149. data/spec/extensions/single_table_inheritance_spec.rb +1 -1
  150. data/spec/extensions/static_cache_spec.rb +59 -9
  151. data/spec/extensions/tactical_eager_loading_spec.rb +19 -4
  152. data/spec/extensions/update_primary_key_spec.rb +17 -1
  153. data/spec/extensions/validation_class_methods_spec.rb +25 -0
  154. data/spec/extensions/validation_helpers_spec.rb +59 -3
  155. data/spec/integration/associations_test.rb +5 -5
  156. data/spec/integration/eager_loader_test.rb +32 -63
  157. data/spec/integration/model_test.rb +2 -2
  158. data/spec/integration/plugin_test.rb +88 -56
  159. data/spec/integration/prepared_statement_test.rb +1 -1
  160. data/spec/integration/schema_test.rb +1 -1
  161. data/spec/integration/timezone_test.rb +0 -1
  162. data/spec/integration/transaction_test.rb +0 -1
  163. data/spec/model/association_reflection_spec.rb +1 -1
  164. data/spec/model/associations_spec.rb +106 -84
  165. data/spec/model/base_spec.rb +4 -4
  166. data/spec/model/eager_loading_spec.rb +8 -8
  167. data/spec/model/model_spec.rb +27 -9
  168. data/spec/model/plugins_spec.rb +71 -0
  169. data/spec/model/record_spec.rb +99 -13
  170. metadata +12 -2
@@ -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 deep-copy the dataset opts" do
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.meta_def(:check_truncation_allowed!){}
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.meta_def(:insert_supports_empty_values?){false}
236
- @dataset.meta_def(:columns){[:a, :b]}
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.meta_def(:supports_multiple_column_in?){false}
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.meta_def(:supports_multiple_column_in?){false}
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.meta_def(:supports_multiple_column_in?){false}
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 with correct NULL handling" do
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.meta_def(:supports_multiple_column_in?){false}
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.meta_def(:supports_multiple_column_in?){false}
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.meta_def(:supports_group_rollup?){true}
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.meta_def(:uses_with_rollup?){true}
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.meta_def(:supports_group_cube?){true}
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.meta_def(:uses_with_rollup?){true}
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.meta_def(:requires_sql_standard_datetimes?){true}
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.meta_def(:supports_timestamp_timezones?){true}
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.meta_def(:supports_timestamp_usecs?){false}
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.meta_def(:supports_timestamp_timezones?){true}
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.meta_def(:supports_cte?){true}
1202
- @dataset.meta_def(:supports_cte_in_subselect?){false}
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.meta_def(:supports_select_all_and_column?){false}
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 value" do
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.meta_def(:supports_distinct_on?){true}
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.should == :t}.should raise_error(Sequel::Error)
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.should == :t}.should raise_error(Sequel::Error)
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
- ds.meta_def(:supports_cte?){true}
2046
- ds.meta_def(:supports_cte_in_subselect?){false}
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.meta_def(:supports_join_using?){false}
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.meta_def(:supports_cte?){true}
2234
- @d.meta_def(:supports_cte_in_subselect?){false}
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
- r = @d.order(:a).first!(i).should == [{:s=>"SELECT * FROM test ORDER BY a LIMIT #{i}"}]
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
- r = @d.order(:a).first!(i){z > 26}.should == [{:s=>"SELECT * FROM test WHERE (z > 26) ORDER BY a LIMIT #{i}"}]
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.meta_def(:supports_intersect_except?){false}
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.meta_def(:supports_intersect_except_all?){false}
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
- ds.meta_def(:supports_cte?){true}
2649
- ds.meta_def(:supports_cte_in_subselect?){false}
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 SQL filter correctly" do
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.meta_def(:insert_sql){|*v| "#{super(*v)}#{' RETURNING *' if opts.has_key?(:returning)}" }
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.meta_def(:select_where_sql){|sql| super(sql); sql << " OR #{columns.first} = 1" if opts[:where]}
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{|p| p.extend(Sequel::Dataset::UnnumberedArgumentMapper)}
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.meta_def(:supports_window_functions?){true}
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.meta_def(:supports_cte?){false}
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.meta_def(:"#{m}_clause_methods"){[:"#{m}_with_sql"] + super()}
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.meta_def(:supports_cte?){true}
3826
- @ds.meta_def(:supports_cte_in_subselect?){false}
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.meta_def(:supports_timestamp_timezones?){true}
3882
- @dataset.meta_def(:supports_timestamp_usecs?){false}
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.meta_def(:supports_modifying_joins?){true}
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.meta_def(:"#{m}_clause_methods"){super() + [:"#{m}_returning_sql"]}
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