sequel 3.21.0 → 3.28.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 (158) hide show
  1. data/CHANGELOG +294 -0
  2. data/README.rdoc +20 -6
  3. data/Rakefile +20 -15
  4. data/doc/association_basics.rdoc +210 -43
  5. data/doc/dataset_basics.rdoc +4 -4
  6. data/doc/mass_assignment.rdoc +54 -0
  7. data/doc/migration.rdoc +15 -538
  8. data/doc/model_hooks.rdoc +64 -27
  9. data/doc/opening_databases.rdoc +37 -10
  10. data/doc/prepared_statements.rdoc +16 -10
  11. data/doc/reflection.rdoc +8 -2
  12. data/doc/release_notes/3.22.0.txt +39 -0
  13. data/doc/release_notes/3.23.0.txt +172 -0
  14. data/doc/release_notes/3.24.0.txt +420 -0
  15. data/doc/release_notes/3.25.0.txt +88 -0
  16. data/doc/release_notes/3.26.0.txt +88 -0
  17. data/doc/release_notes/3.27.0.txt +82 -0
  18. data/doc/release_notes/3.28.0.txt +304 -0
  19. data/doc/schema_modification.rdoc +547 -0
  20. data/doc/testing.rdoc +106 -0
  21. data/doc/transactions.rdoc +97 -0
  22. data/doc/virtual_rows.rdoc +2 -2
  23. data/lib/sequel/adapters/ado.rb +12 -1
  24. data/lib/sequel/adapters/amalgalite.rb +4 -0
  25. data/lib/sequel/adapters/db2.rb +95 -58
  26. data/lib/sequel/adapters/do.rb +12 -0
  27. data/lib/sequel/adapters/firebird.rb +25 -203
  28. data/lib/sequel/adapters/ibmdb.rb +440 -0
  29. data/lib/sequel/adapters/informix.rb +4 -19
  30. data/lib/sequel/adapters/jdbc/as400.rb +0 -7
  31. data/lib/sequel/adapters/jdbc/db2.rb +49 -0
  32. data/lib/sequel/adapters/jdbc/firebird.rb +34 -0
  33. data/lib/sequel/adapters/jdbc/h2.rb +16 -5
  34. data/lib/sequel/adapters/jdbc/informix.rb +31 -0
  35. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  36. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  37. data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
  38. data/lib/sequel/adapters/jdbc/oracle.rb +2 -27
  39. data/lib/sequel/adapters/jdbc/postgresql.rb +6 -0
  40. data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
  41. data/lib/sequel/adapters/jdbc/transactions.rb +34 -0
  42. data/lib/sequel/adapters/jdbc.rb +62 -29
  43. data/lib/sequel/adapters/mysql.rb +22 -139
  44. data/lib/sequel/adapters/mysql2.rb +9 -14
  45. data/lib/sequel/adapters/odbc/db2.rb +21 -0
  46. data/lib/sequel/adapters/odbc.rb +15 -3
  47. data/lib/sequel/adapters/oracle.rb +17 -1
  48. data/lib/sequel/adapters/postgres.rb +111 -16
  49. data/lib/sequel/adapters/shared/access.rb +21 -0
  50. data/lib/sequel/adapters/shared/db2.rb +290 -0
  51. data/lib/sequel/adapters/shared/firebird.rb +214 -0
  52. data/lib/sequel/adapters/shared/informix.rb +45 -0
  53. data/lib/sequel/adapters/shared/mssql.rb +85 -47
  54. data/lib/sequel/adapters/shared/mysql.rb +50 -7
  55. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +143 -0
  56. data/lib/sequel/adapters/shared/oracle.rb +0 -4
  57. data/lib/sequel/adapters/shared/postgres.rb +75 -43
  58. data/lib/sequel/adapters/shared/sqlite.rb +56 -8
  59. data/lib/sequel/adapters/sqlite.rb +12 -11
  60. data/lib/sequel/adapters/swift/mysql.rb +9 -0
  61. data/lib/sequel/adapters/tinytds.rb +139 -7
  62. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +55 -0
  63. data/lib/sequel/ast_transformer.rb +190 -0
  64. data/lib/sequel/connection_pool/threaded.rb +3 -2
  65. data/lib/sequel/connection_pool.rb +1 -1
  66. data/lib/sequel/core.rb +6 -5
  67. data/lib/sequel/database/connecting.rb +5 -5
  68. data/lib/sequel/database/dataset.rb +1 -1
  69. data/lib/sequel/database/dataset_defaults.rb +1 -1
  70. data/lib/sequel/database/logging.rb +1 -1
  71. data/lib/sequel/database/misc.rb +38 -17
  72. data/lib/sequel/database/query.rb +50 -19
  73. data/lib/sequel/database/schema_generator.rb +8 -5
  74. data/lib/sequel/database/schema_methods.rb +52 -27
  75. data/lib/sequel/dataset/actions.rb +167 -48
  76. data/lib/sequel/dataset/features.rb +57 -8
  77. data/lib/sequel/dataset/graph.rb +1 -1
  78. data/lib/sequel/dataset/misc.rb +39 -20
  79. data/lib/sequel/dataset/mutation.rb +3 -3
  80. data/lib/sequel/dataset/prepared_statements.rb +29 -14
  81. data/lib/sequel/dataset/query.rb +182 -32
  82. data/lib/sequel/dataset/sql.rb +31 -58
  83. data/lib/sequel/dataset.rb +8 -0
  84. data/lib/sequel/exceptions.rb +4 -0
  85. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  86. data/lib/sequel/extensions/migration.rb +6 -4
  87. data/lib/sequel/extensions/to_dot.rb +95 -83
  88. data/lib/sequel/model/associations.rb +893 -309
  89. data/lib/sequel/model/base.rb +302 -105
  90. data/lib/sequel/model/errors.rb +1 -1
  91. data/lib/sequel/model/exceptions.rb +5 -1
  92. data/lib/sequel/model.rb +13 -7
  93. data/lib/sequel/plugins/association_pks.rb +22 -4
  94. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  95. data/lib/sequel/plugins/identity_map.rb +113 -6
  96. data/lib/sequel/plugins/many_through_many.rb +67 -5
  97. data/lib/sequel/plugins/prepared_statements.rb +140 -0
  98. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  99. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  100. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  101. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  102. data/lib/sequel/plugins/sharding.rb +12 -20
  103. data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
  104. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  105. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  106. data/lib/sequel/sql.rb +107 -51
  107. data/lib/sequel/timezones.rb +12 -3
  108. data/lib/sequel/version.rb +1 -1
  109. data/spec/adapters/db2_spec.rb +146 -0
  110. data/spec/adapters/mssql_spec.rb +36 -0
  111. data/spec/adapters/mysql_spec.rb +36 -19
  112. data/spec/adapters/postgres_spec.rb +115 -28
  113. data/spec/adapters/spec_helper.rb +6 -0
  114. data/spec/adapters/sqlite_spec.rb +11 -0
  115. data/spec/core/connection_pool_spec.rb +62 -77
  116. data/spec/core/database_spec.rb +244 -287
  117. data/spec/core/dataset_spec.rb +383 -34
  118. data/spec/core/expression_filters_spec.rb +159 -41
  119. data/spec/core/schema_spec.rb +326 -3
  120. data/spec/core/spec_helper.rb +45 -0
  121. data/spec/extensions/association_pks_spec.rb +38 -0
  122. data/spec/extensions/columns_introspection_spec.rb +91 -0
  123. data/spec/extensions/defaults_setter_spec.rb +64 -0
  124. data/spec/extensions/identity_map_spec.rb +162 -0
  125. data/spec/extensions/many_through_many_spec.rb +195 -20
  126. data/spec/extensions/migration_spec.rb +17 -17
  127. data/spec/extensions/nested_attributes_spec.rb +1 -0
  128. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  129. data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
  130. data/spec/extensions/prepared_statements_spec.rb +72 -0
  131. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  132. data/spec/extensions/schema_dumper_spec.rb +2 -2
  133. data/spec/extensions/schema_spec.rb +12 -20
  134. data/spec/extensions/serialization_modification_detection_spec.rb +36 -0
  135. data/spec/extensions/single_table_inheritance_spec.rb +11 -0
  136. data/spec/extensions/spec_helper.rb +3 -1
  137. data/spec/extensions/to_dot_spec.rb +3 -5
  138. data/spec/extensions/xml_serializer_spec.rb +16 -4
  139. data/spec/integration/associations_test.rb +405 -15
  140. data/spec/integration/database_test.rb +4 -2
  141. data/spec/integration/dataset_test.rb +240 -20
  142. data/spec/integration/plugin_test.rb +142 -5
  143. data/spec/integration/prepared_statement_test.rb +174 -95
  144. data/spec/integration/schema_test.rb +128 -16
  145. data/spec/integration/spec_helper.rb +15 -0
  146. data/spec/integration/transaction_test.rb +40 -0
  147. data/spec/integration/type_test.rb +16 -2
  148. data/spec/model/association_reflection_spec.rb +91 -0
  149. data/spec/model/associations_spec.rb +476 -5
  150. data/spec/model/base_spec.rb +91 -1
  151. data/spec/model/eager_loading_spec.rb +519 -31
  152. data/spec/model/hooks_spec.rb +161 -0
  153. data/spec/model/model_spec.rb +89 -2
  154. data/spec/model/plugins_spec.rb +17 -0
  155. data/spec/model/record_spec.rb +184 -12
  156. data/spec/model/spec_helper.rb +5 -0
  157. data/spec/model/validations_spec.rb +11 -0
  158. metadata +85 -34
