sequel 3.33.0 → 3.34.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.
Files changed (152) hide show
  1. data/CHANGELOG +140 -0
  2. data/Rakefile +7 -0
  3. data/bin/sequel +22 -2
  4. data/doc/dataset_basics.rdoc +1 -1
  5. data/doc/mass_assignment.rdoc +3 -1
  6. data/doc/querying.rdoc +28 -4
  7. data/doc/reflection.rdoc +23 -3
  8. data/doc/release_notes/3.34.0.txt +671 -0
  9. data/doc/schema_modification.rdoc +18 -2
  10. data/doc/virtual_rows.rdoc +49 -0
  11. data/lib/sequel/adapters/do/mysql.rb +0 -5
  12. data/lib/sequel/adapters/ibmdb.rb +9 -4
  13. data/lib/sequel/adapters/jdbc.rb +9 -4
  14. data/lib/sequel/adapters/jdbc/h2.rb +8 -2
  15. data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
  16. data/lib/sequel/adapters/jdbc/postgresql.rb +43 -0
  17. data/lib/sequel/adapters/jdbc/sqlite.rb +19 -0
  18. data/lib/sequel/adapters/mock.rb +24 -3
  19. data/lib/sequel/adapters/mysql.rb +29 -50
  20. data/lib/sequel/adapters/mysql2.rb +13 -28
  21. data/lib/sequel/adapters/oracle.rb +8 -2
  22. data/lib/sequel/adapters/postgres.rb +115 -20
  23. data/lib/sequel/adapters/shared/db2.rb +1 -1
  24. data/lib/sequel/adapters/shared/mssql.rb +14 -3
  25. data/lib/sequel/adapters/shared/mysql.rb +59 -11
  26. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
  27. data/lib/sequel/adapters/shared/oracle.rb +1 -1
  28. data/lib/sequel/adapters/shared/postgres.rb +127 -30
  29. data/lib/sequel/adapters/shared/sqlite.rb +55 -38
  30. data/lib/sequel/adapters/sqlite.rb +9 -3
  31. data/lib/sequel/adapters/swift.rb +2 -2
  32. data/lib/sequel/adapters/swift/mysql.rb +0 -5
  33. data/lib/sequel/adapters/swift/postgres.rb +10 -0
  34. data/lib/sequel/ast_transformer.rb +4 -0
  35. data/lib/sequel/connection_pool.rb +8 -0
  36. data/lib/sequel/connection_pool/sharded_single.rb +5 -0
  37. data/lib/sequel/connection_pool/sharded_threaded.rb +17 -0
  38. data/lib/sequel/connection_pool/single.rb +5 -0
  39. data/lib/sequel/connection_pool/threaded.rb +14 -0
  40. data/lib/sequel/core.rb +24 -3
  41. data/lib/sequel/database/connecting.rb +24 -14
  42. data/lib/sequel/database/dataset_defaults.rb +1 -0
  43. data/lib/sequel/database/misc.rb +16 -25
  44. data/lib/sequel/database/query.rb +20 -2
  45. data/lib/sequel/database/schema_generator.rb +2 -2
  46. data/lib/sequel/database/schema_methods.rb +120 -23
  47. data/lib/sequel/dataset/actions.rb +91 -18
  48. data/lib/sequel/dataset/features.rb +5 -0
  49. data/lib/sequel/dataset/prepared_statements.rb +6 -2
  50. data/lib/sequel/dataset/sql.rb +68 -51
  51. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  52. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +18 -13
  53. data/lib/sequel/extensions/migration.rb +4 -0
  54. data/lib/sequel/extensions/null_dataset.rb +90 -0
  55. data/lib/sequel/extensions/pg_array.rb +460 -0
  56. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  57. data/lib/sequel/extensions/pg_auto_parameterize.rb +174 -0
  58. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  59. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  60. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  61. data/lib/sequel/extensions/pretty_table.rb +5 -71
  62. data/lib/sequel/extensions/query_literals.rb +79 -0
  63. data/lib/sequel/extensions/schema_caching.rb +76 -0
  64. data/lib/sequel/extensions/schema_dumper.rb +227 -31
  65. data/lib/sequel/extensions/select_remove.rb +35 -0
  66. data/lib/sequel/extensions/sql_expr.rb +4 -110
  67. data/lib/sequel/extensions/to_dot.rb +1 -1
  68. data/lib/sequel/model.rb +11 -2
  69. data/lib/sequel/model/associations.rb +35 -7
  70. data/lib/sequel/model/base.rb +159 -36
  71. data/lib/sequel/no_core_ext.rb +2 -0
  72. data/lib/sequel/plugins/caching.rb +25 -18
  73. data/lib/sequel/plugins/composition.rb +1 -1
  74. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  75. data/lib/sequel/plugins/identity_map.rb +11 -3
  76. data/lib/sequel/plugins/instance_filters.rb +10 -0
  77. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  78. data/lib/sequel/plugins/nested_attributes.rb +4 -3
  79. data/lib/sequel/plugins/prepared_statements.rb +3 -1
  80. data/lib/sequel/plugins/prepared_statements_associations.rb +5 -1
  81. data/lib/sequel/plugins/schema.rb +7 -2
  82. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  83. data/lib/sequel/plugins/static_cache.rb +99 -0
  84. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  85. data/lib/sequel/sql.rb +417 -7
  86. data/lib/sequel/version.rb +1 -1
  87. data/spec/adapters/firebird_spec.rb +1 -1
  88. data/spec/adapters/mssql_spec.rb +12 -15
  89. data/spec/adapters/mysql_spec.rb +81 -23
  90. data/spec/adapters/postgres_spec.rb +444 -77
  91. data/spec/adapters/spec_helper.rb +2 -0
  92. data/spec/adapters/sqlite_spec.rb +8 -8
  93. data/spec/core/connection_pool_spec.rb +85 -0
  94. data/spec/core/database_spec.rb +29 -5
  95. data/spec/core/dataset_spec.rb +171 -3
  96. data/spec/core/expression_filters_spec.rb +364 -0
  97. data/spec/core/mock_adapter_spec.rb +17 -3
  98. data/spec/core/schema_spec.rb +133 -0
  99. data/spec/extensions/association_dependencies_spec.rb +13 -13
  100. data/spec/extensions/caching_spec.rb +26 -3
  101. data/spec/extensions/class_table_inheritance_spec.rb +2 -2
  102. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +23 -94
  103. data/spec/extensions/force_encoding_spec.rb +4 -2
  104. data/spec/extensions/hook_class_methods_spec.rb +5 -2
  105. data/spec/extensions/identity_map_spec.rb +17 -0
  106. data/spec/extensions/instance_filters_spec.rb +1 -1
  107. data/spec/extensions/lazy_attributes_spec.rb +2 -2
  108. data/spec/extensions/list_spec.rb +4 -4
  109. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  110. data/spec/extensions/migration_spec.rb +6 -2
  111. data/spec/extensions/nested_attributes_spec.rb +20 -0
  112. data/spec/extensions/null_dataset_spec.rb +85 -0
  113. data/spec/extensions/optimistic_locking_spec.rb +2 -2
  114. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  115. data/spec/extensions/pg_array_spec.rb +196 -0
  116. data/spec/extensions/pg_auto_parameterize_spec.rb +64 -0
  117. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  118. data/spec/extensions/pg_hstore_spec.rb +195 -0
  119. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  120. data/spec/extensions/prepared_statements_spec.rb +4 -0
  121. data/spec/extensions/pretty_table_spec.rb +6 -0
  122. data/spec/extensions/query_literals_spec.rb +168 -0
  123. data/spec/extensions/schema_caching_spec.rb +41 -0
  124. data/spec/extensions/schema_dumper_spec.rb +231 -11
  125. data/spec/extensions/schema_spec.rb +14 -2
  126. data/spec/extensions/select_remove_spec.rb +38 -0
  127. data/spec/extensions/sharding_spec.rb +6 -6
  128. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  129. data/spec/extensions/spec_helper.rb +2 -1
  130. data/spec/extensions/sql_expr_spec.rb +28 -19
  131. data/spec/extensions/static_cache_spec.rb +145 -0
  132. data/spec/extensions/touch_spec.rb +1 -1
  133. data/spec/extensions/typecast_on_load_spec.rb +9 -1
  134. data/spec/integration/associations_test.rb +6 -6
  135. data/spec/integration/database_test.rb +1 -1
  136. data/spec/integration/dataset_test.rb +89 -26
  137. data/spec/integration/migrator_test.rb +2 -3
  138. data/spec/integration/model_test.rb +3 -3
  139. data/spec/integration/plugin_test.rb +85 -22
  140. data/spec/integration/prepared_statement_test.rb +28 -8
  141. data/spec/integration/schema_test.rb +78 -7
  142. data/spec/integration/spec_helper.rb +1 -0
  143. data/spec/integration/timezone_test.rb +1 -1
  144. data/spec/integration/transaction_test.rb +4 -6
  145. data/spec/integration/type_test.rb +2 -2
  146. data/spec/model/associations_spec.rb +94 -8
  147. data/spec/model/base_spec.rb +4 -4
  148. data/spec/model/hooks_spec.rb +2 -2
  149. data/spec/model/model_spec.rb +19 -7
  150. data/spec/model/record_spec.rb +135 -58
  151. data/spec/model/spec_helper.rb +1 -0
  152. metadata +35 -7
