sequel 3.33.0 → 3.34.0

Sign up to get free protection for your applications and to get access to all the features.
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