@@ -2,86 +2,84 @@ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
2
2
 
3
3
  describe "Prepared Statements and Bound Arguments" do
4
4
  before do
5
- INTEGRATION_DB.create_table!(:items) do
5
+ @db = INTEGRATION_DB
6
+ @db.create_table!(:items) do
6
7
  primary_key :id
7
8
  integer :number
8
9
  end
9
10
  @c = Class.new(Sequel::Model(:items))
10
- @ds = INTEGRATION_DB[:items]
11
+ @ds = @db[:items]
11
12
  @ds.insert(:number=>10)
12
- @ds.meta_def(:ba) do |sym|
13
- prepared_arg_placeholder == '$' ? :"#{sym}__int" : sym
14
- end
15
13
  end
16
14
  after do
17
- INTEGRATION_DB.drop_table(:items)
15
+ @db.drop_table(:items)
18
16
  end
19
17
 
20
18
  specify "should support bound variables with select, all, and first" do
21
- @ds.filter(:number=>@ds.ba(:$n)).call(:select, :n=>10).should == [{:id=>1, :number=>10}]
22
- @ds.filter(:number=>@ds.ba(:$n)).call(:all, :n=>10).should == [{:id=>1, :number=>10}]
23
- @ds.filter(:number=>@ds.ba(:$n)).call(:first, :n=>10).should == {:id=>1, :number=>10}
19
+ @ds.filter(:number=>:$n).call(:select, :n=>10).should == [{:id=>1, :number=>10}]
20
+ @ds.filter(:number=>:$n).call(:all, :n=>10).should == [{:id=>1, :number=>10}]
21
+ @ds.filter(:number=>:$n).call(:first, :n=>10).should == {:id=>1, :number=>10}
24
22
  end