@@ -713,4 +713,368 @@ describe Sequel::SQL::VirtualRow do
713
713
  Sequel::BasicObject.remove_methods!
714
714
  @d.l{a > adsoiwemlsdaf2}.should == '("a" > "adsoiwemlsdaf2")'
715
715
  end
716
+
717
+ it "should have operator methods defined that produce Sequel expression objects" do
718
+ @d.l{|o| o.&({:a=>1}, :b)}.should == '(("a" = 1) AND "b")'
719
+ @d.l{|o| o.|({:a=>1}, :b)}.should == '(("a" = 1) OR "b")'
720
+ @d.l{|o| o.+(1, :b) > 2}.should == '((1 + "b") > 2)'
721
+ @d.l{|o| o.-(1, :b) < 2}.should == '((1 - "b") < 2)'
722
+ @d.l{|o| o.*(1, :b) >= 2}.should == '((1 * "b") >= 2)'
723
+ @d.l{|o| o./(1, :b) <= 2}.should == '((1 / "b") <= 2)'
724
+ @d.l{|o| o.~(:a=>1)}.should == '("a" != 1)'
725
+ @d.l{|o| o.~([[:a, 1], [:b, 2]])}.should == '(("a" != 1) OR ("b" != 2))'
726
+ @d.l{|o| o.<(1, :b)}.should == '(1 < "b")'
727
+ @d.l{|o| o.>(1, :b)}.should == '(1 > "b")'
728
+ @d.l{|o| o.<=(1, :b)}.should == '(1 <= "b")'
729
+ @d.l{|o| o.>=(1, :b)}.should == '(1 >= "b")'
730
+ end
731
+
732
+ it "should have have ` produce literal strings" do
733
+ @d.l{a > `some SQL`}.should == '("a" > some SQL)'
734
+ @d.l{|o| o.a > o.`('some SQL')}.should == '("a" > some SQL)' #`
735
+ end
736
+ end
737
+
738
+ describe "Sequel core extension replacements" do
739
+ before do
740
+ @db = Sequel::Database.new
741
+ @o = Object.new
742
+ def @o.sql_literal(ds) 'foo' end
743
+ end
744
+
745
+ def l(arg, should)
746
+ @db.literal(arg).should == should
747
+ end
748
+
749
+ it "Sequel.expr should return an appropriate wrapped object" do
750
+ l(Sequel.expr(1) + 1, "(1 + 1)")
751
+ l(Sequel.expr('a') + 'b', "('a' || 'b')")
752
+ l(Sequel.expr(:b) & nil, "(b AND NULL)")
753
+ l(Sequel.expr(nil) & true, "(NULL AND 't')")
754
+ l(Sequel.expr(false) & true, "('f' AND 't')")
755
+ l(Sequel.expr(true) | false, "('t' OR 'f')")
756
+ l(Sequel.expr(@o) + 1, "(foo + 1)")
757
+ end
758
+
759
+ it "Sequel.expr should handle condition specifiers" do
760
+ l(Sequel.expr(:a=>1) & nil, "((a = 1) AND NULL)")
761
+ l(Sequel.expr([[:a, 1]]) & nil, "((a = 1) AND NULL)")
762
+ l(Sequel.expr([[:a, 1], [:b, 2]]) & nil, "((a = 1) AND (b = 2) AND NULL)")
763
+ end
764
+
765
+ it "Sequel.expr should treat blocks/procs as virtual rows and wrap the output" do
766
+ l(Sequel.expr{1} + 1, "(1 + 1)")
767
+ l(Sequel.expr{o__a} + 1, "(o.a + 1)")
768
+ l(Sequel.expr{[[:a, 1]]} & nil, "((a = 1) AND NULL)")
769
+ l(Sequel.expr{|v| @o} + 1, "(foo + 1)")
770
+
771
+ l(Sequel.expr(proc{1}) + 1, "(1 + 1)")
772
+ l(Sequel.expr(proc{o__a}) + 1, "(o.a + 1)")
773
+ l(Sequel.expr(proc{[[:a, 1]]}) & nil, "((a = 1) AND NULL)")
774
+ l(Sequel.expr(proc{|v| @o}) + 1, "(foo + 1)")
775
+ end
776
+
777
+ it "Sequel.expr should raise an error if given an argument and a block" do
778
+ proc{Sequel.expr(nil){}}.should raise_error(Sequel::Error)
779
+ end
780
+
781
+ it "Sequel.expr should raise an error if given neither an argument nor a block" do
782
+ proc{Sequel.expr}.should raise_error(Sequel::Error)
783
+ end
784
+
785
+ it "Sequel.expr should return existing Sequel expressions directly" do
786
+ o = Sequel.expr(1)
787
+ Sequel.expr(o).should equal(o)
788
+ o = Sequel.lit('1')
789
+ Sequel.expr(o).should equal(o)
790
+ end
791
+
792
+ it "Sequel.~ should invert the given object" do
793
+ l(Sequel.~(nil), 'NOT NULL')
794
+ l(Sequel.~(:a=>1), "(a != 1)")
795
+ l(Sequel.~([[:a, 1]]), "(a != 1)")
796
+ l(Sequel.~([[:a, 1], [:b, 2]]), "((a != 1) OR (b != 2))")
797
+ l(Sequel.~(Sequel.expr([[:a, 1], [:b, 2]]) & nil), "((a != 1) OR (b != 2) OR NOT NULL)")
798
+ end
799
+
800
+ it "Sequel.case should use a CASE expression" do
801
+ l(Sequel.case({:a=>1}, 2), "(CASE WHEN a THEN 1 ELSE 2 END)")
802
+ l(Sequel.case({:a=>1}, 2, :b), "(CASE b WHEN a THEN 1 ELSE 2 END)")
803
+ l(Sequel.case([[:a, 1]], 2), "(CASE WHEN a THEN 1 ELSE 2 END)")
804
+ l(Sequel.case([[:a, 1]], 2, :b), "(CASE b WHEN a THEN 1 ELSE 2 END)")
805
+ l(Sequel.case([[:a, 1], [:c, 3]], 2), "(CASE WHEN a THEN 1 WHEN c THEN 3 ELSE 2 END)")
806
+ l(Sequel.case([[:a, 1], [:c, 3]], 2, :b), "(CASE b WHEN a THEN 1 WHEN c THEN 3 ELSE 2 END)")
807
+ end
808
+
809
+ it "Sequel.case should raise an error if not given a condition specifier" do
810
+ proc{Sequel.case(1, 2)}.should raise_error(Sequel::Error)
811
+ end
812
+
813
+ it "Sequel.value_list should use an SQL value list" do
814
+ l(Sequel.value_list([[1, 2]]), "((1, 2))")
815
+ end
816
+
817
+ it "Sequel.value_list raise an error if not given an array" do
818
+ proc{Sequel.value_list(1)}.should raise_error(Sequel::Error)
819
+ end
820
+
821
+ it "Sequel.negate should negate all entries in conditions specifier and join with AND" do
822
+ l(Sequel.negate(:a=>1), "(a != 1)")
823
+ l(Sequel.negate([[:a, 1]]), "(a != 1)")
824
+ l(Sequel.negate([[:a, 1], [:b, 2]]), "((a != 1) AND (b != 2))")
825
+ end
826
+
827
+ it "Sequel.negate should raise an error if not given a conditions specifier" do
828
+ proc{Sequel.negate(1)}.should raise_error(Sequel::Error)
829
+ end
830
+
831
+ it "Sequel.or should join all entries in conditions specifier with OR" do
832
+ l(Sequel.or(:a=>1), "(a = 1)")
833
+ l(Sequel.or([[:a, 1]]), "(a = 1)")
834
+ l(Sequel.or([[:a, 1], [:b, 2]]), "((a = 1) OR (b = 2))")
835
+ end
836
+
837
+ it "Sequel.or should raise an error if not given a conditions specifier" do
838
+ proc{Sequel.or(1)}.should raise_error(Sequel::Error)
839
+ end
840
+
841
+ it "Sequel.join should should use SQL string concatenation to join array" do
842
+ l(Sequel.join([]), "''")
843
+ l(Sequel.join(['a']), "('a')")
844
+ l(Sequel.join(['a', 'b']), "('a' || 'b')")
845
+ l(Sequel.join(['a', 'b'], 'c'), "('a' || 'c' || 'b')")
846
+ l(Sequel.join([true, :b], :c), "('t' || c || b)")
847
+ l(Sequel.join([false, nil], Sequel.lit('c')), "('f' || c || NULL)")
848
+ l(Sequel.join([Sequel.expr('a'), Sequel.lit('d')], 'c'), "('a' || 'c' || d)")
849
+ end
850
+
851
+ it "Sequel.join should raise an error if not given an array" do
852
+ proc{Sequel.join(1)}.should raise_error(Sequel::Error)
853
+ end
854
+
855
+ it "Sequel.& should join all arguments given with AND" do
856
+ l(Sequel.&(:a), "(a)")
857
+ l(Sequel.&(:a, :b=>:c), "(a AND (b = c))")
858
+ l(Sequel.&(:a, {:b=>:c}, Sequel.lit('d')), "(a AND (b = c) AND d)")
859
+ end
860
+
861
+ it "Sequel.& should raise an error if given no arguments" do
862
+ proc{Sequel.&}.should raise_error(Sequel::Error)
863
+ end
864
+
865
+ it "Sequel.| should join all arguments given with OR" do
866
+ l(Sequel.|(:a), "(a)")
867
+ l(Sequel.|(:a, :b=>:c), "(a OR (b = c))")
868
+ l(Sequel.|(:a, {:b=>:c}, Sequel.lit('d')), "(a OR (b = c) OR d)")
869
+ end
870
+
871
+ it "Sequel.| should raise an error if given no arguments" do
872
+ proc{Sequel.|}.should raise_error(Sequel::Error)
873
+ end
874
+
875
+ it "Sequel.as should return an aliased expression" do
876
+ l(Sequel.as(:a, :b), "a AS b")
877
+ end
878
+
879
+ it "Sequel.cast should return a CAST expression" do
880
+ l(Sequel.cast(:a, :int), "CAST(a AS int)")
881
+ l(Sequel.cast(:a, Integer), "CAST(a AS integer)")
882
+ end
883
+
884
+ it "Sequel.cast_numeric should return a CAST expression treated as a number" do
885
+ l(Sequel.cast_numeric(:a), "CAST(a AS integer)")
886
+ l(Sequel.cast_numeric(:a, :int), "CAST(a AS int)")
887
+ l(Sequel.cast_numeric(:a) << 2, "(CAST(a AS integer) << 2)")
888
+ end
889
+
890
+ it "Sequel.cast_string should return a CAST expression treated as a string" do
891
+ l(Sequel.cast_string(:a), "CAST(a AS varchar(255))")
892
+ l(Sequel.cast_string(:a, :text), "CAST(a AS text)")
893
+ l(Sequel.cast_string(:a) + 'a', "(CAST(a AS varchar(255)) || 'a')")
894
+ end
895
+
896
+ it "Sequel.lit should return a literal string" do
897
+ l(Sequel.lit('a'), "a")
898
+ end
899
+
900
+ it "Sequel.lit should return the argument if given a single literal string" do
901
+ o = Sequel.lit('a')
902
+ Sequel.lit(o).should equal(o)
903
+ end
904
+
905
+ it "Sequel.lit should accept multiple arguments for a placeholder literal string" do
906
+ l(Sequel.lit('a = ?', 1), "a = 1")
907
+ l(Sequel.lit('? = ?', :a, 1), "a = 1")
908
+ l(Sequel.lit('a = :a', :a=>1), "a = 1")
909
+ end
910
+
911
+ it "Sequel.lit should work with an array for the placeholder string" do
912
+ l(Sequel.lit(['a = '], 1), "a = 1")
913
+ l(Sequel.lit(['', ' = '], :a, 1), "a = 1")
914
+ end
915
+
916
+ it "Sequel.blob should return an SQL::Blob" do
917
+ l(Sequel.blob('a'), "'a'")
918
+ Sequel.blob('a').should be_a_kind_of(Sequel::SQL::Blob)
919
+ end
920
+
921
+ it "Sequel.blob should return the given argument if given a blob" do
922
+ o = Sequel.blob('a')
923
+ Sequel.blob(o).should equal(o)
924
+ end
925
+
926
+ it "Sequel.qualify should return a qualified identifier" do
927
+ l(Sequel.qualify(:t, :c), "t.c")
928
+ end
929
+
930
+ it "Sequel.identifier should return an identifier" do
931
+ l(Sequel.identifier(:t__c), "t__c")
932
+ end
933
+
934
+ it "Sequel.asc should return an ASC ordered expression" do
935
+ l(Sequel.asc(:a), "a ASC")
936
+ l(Sequel.asc(:a, :nulls=>:first), "a ASC NULLS FIRST")
937
+ end
938
+
939
+ it "Sequel.desc should return a DESC ordered expression " do
940
+ l(Sequel.desc(:a), "a DESC")
941
+ l(Sequel.desc(:a, :nulls=>:last), "a DESC NULLS LAST")
942
+ end
943
+
944
+ it "Sequel.{+,-,*,/} should accept arguments and use the appropriate operator" do
945
+ %w'+ - * /'.each do |op|
946
+ l(Sequel.send(op, 1), '(1)')
947
+ l(Sequel.send(op, 1, 2), "(1 #{op} 2)")
948
+ l(Sequel.send(op, 1, 2, 3), "(1 #{op} 2 #{op} 3)")
949
+ end
950
+ end
951
+
952
+ it "Sequel.{+,-,*,/} should raise if given no arguments" do
953
+ %w'+ - * /'.each do |op|
954
+ proc{Sequel.send(op)}.should raise_error(Sequel::Error)
955
+ end
956
+ end
957
+
958
+ it "Sequel.like should use a LIKE expression" do
959
+ l(Sequel.like('a', 'b'), "('a' LIKE 'b')")
960
+ l(Sequel.like(:a, :b), "(a LIKE b)")
961
+ l(Sequel.like(:a, /b/), "(a ~ 'b')")
962
+ l(Sequel.like(:a, 'c', /b/), "((a LIKE 'c') OR (a ~ 'b'))")
963
+ end
964
+
965
+ it "Sequel.ilike should use an ILIKE expression" do
966
+ l(Sequel.ilike('a', 'b'), "('a' ILIKE 'b')")
967
+ l(Sequel.ilike(:a, :b), "(a ILIKE b)")
968
+ l(Sequel.ilike(:a, /b/), "(a ~* 'b')")
969
+ l(Sequel.ilike(:a, 'c', /b/), "((a ILIKE 'c') OR (a ~* 'b'))")
970
+ end
971
+
972
+ it "Sequel.subscript should use an SQL subscript" do
973
+ l(Sequel.subscript(:a, 1), 'a[1]')
974
+ l(Sequel.subscript(:a, 1, 2), 'a[1, 2]')
975
+ l(Sequel.subscript(:a, [1, 2]), 'a[1, 2]')
976
+ end
977
+
978
+ it "Sequel.function should return an SQL function" do
979
+ l(Sequel.function(:a), 'a()')
980
+ l(Sequel.function(:a, 1), 'a(1)')
981
+ l(Sequel.function(:a, :b, 2), 'a(b, 2)')
982
+ end
983
+
984
+ it "Sequel.extract should use a date/time extraction" do
985
+ l(Sequel.extract(:year, :a), 'extract(year FROM a)')
986
+ end
987
+ end
988
+
989
+ describe "Sequel::SQL::Function#==" do
990
+ specify "should be true for functions with the same name and arguments, false otherwise" do
991
+ a = :date.sql_function(:t)
992
+ b = :date.sql_function(:t)
993
+ a.should == b
994
+ (a == b).should == true
995
+ c = :date.sql_function(:c)
996
+ a.should_not == c
997
+ (a == c).should == false
998
+ d = :time.sql_function(:c)
999
+ a.should_not == d
1000
+ c.should_not == d
1001
+ (a == d).should == false
1002
+ (c == d).should == false
1003
+ end
1004
+ end
1005
+
1006
+ describe "Sequel::SQL::OrderedExpression" do
1007
+ specify "should #desc" do
1008
+ @oe = Sequel.asc(:column)
1009
+ @oe.descending.should == false
1010
+ @oe.desc.descending.should == true
1011
+ end
1012
+
1013
+ specify "should #asc" do
1014
+ @oe = Sequel.desc(:column)
1015
+ @oe.descending.should == true
1016
+ @oe.asc.descending.should == false
1017
+ end
1018
+
1019
+ specify "should #invert" do
1020
+ @oe = Sequel.desc(:column)
1021
+ @oe.invert.descending.should == false
1022
+ @oe.invert.invert.descending.should == true
1023
+ end
1024
+ end
1025
+
1026
+ describe "Expression" do
1027
+ specify "should consider objects == only if they have the same attributes" do
1028
+ :column.qualify(:table).cast(:type).*(:numeric_column).asc.should == :column.qualify(:table).cast(:type).*(:numeric_column).asc
1029
+ :other_column.qualify(:table).cast(:type).*(:numeric_column).asc.should_not == :column.qualify(:table).cast(:type).*(:numeric_column).asc
1030
+
1031
+ :column.qualify(:table).cast(:type).*(:numeric_column).asc.should eql(:column.qualify(:table).cast(:type).*(:numeric_column).asc)
1032
+ :other_column.qualify(:table).cast(:type).*(:numeric_column).asc.should_not eql(:column.qualify(:table).cast(:type).*(:numeric_column).asc)
1033
+ end
1034
+
1035
+ specify "should use the same hash value for objects that have the same attributes" do
1036
+ :column.qualify(:table).cast(:type).*(:numeric_column).asc.hash.should == :column.qualify(:table).cast(:type).*(:numeric_column).asc.hash
1037
+ :other_column.qualify(:table).cast(:type).*(:numeric_column).asc.hash.should_not == :column.qualify(:table).cast(:type).*(:numeric_column).asc.hash
1038
+
1039
+ h = {}
1040
+ a = :column.qualify(:table).cast(:type).*(:numeric_column).asc
1041
+ b = :column.qualify(:table).cast(:type).*(:numeric_column).asc
1042
+ h[a] = 1
1043
+ h[b] = 2
1044
+ h[a].should == 2
1045
+ h[b].should == 2
1046
+ end
1047
+ end
1048
+
1049
+ describe "Sequel::SQLTime" do
1050
+ before do
1051
+ @db = Sequel.mock
1052
+ end
1053
+
1054
+ specify ".create should create from hour, minutes, seconds and optional microseconds" do
1055
+ @db.literal(Sequel::SQLTime.create(1, 2, 3)).should == "'01:02:03.000000'"
1056
+ @db.literal(Sequel::SQLTime.create(1, 2, 3, 500000)).should == "'01:02:03.500000'"
1057
+ end
1058
+ end
1059
+
1060
+ describe "Sequel::SQL::Wrapper" do
1061
+ before do
1062
+ @ds = Sequel.mock.dataset
1063
+ end
1064
+
1065
+ specify "should wrap objects so they can be used by the Sequel DSL" do
1066
+ o = Object.new
1067
+ def o.sql_literal(ds) 'foo' end
1068
+ s = Sequel::SQL::Wrapper.new(o)
1069
+ @ds.literal(s).should == "foo"
1070
+ @ds.literal(s+1).should == "(foo + 1)"
1071
+ @ds.literal(s & true).should == "(foo AND 't')"
1072
+ @ds.literal(s < 1).should == "(foo < 1)"
1073
+ @ds.literal(s.sql_subscript(1)).should == "foo[1]"
1074
+ @ds.literal(s.like('a')).should == "(foo LIKE 'a')"
1075
+ @ds.literal(s.as(:a)).should == "foo AS a"
1076
+ @ds.literal(s.cast(Integer)).should == "CAST(foo AS integer)"
1077
+ @ds.literal(s.desc).should == "foo DESC"
1078
+ @ds.literal(s.sql_string + '1').should == "(foo || '1')"
1079
+ end
716
1080
  end
@@ -17,13 +17,16 @@ describe "Sequel Mock Adapter" do
17
17
  called.should be_false
18
18
  end
19
19
 
20
- specify "should return 0 for update and delete by default" do
20
+ specify "should return 0 for update/delete/with_sql_delete/execute_dui by default" do
21
21
  Sequel.mock[:t].update(:a=>1).should == 0
22
22
  Sequel.mock[:t].delete.should == 0
23
+ Sequel.mock[:t].with_sql_delete('DELETE FROM t').should == 0
24
+ Sequel.mock.execute_dui('DELETE FROM t').should == 0
23
25
  end
24
26
 
25
- specify "should return nil for insert by default" do
27
+ specify "should return nil for insert/execute_insert by default" do
26
28
  Sequel.mock[:t].insert(:a=>1).should be_nil
29
+ Sequel.mock.execute_insert('INSERT INTO a () DEFAULT VALUES').should be_nil
27
30
  end
28
31
 
29
32
  specify "should be able to set the rows returned by each using :fetch option with a single hash" do
@@ -299,6 +302,13 @@ describe "Sequel Mock Adapter" do
299
302
  Sequel.mock.send(:quote_identifiers_default).should be_false
300
303
  end
301
304
 
305
+ specify "should allow overriding of server_version" do
306
+ db = Sequel.mock
307
+ db.server_version.should == nil
308
+ db.server_version = 80102
309
+ db.server_version.should == 80102
310
+ end
311
+
302
312
  specify "should not have identifier input/output methods by default" do
303
313
  Sequel.mock.send(:identifier_input_method_default).should be_nil
304
314
  Sequel.mock.send(:identifier_output_method_default).should be_nil
@@ -420,7 +430,7 @@ describe "Sequel Mock Adapter" do
420
430
  Sequel.mock(:host=>'mssql')[:a].full_text_search(:b, 'c').sql.should == "SELECT * FROM [A] WHERE (CONTAINS ([B], 'c'))"
421
431
  Sequel.mock(:host=>'mysql')[:a].full_text_search(:b, 'c').sql.should == "SELECT * FROM `a` WHERE (MATCH (`b`) AGAINST ('c'))"
422
432
  Sequel.mock(:host=>'oracle')[:a].limit(1).sql.should == 'SELECT * FROM (SELECT * FROM "A") "T1" WHERE (ROWNUM <= 1)'
423
- Sequel.mock(:host=>'postgres')[:a].full_text_search(:b, 'c').sql.should == "SELECT * FROM \"a\" WHERE (to_tsvector('simple', (COALESCE(\"b\", ''))) @@ to_tsquery('simple', 'c'))"
433
+ Sequel.mock(:host=>'postgres')[:a].full_text_search(:b, 'c').sql.should == "SELECT * FROM \"a\" WHERE (to_tsvector('simple'::regconfig, (COALESCE(\"b\", ''))) @@ to_tsquery('simple'::regconfig, 'c'))"
424
434
  Sequel.mock(:host=>'sqlite')[:a___b].sql.should == "SELECT * FROM `a` AS 'b'"
425
435
  ensure
426
436
  Sequel.quote_identifiers = qi
@@ -428,4 +438,8 @@ describe "Sequel Mock Adapter" do
428
438
  Sequel::Database.send(:class_variable_set, :@@identifier_output_method, io)
429
439
  end
430
440
  end
441
+
442
+ specify "should automatically set version for postgres" do
443
+ Sequel.mock(:host=>'postgres').server_version.should == 90103
444
+ end
431
445
  end
@@ -227,6 +227,11 @@ describe "DB#create_table" do
227
227
  foreign_key :project_id, :table => :projects, :on_delete => :set_default
228
228
  end
229
229
  @db.sqls.should == ["CREATE TABLE cats (project_id integer REFERENCES projects ON DELETE SET DEFAULT)"]
230
+
231
+ @db.create_table(:cats) do
232
+ foreign_key :project_id, :table => :projects, :on_delete => 'NO ACTION FOO'
233
+ end
234
+ @db.sqls.should == ["CREATE TABLE cats (project_id integer REFERENCES projects ON DELETE NO ACTION FOO)"]
230
235
  end
231
236
 
232
237
  specify "should accept foreign keys with ON UPDATE clause" do
@@ -254,6 +259,11 @@ describe "DB#create_table" do
254
259
  foreign_key :project_id, :table => :projects, :on_update => :set_default
255
260
  end
256
261
  @db.sqls.should == ["CREATE TABLE cats (project_id integer REFERENCES projects ON UPDATE SET DEFAULT)"]
262
+
263
+ @db.create_table(:cats) do
264
+ foreign_key :project_id, :table => :projects, :on_update => 'SET DEFAULT FOO'
265
+ end
266
+ @db.sqls.should == ["CREATE TABLE cats (project_id integer REFERENCES projects ON UPDATE SET DEFAULT FOO)"]
257
267
  end
258
268
 
259
269
  specify "should accept foreign keys with deferrable option" do
@@ -569,6 +579,87 @@ describe "DB#create_table?" do
569
579
  end
570
580
  end
571
581
 
582
+ describe "DB#create_join_table" do
583
+ before do
584
+ @db = Sequel.mock
585
+ end
586
+
587
+ specify "should take a hash with foreign keys and table name values" do
588
+ @db.create_join_table(:cat_id=>:cats, :dog_id=>:dogs)
589
+ @db.sqls.should == ['CREATE TABLE cats_dogs (cat_id integer NOT NULL REFERENCES cats, dog_id integer NOT NULL REFERENCES dogs, PRIMARY KEY (cat_id, dog_id))', 'CREATE INDEX cats_dogs_dog_id_cat_id_index ON cats_dogs (dog_id, cat_id)']
590
+ end
591
+
592
+ specify "should be able to have values be a hash of options" do
593
+ @db.create_join_table(:cat_id=>{:table=>:cats, :null=>true}, :dog_id=>{:table=>:dogs, :default=>0})
594
+ @db.sqls.should == ['CREATE TABLE cats_dogs (cat_id integer NULL REFERENCES cats, dog_id integer DEFAULT 0 NOT NULL REFERENCES dogs, PRIMARY KEY (cat_id, dog_id))', 'CREATE INDEX cats_dogs_dog_id_cat_id_index ON cats_dogs (dog_id, cat_id)']
595
+ end
596
+
597
+ specify "should be able to pass a second hash of table options" do
598
+ @db.create_join_table({:cat_id=>:cats, :dog_id=>:dogs}, :temp=>true)
599
+ @db.sqls.should == ['CREATE TEMPORARY TABLE cats_dogs (cat_id integer NOT NULL REFERENCES cats, dog_id integer NOT NULL REFERENCES dogs, PRIMARY KEY (cat_id, dog_id))', 'CREATE INDEX cats_dogs_dog_id_cat_id_index ON cats_dogs (dog_id, cat_id)']
600
+ end
601
+
602
+ specify "should recognize :name option in table options" do
603
+ @db.create_join_table({:cat_id=>:cats, :dog_id=>:dogs}, :name=>:f)
604
+ @db.sqls.should == ['CREATE TABLE f (cat_id integer NOT NULL REFERENCES cats, dog_id integer NOT NULL REFERENCES dogs, PRIMARY KEY (cat_id, dog_id))', 'CREATE INDEX f_dog_id_cat_id_index ON f (dog_id, cat_id)']
605
+ end
606
+
607
+ specify "should recognize :index_options option in table options" do
608
+ @db.create_join_table({:cat_id=>:cats, :dog_id=>:dogs}, :index_options=>{:name=>:foo_index})
609
+ @db.sqls.should == ['CREATE TABLE cats_dogs (cat_id integer NOT NULL REFERENCES cats, dog_id integer NOT NULL REFERENCES dogs, PRIMARY KEY (cat_id, dog_id))', 'CREATE INDEX foo_index ON cats_dogs (dog_id, cat_id)']
610
+ end
611
+
612
+ specify "should recognize :no_index option in table options" do
613
+ @db.create_join_table({:cat_id=>:cats, :dog_id=>:dogs}, :no_index=>true)
614
+ @db.sqls.should == ['CREATE TABLE cats_dogs (cat_id integer NOT NULL REFERENCES cats, dog_id integer NOT NULL REFERENCES dogs, PRIMARY KEY (cat_id, dog_id))']
615
+ end
616
+
617
+ specify "should recognize :no_primary_key option in table options" do
618
+ @db.create_join_table({:cat_id=>:cats, :dog_id=>:dogs}, :no_primary_key=>true)
619
+ @db.sqls.should == ['CREATE TABLE cats_dogs (cat_id integer NOT NULL REFERENCES cats, dog_id integer NOT NULL REFERENCES dogs)', 'CREATE INDEX cats_dogs_dog_id_cat_id_index ON cats_dogs (dog_id, cat_id)']
620
+ end
621
+
622
+ specify "should raise an error if the hash doesn't have 2 entries with table names" do
623
+ proc{@db.create_join_table({})}.should raise_error(Sequel::Error)
624
+ proc{@db.create_join_table({:cat_id=>:cats})}.should raise_error(Sequel::Error)
625
+ proc{@db.create_join_table({:cat_id=>:cats, :human_id=>:humans, :dog_id=>:dog})}.should raise_error(Sequel::Error)
626
+ proc{@db.create_join_table({:cat_id=>:cats, :dog_id=>{}})}.should raise_error(Sequel::Error)
627
+ end
628
+ end
629
+
630
+ describe "DB#drop_join_table" do
631
+ before do
632
+ @db = Sequel.mock
633
+ end
634
+
635
+ specify "should take a hash with foreign keys and table name values and drop the table" do
636
+ @db.drop_join_table(:cat_id=>:cats, :dog_id=>:dogs)
637
+ @db.sqls.should == ['DROP TABLE cats_dogs']
638
+ end
639
+
640
+ specify "should be able to have values be a hash of options" do
641
+ @db.drop_join_table(:cat_id=>{:table=>:cats, :null=>true}, :dog_id=>{:table=>:dogs, :default=>0})
642
+ @db.sqls.should == ['DROP TABLE cats_dogs']
643
+ end
644
+
645
+ specify "should respect a second hash of table options" do
646
+ @db.drop_join_table({:cat_id=>:cats, :dog_id=>:dogs}, :cascade=>true)
647
+ @db.sqls.should == ['DROP TABLE cats_dogs CASCADE']
648
+ end
649
+
650
+ specify "should respect :name option for table name" do
651
+ @db.drop_join_table({:cat_id=>:cats, :dog_id=>:dogs}, :name=>:f)
652
+ @db.sqls.should == ['DROP TABLE f']
653
+ end
654
+
655
+ specify "should raise an error if the hash doesn't have 2 entries with table names" do
656
+ proc{@db.drop_join_table({})}.should raise_error(Sequel::Error)
657
+ proc{@db.drop_join_table({:cat_id=>:cats})}.should raise_error(Sequel::Error)
658
+ proc{@db.drop_join_table({:cat_id=>:cats, :human_id=>:humans, :dog_id=>:dog})}.should raise_error(Sequel::Error)
659
+ proc{@db.drop_join_table({:cat_id=>:cats, :dog_id=>{}})}.should raise_error(Sequel::Error)
660
+ end
661
+ end
662
+
572
663
  describe "DB#drop_table" do
573
664
  before do
574
665
  @db = Sequel.mock
@@ -590,6 +681,48 @@ describe "DB#drop_table" do
590
681
  end
591
682
  end
592
683
 
684
+ describe "DB#drop_table?" do
685
+ before do
686
+ @db = Sequel.mock
687
+ end
688
+
689
+ specify "should drop the table if it exists" do
690
+ @db.meta_def(:table_exists?){|a| true}
691
+ @db.drop_table?(:cats)
692
+ @db.sqls.should == ["DROP TABLE cats"]
693
+ end
694
+
695
+ specify "should do nothing if the table does not exist" do
696
+ @db.meta_def(:table_exists?){|a| false}
697
+ @db.drop_table?(:cats)
698
+ @db.sqls.should == []
699
+ end
700
+
701
+ specify "should operate on multiple tables at once" do
702
+ @db.meta_def(:table_exists?){|a| a == :cats}
703
+ @db.drop_table? :cats, :dogs
704
+ @db.sqls.should == ['DROP TABLE cats']
705
+ end
706
+
707
+ specify "should take an options hash and support the :cascade option" do
708
+ @db.meta_def(:table_exists?){|a| true}
709
+ @db.drop_table? :cats, :dogs, :cascade=>true
710
+ @db.sqls.should == ['DROP TABLE cats CASCADE', 'DROP TABLE dogs CASCADE']
711
+ end
712
+
713
+ specify "should use IF NOT EXISTS if the database supports that" do
714
+ @db.meta_def(:supports_drop_table_if_exists?){true}
715
+ @db.drop_table? :cats, :dogs
716
+ @db.sqls.should == ['DROP TABLE IF EXISTS cats', 'DROP TABLE IF EXISTS dogs']
717
+ end
718
+
719
+ specify "should use IF NOT EXISTS with CASCADE if the database supports that" do
720
+ @db.meta_def(:supports_drop_table_if_exists?){true}
721
+ @db.drop_table? :cats, :dogs, :cascade=>true
722
+ @db.sqls.should == ['DROP TABLE IF EXISTS cats CASCADE', 'DROP TABLE IF EXISTS dogs CASCADE']
723
+ end
724
+ end
725
+
593
726
  describe "DB#alter_table" do
594
727
  before do
595
728
  @db = Sequel.mock