25
23
 
26
24
  specify "should support blocks for select and all" do
27
- @ds.filter(:number=>@ds.ba(:$n)).call(:select, :n=>10){|r| r[:number] *= 2}.should == [{:id=>1, :number=>20}]
28
- @ds.filter(:number=>@ds.ba(:$n)).call(:all, :n=>10){|r| r[:number] *= 2}.should == [{:id=>1, :number=>20}]
25
+ @ds.filter(:number=>:$n).call(:select, :n=>10){|r| r[:number] *= 2}.should == [{:id=>1, :number=>20}]
26
+ @ds.filter(:number=>:$n).call(:all, :n=>10){|r| r[:number] *= 2}.should == [{:id=>1, :number=>20}]
29
27
  end
30
28
 
31
29
  specify "should support binding variables before the call with #bind" do
32
- @ds.filter(:number=>@ds.ba(:$n)).bind(:n=>10).call(:select).should == [{:id=>1, :number=>10}]
33
- @ds.filter(:number=>@ds.ba(:$n)).bind(:n=>10).call(:all).should == [{:id=>1, :number=>10}]
34
- @ds.filter(:number=>@ds.ba(:$n)).bind(:n=>10).call(:first).should == {:id=>1, :number=>10}
30
+ @ds.filter(:number=>:$n).bind(:n=>10).call(:select).should == [{:id=>1, :number=>10}]
31
+ @ds.filter(:number=>:$n).bind(:n=>10).call(:all).should == [{:id=>1, :number=>10}]
32
+ @ds.filter(:number=>:$n).bind(:n=>10).call(:first).should == {:id=>1, :number=>10}
35
33
 
36
- @ds.bind(:n=>10).filter(:number=>@ds.ba(:$n)).call(:select).should == [{:id=>1, :number=>10}]
37
- @ds.bind(:n=>10).filter(:number=>@ds.ba(:$n)).call(:all).should == [{:id=>1, :number=>10}]
38
- @ds.bind(:n=>10).filter(:number=>@ds.ba(:$n)).call(:first).should == {:id=>1, :number=>10}
34
+ @ds.bind(:n=>10).filter(:number=>:$n).call(:select).should == [{:id=>1, :number=>10}]
35
+ @ds.bind(:n=>10).filter(:number=>:$n).call(:all).should == [{:id=>1, :number=>10}]
36
+ @ds.bind(:n=>10).filter(:number=>:$n).call(:first).should == {:id=>1, :number=>10}
39
37
  end
40
38
 
41
39
  specify "should allow overriding variables specified with #bind" do
42
- @ds.filter(:number=>@ds.ba(:$n)).bind(:n=>1).call(:select, :n=>10).should == [{:id=>1, :number=>10}]
43
- @ds.filter(:number=>@ds.ba(:$n)).bind(:n=>1).call(:all, :n=>10).should == [{:id=>1, :number=>10}]
44
- @ds.filter(:number=>@ds.ba(:$n)).bind(:n=>1).call(:first, :n=>10).should == {:id=>1, :number=>10}
40
+ @ds.filter(:number=>:$n).bind(:n=>1).call(:select, :n=>10).should == [{:id=>1, :number=>10}]
41
+ @ds.filter(:number=>:$n).bind(:n=>1).call(:all, :n=>10).should == [{:id=>1, :number=>10}]
42
+ @ds.filter(:number=>:$n).bind(:n=>1).call(:first, :n=>10).should == {:id=>1, :number=>10}
45
43
 
46
- @ds.filter(:number=>@ds.ba(:$n)).bind(:n=>1).bind(:n=>10).call(:select).should == [{:id=>1, :number=>10}]
47
- @ds.filter(:number=>@ds.ba(:$n)).bind(:n=>1).bind(:n=>10).call(:all).should == [{:id=>1, :number=>10}]
48
- @ds.filter(:number=>@ds.ba(:$n)).bind(:n=>1).bind(:n=>10).call(:first).should == {:id=>1, :number=>10}
44
+ @ds.filter(:number=>:$n).bind(:n=>1).bind(:n=>10).call(:select).should == [{:id=>1, :number=>10}]
45
+ @ds.filter(:number=>:$n).bind(:n=>1).bind(:n=>10).call(:all).should == [{:id=>1, :number=>10}]
46
+ @ds.filter(:number=>:$n).bind(:n=>1).bind(:n=>10).call(:first).should == {:id=>1, :number=>10}
49
47
  end
50
48
 
51
49
  specify "should support placeholder literal strings with call" do
52
- @ds.filter("number = ?", @ds.ba(:$n)).call(:select, :n=>10).should == [{:id=>1, :number=>10}]
50
+ @ds.filter("number = ?", :$n).call(:select, :n=>10).should == [{:id=>1, :number=>10}]
53
51
  end
54
52
 
55
53
  specify "should support named placeholder literal strings and handle multiple named placeholders correctly with call" do
56
- @ds.filter("number = :n", :n=>@ds.ba(:$n)).call(:select, :n=>10).should == [{:id=>1, :number=>10}]
54
+ @ds.filter("number = :n", :n=>:$n).call(:select, :n=>10).should == [{:id=>1, :number=>10}]
57
55
  @ds.insert(:number=>20)
58
56
  @ds.insert(:number=>30)
59
- @ds.filter("number > :n1 AND number < :n2 AND number = :n3", :n3=>@ds.ba(:$n3), :n2=>@ds.ba(:$n2), :n1=>@ds.ba(:$n1)).call(:select, :n3=>20, :n2=>30, :n1=>10).should == [{:id=>2, :number=>20}]
57
+ @ds.filter("number > :n1 AND number < :n2 AND number = :n3", :n3=>:$n3, :n2=>:$n2, :n1=>:$n1).call(:select, :n3=>20, :n2=>30, :n1=>10).should == [{:id=>2, :number=>20}]
60
58
  end
61
59
 
62
60
  specify "should support datasets with static sql and placeholders with call" do
63
- INTEGRATION_DB["SELECT * FROM items WHERE number = ?", @ds.ba(:$n)].call(:select, :n=>10).should == [{:id=>1, :number=>10}]
61
+ @db["SELECT * FROM items WHERE number = ?", :$n].call(:select, :n=>10).should == [{:id=>1, :number=>10}]
64
62
  end
65
63
 
66
64
  specify "should support subselects with call" do
67
- @ds.filter(:id=>:$i).filter(:number=>@ds.select(:number).filter(:number=>@ds.ba(:$n))).filter(:id=>:$j).call(:select, :n=>10, :i=>1, :j=>1).should == [{:id=>1, :number=>10}]
65
+ @ds.filter(:id=>:$i).filter(:number=>@ds.select(:number).filter(:number=>:$n)).filter(:id=>:$j).call(:select, :n=>10, :i=>1, :j=>1).should == [{:id=>1, :number=>10}]
68
66
  end
69
67
 
70
68
  specify "should support subselects with literal strings with call" do
71
- @ds.filter(:id=>:$i, :number=>@ds.select(:number).filter("number = ?", @ds.ba(:$n))).call(:select, :n=>10, :i=>1).should == [{:id=>1, :number=>10}]
69
+ @ds.filter(:id=>:$i, :number=>@ds.select(:number).filter("number = ?", :$n)).call(:select, :n=>10, :i=>1).should == [{:id=>1, :number=>10}]
72
70
  end
73
71
 
74
72
  specify "should support subselects with static sql and placeholders with call" do
75
- @ds.filter(:id=>:$i, :number=>INTEGRATION_DB["SELECT number FROM items WHERE number = ?", @ds.ba(:$n)]).call(:select, :n=>10, :i=>1).should == [{:id=>1, :number=>10}]
73
+ @ds.filter(:id=>:$i, :number=>@db["SELECT number FROM items WHERE number = ?", :$n]).call(:select, :n=>10, :i=>1).should == [{:id=>1, :number=>10}]
76
74
  end
77
75
 
78
76
  specify "should support subselects of subselects with call" do
79
- @ds.filter(:id=>:$i).filter(:number=>@ds.select(:number).filter(:number=>@ds.select(:number).filter(:number=>@ds.ba(:$n)))).filter(:id=>:$j).call(:select, :n=>10, :i=>1, :j=>1).should == [{:id=>1, :number=>10}]
77
+ @ds.filter(:id=>:$i).filter(:number=>@ds.select(:number).filter(:number=>@ds.select(:number).filter(:number=>:$n))).filter(:id=>:$j).call(:select, :n=>10, :i=>1, :j=>1).should == [{:id=>1, :number=>10}]
80
78
  end
81
79
 
82
- specify "should support using a bound variable for a limit and offset" do
80
+ cspecify "should support using a bound variable for a limit and offset", [:jdbc, :db2] do
83
81
  @ds.insert(:number=>20)
84
- ds = @ds.limit(@ds.ba(:$n), @ds.ba(:$n2)).order(:id)
82
+ ds = @ds.limit(:$n, :$n2).order(:id)
85
83
  ds.call(:select, :n=>1, :n2=>0).should == [{:id=>1, :number=>10}]
86
84
  ds.call(:select, :n=>1, :n2=>1).should == [{:id=>2, :number=>20}]
87
85
  ds.call(:select, :n=>1, :n2=>2).should == []
@@ -90,74 +88,83 @@ describe "Prepared Statements and Bound Arguments" do
90
88
  end
91
89
 
92
90
  specify "should support bound variables with insert" do
93
- @ds.call(:insert, {:n=>20}, :number=>@ds.ba(:$n))
91
+ @ds.call(:insert, {:n=>20}, :number=>:$n)
94
92
  @ds.count.should == 2
95
93
  @ds.order(:id).map(:number).should == [10, 20]
96
94
  end
97
95
 
96
+ specify "should support bound variables with NULL values" do
97
+ @ds.delete
98
+ @ds.call(:insert, {:n=>nil}, :number=>:$n)
99
+ @ds.count.should == 1
100
+ @ds.map(:number).should == [nil]
101
+ end
102
+
98
103
  specify "should have insert return primary key value when using bound arguments" do
99
- @ds.call(:insert, {:n=>20}, :number=>@ds.ba(:$n)).should == 2
104
+ @ds.call(:insert, {:n=>20}, :number=>:$n).should == 2
100
105
  @ds.filter(:id=>2).first[:number].should == 20
101
106
  end
102
107
 
103
108
  specify "should support bound variables with delete" do
104
- @ds.filter(:number=>@ds.ba(:$n)).call(:delete, :n=>10).should == 1
109
+ @ds.filter(:number=>:$n).call(:delete, :n=>10).should == 1
105
110
  @ds.count.should == 0
106
111
  end
107
112
 
108
113
  specify "should support bound variables with update" do
109
- @ds.filter(:number=>@ds.ba(:$n)).call(:update, {:n=>10, :nn=>20}, :number=>:number+@ds.ba(:$nn)).should == 1
114
+ @ds.filter(:number=>:$n).call(:update, {:n=>10, :nn=>20}, :number=>:number+:$nn).should == 1
110
115
  @ds.all.should == [{:id=>1, :number=>30}]
111
116
  end
112
117
 
113
118
  specify "should support prepared statements with select, first, and all" do
114
- @ds.filter(:number=>@ds.ba(:$n)).prepare(:select, :select_n)
115
- INTEGRATION_DB.call(:select_n, :n=>10).should == [{:id=>1, :number=>10}]
116
- @ds.filter(:number=>@ds.ba(:$n)).prepare(:all, :select_n)
117
- INTEGRATION_DB.call(:select_n, :n=>10).should == [{:id=>1, :number=>10}]
118
- @ds.filter(:number=>@ds.ba(:$n)).prepare(:first, :select_n)
119
- INTEGRATION_DB.call(:select_n, :n=>10).should == {:id=>1, :number=>10}
120
- if INTEGRATION_DB.adapter_scheme == :jdbc and INTEGRATION_DB.database_type == :sqlite
121
- # Work around for open prepared statements on a table not allowing the
122
- # dropping of a table when using SQLite over JDBC
123
- INTEGRATION_DB.synchronize{|c| c.prepared_statements[:select_n][1].close}
124
- end
119
+ @ds.filter(:number=>:$n).prepare(:select, :select_n)
120
+ @db.call(:select_n, :n=>10).should == [{:id=>1, :number=>10}]
121
+ @ds.filter(:number=>:$n).prepare(:all, :select_n)
122
+ @db.call(:select_n, :n=>10).should == [{:id=>1, :number=>10}]
123
+ @ds.filter(:number=>:$n).prepare(:first, :select_n)
124
+ @db.call(:select_n, :n=>10).should == {:id=>1, :number=>10}
125
+ end
126
+
127
+ specify "should support prepared statements being call multiple times with different arguments" do
128
+ @ds.filter(:number=>:$n).prepare(:select, :select_n)
129
+ @db.call(:select_n, :n=>10).should == [{:id=>1, :number=>10}]
130
+ @db.call(:select_n, :n=>0).should == []
131
+ @db.call(:select_n, :n=>10).should == [{:id=>1, :number=>10}]
125
132
  end
126
133
 
127
134
  specify "should support placeholder literal strings with prepare" do
128
- @ds.filter("number = ?", @ds.ba(:$n)).prepare(:select, :seq_select).call(:n=>10).should == [{:id=>1, :number=>10}]
135
+ @ds.filter("number = ?", :$n).prepare(:select, :seq_select).call(:n=>10).should == [{:id=>1, :number=>10}]
129
136
  end
130
137
 
131
138
  specify "should support named placeholder literal strings and handle multiple named placeholders correctly with prepare" do
132
- @ds.filter("number = :n", :n=>@ds.ba(:$n)).prepare(:select, :seq_select).call(:n=>10).should == [{:id=>1, :number=>10}]
139
+ @ds.filter("number = :n", :n=>:$n).prepare(:select, :seq_select).call(:n=>10).should == [{:id=>1, :number=>10}]
133
140
  @ds.insert(:number=>20)
134
141
  @ds.insert(:number=>30)
135
- @ds.filter("number > :n1 AND number < :n2 AND number = :n3", :n3=>@ds.ba(:$n3), :n2=>@ds.ba(:$n2), :n1=>@ds.ba(:$n1)).call(:select, :n3=>20, :n2=>30, :n1=>10).should == [{:id=>2, :number=>20}]
142
+ @ds.filter("number > :n1 AND number < :n2 AND number = :n3", :n3=>:$n3, :n2=>:$n2, :n1=>:$n1).call(:select, :n3=>20, :n2=>30, :n1=>10).should == [{:id=>2, :number=>20}]
136
143
  end
137
144
 
138
145
  specify "should support datasets with static sql and placeholders with prepare" do
139
- INTEGRATION_DB["SELECT * FROM items WHERE number = ?", @ds.ba(:$n)].prepare(:select, :seq_select).call(:n=>10).should == [{:id=>1, :number=>10}]
146
+ @db["SELECT * FROM items WHERE number = ?", :$n].prepare(:select, :seq_select).call(:n=>10).should == [{:id=>1, :number=>10}]
140
147
  end
141
148
 
142
149
  specify "should support subselects with prepare" do
143
- @ds.filter(:id=>:$i).filter(:number=>@ds.select(:number).filter(:number=>@ds.ba(:$n))).filter(:id=>:$j).prepare(:select, :seq_select).call(:n=>10, :i=>1, :j=>1).should == [{:id=>1, :number=>10}]
150
+ @ds.filter(:id=>:$i).filter(:number=>@ds.select(:number).filter(:number=>:$n)).filter(:id=>:$j).prepare(:select, :seq_select).call(:n=>10, :i=>1, :j=>1).should == [{:id=>1, :number=>10}]
144
151
  end
145
152
 
146
153
  specify "should support subselects with literal strings with prepare" do
147
- @ds.filter(:id=>:$i, :number=>@ds.select(:number).filter("number = ?", @ds.ba(:$n))).prepare(:select, :seq_select).call(:n=>10, :i=>1).should == [{:id=>1, :number=>10}]
154
+ @ds.filter(:id=>:$i, :number=>@ds.select(:number).filter("number = ?", :$n)).prepare(:select, :seq_select).call(:n=>10, :i=>1).should == [{:id=>1, :number=>10}]
148
155
  end
149
156
 
150
157
  specify "should support subselects with static sql and placeholders with prepare" do
151
- @ds.filter(:id=>:$i, :number=>INTEGRATION_DB["SELECT number FROM items WHERE number = ?", @ds.ba(:$n)]).prepare(:select, :seq_select).call(:n=>10, :i=>1).should == [{:id=>1, :number=>10}]
158
+ @ds.filter(:id=>:$i, :number=>@db["SELECT number FROM items WHERE number = ?", :$n]).prepare(:select, :seq_select).call(:n=>10, :i=>1).should == [{:id=>1, :number=>10}]
152
159
  end
153
160
 
154
161
  specify "should support subselects of subselects with prepare" do
155
- @ds.filter(:id=>:$i).filter(:number=>@ds.select(:number).filter(:number=>@ds.select(:number).filter(:number=>@ds.ba(:$n)))).filter(:id=>:$j).prepare(:select, :seq_select).call(:n=>10, :i=>1, :j=>1).should == [{:id=>1, :number=>10}]
162
+ @ds.filter(:id=>:$i).filter(:number=>@ds.select(:number).filter(:number=>@ds.select(:number).filter(:number=>:$n))).filter(:id=>:$j).prepare(:select, :seq_select).call(:n=>10, :i=>1, :j=>1).should == [{:id=>1, :number=>10}]
156
163
  end
157
164
 
158
- specify "should support using a prepared_statement for a limit and offset" do
165
+ cspecify "should support using a prepared_statement for a limit and offset", :db2 do
159
166
  @ds.insert(:number=>20)
160
- ps = @ds.limit(@ds.ba(:$n), @ds.ba(:$n2)).order(:id).prepare(:select, :seq_select)
167
+ ps = @ds.limit(:$n, :$n2).order(:id).prepare(:select, :seq_select)
161
168
  ps.call(:n=>1, :n2=>0).should == [{:id=>1, :number=>10}]
162
169
  ps.call(:n=>1, :n2=>1).should == [{:id=>2, :number=>20}]
163
170
  ps.call(:n=>1, :n2=>2).should == []
@@ -166,54 +173,58 @@ describe "Prepared Statements and Bound Arguments" do
166
173
  end
167
174
 
168
175
  specify "should support prepared statements with insert" do
169
- @ds.prepare(:insert, :insert_n, :number=>@ds.ba(:$n))
170
- INTEGRATION_DB.call(:insert_n, :n=>20)
176
+ @ds.prepare(:insert, :insert_n, :number=>:$n)
177
+ @db.call(:insert_n, :n=>20)
171
178
  @ds.count.should == 2
172
179
  @ds.order(:id).map(:number).should == [10, 20]
173
180
  end
174
181
 
182
+ specify "should support prepared statements with NULL values" do
183
+ @ds.delete
184
+ @ds.prepare(:insert, :insert_n, :number=>:$n)
185
+ @db.call(:insert_n, :n=>nil)
186
+ @ds.count.should == 1
187
+ @ds.map(:number).should == [nil]
188
+ end
189
+
175
190
  specify "should have insert return primary key value when using prepared statements" do
176
- @ds.prepare(:insert, :insert_n, :number=>@ds.ba(:$n))
177
- INTEGRATION_DB.call(:insert_n, :n=>20).should == 2
191
+ @ds.prepare(:insert, :insert_n, :number=>:$n)
192
+ @db.call(:insert_n, :n=>20).should == 2
178
193
  @ds.filter(:id=>2).first[:number].should == 20
179
194
  end
180
195
 
181
196
  specify "should support prepared statements with delete" do
182
- @ds.filter(:number=>@ds.ba(:$n)).prepare(:delete, :delete_n)
183
- INTEGRATION_DB.call(:delete_n, :n=>10).should == 1
197
+ @ds.filter(:number=>:$n).prepare(:delete, :delete_n)
198
+ @db.call(:delete_n, :n=>10).should == 1
184
199
  @ds.count.should == 0
185
200
  end
186
201
 
187
202
  specify "should support prepared statements with update" do
188
- @ds.filter(:number=>@ds.ba(:$n)).prepare(:update, :update_n, :number=>:number+@ds.ba(:$nn))
189
- INTEGRATION_DB.call(:update_n, :n=>10, :nn=>20).should == 1
203
+ @ds.filter(:number=>:$n).prepare(:update, :update_n, :number=>:number+:$nn)
204
+ @db.call(:update_n, :n=>10, :nn=>20).should == 1
190
205
  @ds.all.should == [{:id=>1, :number=>30}]
191
206
  end
192
207
 
193
208
  specify "model datasets should return model instances when using select, all, and first with bound variables" do
194
- @c.filter(:number=>@ds.ba(:$n)).call(:select, :n=>10).should == [@c.load(:id=>1, :number=>10)]
195
- @c.filter(:number=>@ds.ba(:$n)).call(:all, :n=>10).should == [@c.load(:id=>1, :number=>10)]
196
- @c.filter(:number=>@ds.ba(:$n)).call(:first, :n=>10).should == @c.load(:id=>1, :number=>10)
209
+ @c.filter(:number=>:$n).call(:select, :n=>10).should == [@c.load(:id=>1, :number=>10)]
210
+ @c.filter(:number=>:$n).call(:all, :n=>10).should == [@c.load(:id=>1, :number=>10)]
211
+ @c.filter(:number=>:$n).call(:first, :n=>10).should == @c.load(:id=>1, :number=>10)
197
212
  end
198
213
 
199
214
  specify "model datasets should return model instances when using select, all, and first with prepared statements" do
200
- @c.filter(:number=>@ds.ba(:$n)).prepare(:select, :select_n)
201
- INTEGRATION_DB.call(:select_n, :n=>10).should == [@c.load(:id=>1, :number=>10)]
202
- @c.filter(:number=>@ds.ba(:$n)).prepare(:all, :select_n)
203
- INTEGRATION_DB.call(:select_n, :n=>10).should == [@c.load(:id=>1, :number=>10)]
204
- @c.filter(:number=>@ds.ba(:$n)).prepare(:first, :select_n)
205
- INTEGRATION_DB.call(:select_n, :n=>10).should == @c.load(:id=>1, :number=>10)
206
- if INTEGRATION_DB.adapter_scheme == :jdbc and INTEGRATION_DB.database_type == :sqlite
207
- # Work around for open prepared statements on a table not allowing the
208
- # dropping of a table when using SQLite over JDBC
209
- INTEGRATION_DB.synchronize{|c| c.prepared_statements[:select_n][1].close}
210
- end
215
+ @c.filter(:number=>:$n).prepare(:select, :select_n)
216
+ @db.call(:select_n, :n=>10).should == [@c.load(:id=>1, :number=>10)]
217
+ @c.filter(:number=>:$n).prepare(:all, :select_n)
218
+ @db.call(:select_n, :n=>10).should == [@c.load(:id=>1, :number=>10)]
219
+ @c.filter(:number=>:$n).prepare(:first, :select_n)
220
+ @db.call(:select_n, :n=>10).should == @c.load(:id=>1, :number=>10)
211
221
  end
212
222
  end
213
223
 
214
224
  describe "Bound Argument Types" do
215
225
  before do
216
- INTEGRATION_DB.create_table!(:items) do
226
+ @db = INTEGRATION_DB
227
+ @db.create_table!(:items) do
217
228
  primary_key :id
218
229
  Date :d
219
230
  DateTime :dt
@@ -223,7 +234,7 @@ describe "Bound Argument Types" do
223
234
  Float :f
224
235
  TrueClass :b
225
236
  end
226
- @ds = INTEGRATION_DB[:items]
237
+ @ds = @db[:items]
227
238
  @vs = {:d=>Date.civil(2010, 10, 11), :dt=>DateTime.civil(2010, 10, 12, 13, 14, 15), :f=>1.0, :s=>'str', :t=>Time.at(20101010), :file=>Sequel::SQL::Blob.new('blob'), :b=>true}
228
239
  @ds.insert(@vs)
229
240
  @ds.meta_def(:ba) do |sym, type|
@@ -232,39 +243,107 @@ describe "Bound Argument Types" do
232
243
  end
233
244
  after do
234
245
  Sequel.datetime_class = Time
235
- if INTEGRATION_DB.adapter_scheme == :jdbc && INTEGRATION_DB.database_type == :sqlite
236
- INTEGRATION_DB.synchronize{|c| c.prepared_statements.each{|k, ps| ps[1].close}.clear}
237
- end
238
- INTEGRATION_DB.drop_table(:items)
246
+ @db.drop_table(:items)
239
247
  end
240
248
 
241
249
  cspecify "should handle date type", [:do, :sqlite], :mssql, [:jdbc, :sqlite] do
242
- @ds.filter(:d=>@ds.ba(:$x, :date)).prepare(:first, :ps_date).call(:x=>@vs[:d])[:d].should == @vs[:d]
250
+ @ds.filter(:d=>:$x).prepare(:first, :ps_date).call(:x=>@vs[:d])[:d].should == @vs[:d]
243
251
  end
244
252
 
245
253
  cspecify "should handle datetime type", [:do], [:mysql2], [:swift], [:jdbc, :sqlite], [:tinytds] do
246
254
  Sequel.datetime_class = DateTime
247
- @ds.filter(:dt=>@ds.ba(:$x, :timestamp)).prepare(:first, :ps_datetime).call(:x=>@vs[:dt])[:dt].should == @vs[:dt]
255
+ @ds.filter(:dt=>:$x).prepare(:first, :ps_datetime).call(:x=>@vs[:dt])[:dt].should == @vs[:dt]
248
256
  end
249
257
 
250
258
  cspecify "should handle time type", [:do], [:jdbc, :sqlite] do
251
- @ds.filter(:t=>@ds.ba(:$x, :timestamp)).prepare(:first, :ps_time).call(:x=>@vs[:t])[:t].should == @vs[:t]
259
+ @ds.filter(:t=>:$x).prepare(:first, :ps_time).call(:x=>@vs[:t])[:t].should == @vs[:t]
252
260
  end
253
261
 
254
- cspecify "should handle blob type", [:swift] do
255
- @ds.filter(:file=>@ds.ba(:$x, :bytea)).prepare(:first, :ps_blob).call(:x=>@vs[:file])[:file].should == @vs[:file]
262
+ cspecify "should handle blob type", [:swift], [:odbc], [:jdbc, :db2] do
263
+ @ds.filter(:file=>:$x).prepare(:first, :ps_blob).call(:x=>@vs[:file])[:file].should == @vs[:file]
256
264
  end
257
265
 
258
266
  cspecify "should handle float type", [:swift, :sqlite] do
259
- @ds.filter(:f=>@ds.ba(:$x, :"double precision")).prepare(:first, :ps_float).call(:x=>@vs[:f])[:f].should == @vs[:f]
267
+ @ds.filter(:f=>:$x).prepare(:first, :ps_float).call(:x=>@vs[:f])[:f].should == @vs[:f]
260
268
  end
261
269
 
262
270
  specify "should handle string type" do
263
- @ds.filter(:s=>@ds.ba(:$x, :text)).prepare(:first, :ps_string).call(:x=>@vs[:s])[:s].should == @vs[:s]
271
+ @ds.filter(:s=>:$x).prepare(:first, :ps_string).call(:x=>@vs[:s])[:s].should == @vs[:s]
264
272
  end
265
273
 
266
- cspecify "should handle boolean type", [:do, :sqlite], [:odbc, :mssql], [:jdbc, :sqlite] do
267
- @ds.filter(:b=>@ds.ba(:$x, :boolean)).prepare(:first, :ps_string).call(:x=>@vs[:b])[:b].should == @vs[:b]
274
+ cspecify "should handle boolean type", [:do, :sqlite], [:odbc, :mssql], [:jdbc, :sqlite], [:jdbc, :db2] do
275
+ @ds.filter(:b=>:$x).prepare(:first, :ps_string).call(:x=>@vs[:b])[:b].should == @vs[:b]
268
276
  end
269
277
  end unless INTEGRATION_DB.adapter_scheme == :swift && INTEGRATION_DB.database_type == :postgres
270
278
 
279
+ describe "Dataset#unbind" do
280
+ before do
281
+ @ds = ds = INTEGRATION_DB[:items]
282
+ @ct = proc do |t, v|
283
+ INTEGRATION_DB.create_table!(:items) do
284
+ column :c, t
285
+ end
286
+ ds.insert(:c=>v)
287
+ end
288
+ @u = proc{|ds| ds, bv = ds.unbind; ds.call(:first, bv)}
289
+ end
290
+ after do
291
+ INTEGRATION_DB.drop_table(:items) rescue nil
292
+ end
293
+
294
+ specify "should unbind values assigned to equality and inequality statements" do
295
+ @ct[Integer, 10]
296
+ @u[@ds.filter(:c=>10)].should == {:c=>10}
297
+ @u[@ds.exclude(:c=>10)].should == nil
298
+ @u[@ds.filter{c < 10}].should == nil
299
+ @u[@ds.filter{c <= 10}].should == {:c=>10}
300
+ @u[@ds.filter{c > 10}].should == nil
301
+ @u[@ds.filter{c >= 10}].should == {:c=>10}
302
+ end
303
+
304
+ cspecify "should handle numerics and strings", [:odbc], [:swift, :sqlite] do
305
+ @ct[Integer, 10]
306
+ @u[@ds.filter(:c=>10)].should == {:c=>10}
307
+ @ct[Float, 0.0]
308
+ @u[@ds.filter{c < 1}].should == {:c=>0.0}
309
+ @ct[String, 'foo']
310
+ @u[@ds.filter(:c=>'foo')].should == {:c=>'foo'}
311
+
312
+ INTEGRATION_DB.create_table!(:items) do
313
+ BigDecimal :c, :size=>[15,2]
314
+ end
315
+ @ds.insert(:c=>BigDecimal.new('1.1'))
316
+ @u[@ds.filter{c > 0}].should == {:c=>BigDecimal.new('1.1')}
317
+ end
318
+
319
+ cspecify "should handle dates and times", [:sqlite], [:do], [:jdbc, :mssql], [:tinytds] do
320
+ @ct[Date, Date.today]
321
+ @u[@ds.filter(:c=>Date.today)].should == {:c=>Date.today}
322
+ t = Time.now
323
+ @ct[Time, t]
324
+ @u[@ds.filter{c < t + 1}][:c].to_i.should == t.to_i
325
+ end
326
+
327
+ specify "should handle QualifiedIdentifiers" do
328
+ @ct[Integer, 10]
329
+ @u[@ds.filter{items__c > 1}].should == {:c=>10}
330
+ end
331
+
332
+ specify "should handle deep nesting" do
333
+ INTEGRATION_DB.create_table!(:items) do
334
+ Integer :a
335
+ Integer :b
336
+ Integer :c
337
+ Integer :d
338
+ end
339
+ @ds.insert(:a=>2, :b=>0, :c=>3, :d=>5)
340
+ @u[@ds.filter{a > 1}.and{b < 2}.or(:c=>3).and({~{:d=>4}=>1}.case(0) => 1)].should == {:a=>2, :b=>0, :c=>3, :d=>5}
341
+ @u[@ds.filter{a > 1}.and{b < 2}.or(:c=>3).and({~{:d=>5}=>1}.case(0) => 1)].should == nil
342
+ end
343
+
344
+ specify "should handle case where the same variable has the same value in multiple places " do
345
+ @ct[Integer, 1]
346
+ @u[@ds.filter{c > 1}.or{c < 1}.invert].should == {:c=>1}
347
+ @u[@ds.filter{c > 1}.or{c < 1}].should == nil
348
+ end
349
+ end