viking-sequel 3.10.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 (237) hide show
  1. data/CHANGELOG +3134 -0
  2. data/COPYING +19 -0
  3. data/README.rdoc +723 -0
  4. data/Rakefile +193 -0
  5. data/bin/sequel +196 -0
  6. data/doc/advanced_associations.rdoc +644 -0
  7. data/doc/cheat_sheet.rdoc +218 -0
  8. data/doc/dataset_basics.rdoc +106 -0
  9. data/doc/dataset_filtering.rdoc +158 -0
  10. data/doc/opening_databases.rdoc +296 -0
  11. data/doc/prepared_statements.rdoc +104 -0
  12. data/doc/reflection.rdoc +84 -0
  13. data/doc/release_notes/1.0.txt +38 -0
  14. data/doc/release_notes/1.1.txt +143 -0
  15. data/doc/release_notes/1.3.txt +101 -0
  16. data/doc/release_notes/1.4.0.txt +53 -0
  17. data/doc/release_notes/1.5.0.txt +155 -0
  18. data/doc/release_notes/2.0.0.txt +298 -0
  19. data/doc/release_notes/2.1.0.txt +271 -0
  20. data/doc/release_notes/2.10.0.txt +328 -0
  21. data/doc/release_notes/2.11.0.txt +215 -0
  22. data/doc/release_notes/2.12.0.txt +534 -0
  23. data/doc/release_notes/2.2.0.txt +253 -0
  24. data/doc/release_notes/2.3.0.txt +88 -0
  25. data/doc/release_notes/2.4.0.txt +106 -0
  26. data/doc/release_notes/2.5.0.txt +137 -0
  27. data/doc/release_notes/2.6.0.txt +157 -0
  28. data/doc/release_notes/2.7.0.txt +166 -0
  29. data/doc/release_notes/2.8.0.txt +171 -0
  30. data/doc/release_notes/2.9.0.txt +97 -0
  31. data/doc/release_notes/3.0.0.txt +221 -0
  32. data/doc/release_notes/3.1.0.txt +406 -0
  33. data/doc/release_notes/3.10.0.txt +286 -0
  34. data/doc/release_notes/3.2.0.txt +268 -0
  35. data/doc/release_notes/3.3.0.txt +192 -0
  36. data/doc/release_notes/3.4.0.txt +325 -0
  37. data/doc/release_notes/3.5.0.txt +510 -0
  38. data/doc/release_notes/3.6.0.txt +366 -0
  39. data/doc/release_notes/3.7.0.txt +179 -0
  40. data/doc/release_notes/3.8.0.txt +151 -0
  41. data/doc/release_notes/3.9.0.txt +233 -0
  42. data/doc/schema.rdoc +36 -0
  43. data/doc/sharding.rdoc +113 -0
  44. data/doc/virtual_rows.rdoc +205 -0
  45. data/lib/sequel.rb +1 -0
  46. data/lib/sequel/adapters/ado.rb +90 -0
  47. data/lib/sequel/adapters/ado/mssql.rb +30 -0
  48. data/lib/sequel/adapters/amalgalite.rb +176 -0
  49. data/lib/sequel/adapters/db2.rb +139 -0
  50. data/lib/sequel/adapters/dbi.rb +113 -0
  51. data/lib/sequel/adapters/do.rb +188 -0
  52. data/lib/sequel/adapters/do/mysql.rb +49 -0
  53. data/lib/sequel/adapters/do/postgres.rb +91 -0
  54. data/lib/sequel/adapters/do/sqlite.rb +40 -0
  55. data/lib/sequel/adapters/firebird.rb +283 -0
  56. data/lib/sequel/adapters/informix.rb +77 -0
  57. data/lib/sequel/adapters/jdbc.rb +587 -0
  58. data/lib/sequel/adapters/jdbc/as400.rb +58 -0
  59. data/lib/sequel/adapters/jdbc/h2.rb +133 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
  61. data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
  62. data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
  64. data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
  65. data/lib/sequel/adapters/mysql.rb +421 -0
  66. data/lib/sequel/adapters/odbc.rb +143 -0
  67. data/lib/sequel/adapters/odbc/mssql.rb +42 -0
  68. data/lib/sequel/adapters/openbase.rb +64 -0
  69. data/lib/sequel/adapters/oracle.rb +131 -0
  70. data/lib/sequel/adapters/postgres.rb +504 -0
  71. data/lib/sequel/adapters/shared/mssql.rb +490 -0
  72. data/lib/sequel/adapters/shared/mysql.rb +498 -0
  73. data/lib/sequel/adapters/shared/oracle.rb +195 -0
  74. data/lib/sequel/adapters/shared/postgres.rb +830 -0
  75. data/lib/sequel/adapters/shared/progress.rb +44 -0
  76. data/lib/sequel/adapters/shared/sqlite.rb +389 -0
  77. data/lib/sequel/adapters/sqlite.rb +224 -0
  78. data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
  79. data/lib/sequel/connection_pool.rb +99 -0
  80. data/lib/sequel/connection_pool/sharded_single.rb +84 -0
  81. data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
  82. data/lib/sequel/connection_pool/single.rb +29 -0
  83. data/lib/sequel/connection_pool/threaded.rb +150 -0
  84. data/lib/sequel/core.rb +293 -0
  85. data/lib/sequel/core_sql.rb +241 -0
  86. data/lib/sequel/database.rb +1079 -0
  87. data/lib/sequel/database/schema_generator.rb +327 -0
  88. data/lib/sequel/database/schema_methods.rb +203 -0
  89. data/lib/sequel/database/schema_sql.rb +320 -0
  90. data/lib/sequel/dataset.rb +32 -0
  91. data/lib/sequel/dataset/actions.rb +441 -0
  92. data/lib/sequel/dataset/features.rb +86 -0
  93. data/lib/sequel/dataset/graph.rb +254 -0
  94. data/lib/sequel/dataset/misc.rb +119 -0
  95. data/lib/sequel/dataset/mutation.rb +64 -0
  96. data/lib/sequel/dataset/prepared_statements.rb +227 -0
  97. data/lib/sequel/dataset/query.rb +709 -0
  98. data/lib/sequel/dataset/sql.rb +996 -0
  99. data/lib/sequel/exceptions.rb +51 -0
  100. data/lib/sequel/extensions/blank.rb +43 -0
  101. data/lib/sequel/extensions/inflector.rb +242 -0
  102. data/lib/sequel/extensions/looser_typecasting.rb +21 -0
  103. data/lib/sequel/extensions/migration.rb +239 -0
  104. data/lib/sequel/extensions/named_timezones.rb +61 -0
  105. data/lib/sequel/extensions/pagination.rb +100 -0
  106. data/lib/sequel/extensions/pretty_table.rb +82 -0
  107. data/lib/sequel/extensions/query.rb +52 -0
  108. data/lib/sequel/extensions/schema_dumper.rb +271 -0
  109. data/lib/sequel/extensions/sql_expr.rb +122 -0
  110. data/lib/sequel/extensions/string_date_time.rb +46 -0
  111. data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
  112. data/lib/sequel/metaprogramming.rb +9 -0
  113. data/lib/sequel/model.rb +120 -0
  114. data/lib/sequel/model/associations.rb +1514 -0
  115. data/lib/sequel/model/base.rb +1069 -0
  116. data/lib/sequel/model/default_inflections.rb +45 -0
  117. data/lib/sequel/model/errors.rb +39 -0
  118. data/lib/sequel/model/exceptions.rb +21 -0
  119. data/lib/sequel/model/inflections.rb +162 -0
  120. data/lib/sequel/model/plugins.rb +70 -0
  121. data/lib/sequel/plugins/active_model.rb +59 -0
  122. data/lib/sequel/plugins/association_dependencies.rb +103 -0
  123. data/lib/sequel/plugins/association_proxies.rb +41 -0
  124. data/lib/sequel/plugins/boolean_readers.rb +53 -0
  125. data/lib/sequel/plugins/caching.rb +141 -0
  126. data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
  127. data/lib/sequel/plugins/composition.rb +138 -0
  128. data/lib/sequel/plugins/force_encoding.rb +72 -0
  129. data/lib/sequel/plugins/hook_class_methods.rb +126 -0
  130. data/lib/sequel/plugins/identity_map.rb +116 -0
  131. data/lib/sequel/plugins/instance_filters.rb +98 -0
  132. data/lib/sequel/plugins/instance_hooks.rb +57 -0
  133. data/lib/sequel/plugins/lazy_attributes.rb +77 -0
  134. data/lib/sequel/plugins/many_through_many.rb +208 -0
  135. data/lib/sequel/plugins/nested_attributes.rb +206 -0
  136. data/lib/sequel/plugins/optimistic_locking.rb +81 -0
  137. data/lib/sequel/plugins/rcte_tree.rb +281 -0
  138. data/lib/sequel/plugins/schema.rb +66 -0
  139. data/lib/sequel/plugins/serialization.rb +166 -0
  140. data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
  141. data/lib/sequel/plugins/subclasses.rb +45 -0
  142. data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
  143. data/lib/sequel/plugins/timestamps.rb +87 -0
  144. data/lib/sequel/plugins/touch.rb +118 -0
  145. data/lib/sequel/plugins/typecast_on_load.rb +72 -0
  146. data/lib/sequel/plugins/validation_class_methods.rb +405 -0
  147. data/lib/sequel/plugins/validation_helpers.rb +223 -0
  148. data/lib/sequel/sql.rb +1020 -0
  149. data/lib/sequel/timezones.rb +161 -0
  150. data/lib/sequel/version.rb +12 -0
  151. data/lib/sequel_core.rb +1 -0
  152. data/lib/sequel_model.rb +1 -0
  153. data/spec/adapters/firebird_spec.rb +407 -0
  154. data/spec/adapters/informix_spec.rb +97 -0
  155. data/spec/adapters/mssql_spec.rb +403 -0
  156. data/spec/adapters/mysql_spec.rb +1019 -0
  157. data/spec/adapters/oracle_spec.rb +286 -0
  158. data/spec/adapters/postgres_spec.rb +969 -0
  159. data/spec/adapters/spec_helper.rb +51 -0
  160. data/spec/adapters/sqlite_spec.rb +432 -0
  161. data/spec/core/connection_pool_spec.rb +808 -0
  162. data/spec/core/core_sql_spec.rb +417 -0
  163. data/spec/core/database_spec.rb +1662 -0
  164. data/spec/core/dataset_spec.rb +3827 -0
  165. data/spec/core/expression_filters_spec.rb +595 -0
  166. data/spec/core/object_graph_spec.rb +296 -0
  167. data/spec/core/schema_generator_spec.rb +159 -0
  168. data/spec/core/schema_spec.rb +830 -0
  169. data/spec/core/spec_helper.rb +56 -0
  170. data/spec/core/version_spec.rb +7 -0
  171. data/spec/extensions/active_model_spec.rb +76 -0
  172. data/spec/extensions/association_dependencies_spec.rb +127 -0
  173. data/spec/extensions/association_proxies_spec.rb +50 -0
  174. data/spec/extensions/blank_spec.rb +67 -0
  175. data/spec/extensions/boolean_readers_spec.rb +92 -0
  176. data/spec/extensions/caching_spec.rb +250 -0
  177. data/spec/extensions/class_table_inheritance_spec.rb +252 -0
  178. data/spec/extensions/composition_spec.rb +194 -0
  179. data/spec/extensions/force_encoding_spec.rb +117 -0
  180. data/spec/extensions/hook_class_methods_spec.rb +470 -0
  181. data/spec/extensions/identity_map_spec.rb +202 -0
  182. data/spec/extensions/inflector_spec.rb +181 -0
  183. data/spec/extensions/instance_filters_spec.rb +55 -0
  184. data/spec/extensions/instance_hooks_spec.rb +133 -0
  185. data/spec/extensions/lazy_attributes_spec.rb +153 -0
  186. data/spec/extensions/looser_typecasting_spec.rb +39 -0
  187. data/spec/extensions/many_through_many_spec.rb +884 -0
  188. data/spec/extensions/migration_spec.rb +332 -0
  189. data/spec/extensions/named_timezones_spec.rb +72 -0
  190. data/spec/extensions/nested_attributes_spec.rb +396 -0
  191. data/spec/extensions/optimistic_locking_spec.rb +100 -0
  192. data/spec/extensions/pagination_spec.rb +99 -0
  193. data/spec/extensions/pretty_table_spec.rb +91 -0
  194. data/spec/extensions/query_spec.rb +85 -0
  195. data/spec/extensions/rcte_tree_spec.rb +205 -0
  196. data/spec/extensions/schema_dumper_spec.rb +357 -0
  197. data/spec/extensions/schema_spec.rb +127 -0
  198. data/spec/extensions/serialization_spec.rb +209 -0
  199. data/spec/extensions/single_table_inheritance_spec.rb +96 -0
  200. data/spec/extensions/spec_helper.rb +91 -0
  201. data/spec/extensions/sql_expr_spec.rb +89 -0
  202. data/spec/extensions/string_date_time_spec.rb +93 -0
  203. data/spec/extensions/subclasses_spec.rb +52 -0
  204. data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
  205. data/spec/extensions/thread_local_timezones_spec.rb +45 -0
  206. data/spec/extensions/timestamps_spec.rb +150 -0
  207. data/spec/extensions/touch_spec.rb +155 -0
  208. data/spec/extensions/typecast_on_load_spec.rb +69 -0
  209. data/spec/extensions/validation_class_methods_spec.rb +984 -0
  210. data/spec/extensions/validation_helpers_spec.rb +438 -0
  211. data/spec/integration/associations_test.rb +281 -0
  212. data/spec/integration/database_test.rb +26 -0
  213. data/spec/integration/dataset_test.rb +963 -0
  214. data/spec/integration/eager_loader_test.rb +734 -0
  215. data/spec/integration/model_test.rb +130 -0
  216. data/spec/integration/plugin_test.rb +814 -0
  217. data/spec/integration/prepared_statement_test.rb +213 -0
  218. data/spec/integration/schema_test.rb +361 -0
  219. data/spec/integration/spec_helper.rb +73 -0
  220. data/spec/integration/timezone_test.rb +55 -0
  221. data/spec/integration/transaction_test.rb +122 -0
  222. data/spec/integration/type_test.rb +96 -0
  223. data/spec/model/association_reflection_spec.rb +175 -0
  224. data/spec/model/associations_spec.rb +2633 -0
  225. data/spec/model/base_spec.rb +418 -0
  226. data/spec/model/dataset_methods_spec.rb +78 -0
  227. data/spec/model/eager_loading_spec.rb +1391 -0
  228. data/spec/model/hooks_spec.rb +240 -0
  229. data/spec/model/inflector_spec.rb +26 -0
  230. data/spec/model/model_spec.rb +593 -0
  231. data/spec/model/plugins_spec.rb +236 -0
  232. data/spec/model/record_spec.rb +1500 -0
  233. data/spec/model/spec_helper.rb +97 -0
  234. data/spec/model/validations_spec.rb +153 -0
  235. data/spec/rcov.opts +6 -0
  236. data/spec/spec_config.rb.example +10 -0
  237. metadata +346 -0
@@ -0,0 +1,26 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper.rb')
2
+
3
+ describe Sequel::Database do
4
+ specify "should provide disconnect functionality" do
5
+ INTEGRATION_DB.test_connection
6
+ INTEGRATION_DB.pool.size.should == 1
7
+ INTEGRATION_DB.disconnect
8
+ INTEGRATION_DB.pool.size.should == 0
9
+ end
10
+
11
+ specify "should raise Sequel::DatabaseError on invalid SQL" do
12
+ proc{INTEGRATION_DB << "SELECT"}.should raise_error(Sequel::DatabaseError)
13
+ end
14
+
15
+ specify "should store underlying wrapped exception in Sequel::DatabaseError" do
16
+ begin
17
+ INTEGRATION_DB << "SELECT"
18
+ rescue Sequel::DatabaseError=>e
19
+ e.wrapped_exception.should be_a_kind_of(Exception)
20
+ end
21
+ end
22
+
23
+ specify "should not have the connection pool swallow non-StandardError based exceptions" do
24
+ proc{INTEGRATION_DB.pool.hold{raise Interrupt, "test"}}.should raise_error(Interrupt)
25
+ end
26
+ end
@@ -0,0 +1,963 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper.rb')
2
+
3
+ describe "Simple Dataset operations" do
4
+ before do
5
+ INTEGRATION_DB.create_table!(:items) do
6
+ primary_key :id
7
+ Integer :number
8
+ end
9
+ @ds = INTEGRATION_DB[:items]
10
+ @ds.insert(:number=>10)
11
+ clear_sqls
12
+ end
13
+ after do
14
+ INTEGRATION_DB.drop_table(:items)
15
+ end
16
+
17
+ specify "should support sequential primary keys" do
18
+ @ds << {:number=>20}
19
+ @ds << {:number=>30}
20
+ @ds.order(:number).all.should == [
21
+ {:id => 1, :number=>10},
22
+ {:id => 2, :number=>20},
23
+ {:id => 3, :number=>30} ]
24
+ end
25
+
26
+ cspecify "should insert with a primary key specified", :mssql do
27
+ @ds.insert(:id=>100, :number=>20)
28
+ @ds.count.should == 2
29
+ @ds.order(:id).all.should == [{:id=>1, :number=>10}, {:id=>100, :number=>20}]
30
+ end
31
+
32
+ specify "should have insert return primary key value" do
33
+ @ds.insert(:number=>20).should == 2
34
+ @ds.filter(:id=>2).first[:number].should == 20
35
+ end
36
+
37
+ specify "should delete correctly" do
38
+ @ds.filter(1=>1).delete.should == 1
39
+ @ds.count.should == 0
40
+ end
41
+
42
+ specify "should update correctly" do
43
+ @ds.update(:number=>:number+1).should == 1
44
+ @ds.all.should == [{:id=>1, :number=>11}]
45
+ end
46
+
47
+ cspecify "should have update return the number of matched rows", [:mysql, :mysql], [:do, :mysql], [:ado] do
48
+ @ds.update(:number=>:number).should == 1
49
+ @ds.filter(:id=>1).update(:number=>:number).should == 1
50
+ @ds.filter(:id=>2).update(:number=>:number).should == 0
51
+ @ds.all.should == [{:id=>1, :number=>10}]
52
+ end
53
+
54
+ specify "should fetch all results correctly" do
55
+ @ds.all.should == [{:id=>1, :number=>10}]
56
+ end
57
+
58
+ specify "should fetch a single row correctly" do
59
+ @ds.first.should == {:id=>1, :number=>10}
60
+ end
61
+
62
+ specify "should fetch correctly with a limit" do
63
+ @ds.order(:id).limit(2).all.should == [{:id=>1, :number=>10}]
64
+ @ds.insert(:number=>20)
65
+ @ds.order(:id).limit(1).all.should == [{:id=>1, :number=>10}]
66
+ @ds.order(:id).limit(2).all.should == [{:id=>1, :number=>10}, {:id=>2, :number=>20}]
67
+ end
68
+
69
+ specify "should fetch correctly with a limit and offset" do
70
+ @ds.order(:id).limit(2, 0).all.should == [{:id=>1, :number=>10}]
71
+ @ds.order(:id).limit(2, 1).all.should == []
72
+ @ds.insert(:number=>20)
73
+ @ds.order(:id).limit(1, 1).all.should == [{:id=>2, :number=>20}]
74
+ @ds.order(:id).limit(2, 0).all.should == [{:id=>1, :number=>10}, {:id=>2, :number=>20}]
75
+ @ds.order(:id).limit(2, 1).all.should == [{:id=>2, :number=>20}]
76
+ end
77
+
78
+ cspecify "should fetch correctly with a limit and offset without an order", :mssql do
79
+ @ds.limit(2, 1).all.should == []
80
+ end
81
+
82
+ specify "should alias columns correctly" do
83
+ @ds.select(:id___x, :number___n).first.should == {:x=>1, :n=>10}
84
+ end
85
+ end
86
+
87
+ describe Sequel::Dataset do
88
+ before do
89
+ INTEGRATION_DB.create_table!(:test) do
90
+ String :name
91
+ Integer :value
92
+ end
93
+ @d = INTEGRATION_DB[:test]
94
+ clear_sqls
95
+ end
96
+ after do
97
+ INTEGRATION_DB.drop_table(:test)
98
+ end
99
+
100
+ specify "should return the correct record count" do
101
+ @d.count.should == 0
102
+ @d << {:name => 'abc', :value => 123}
103
+ @d << {:name => 'abc', :value => 456}
104
+ @d << {:name => 'def', :value => 789}
105
+ @d.count.should == 3
106
+ end
107
+
108
+ specify "should handle aggregate methods on limited datasets correctly" do
109
+ @d << {:name => 'abc', :value => 6}
110
+ @d << {:name => 'bcd', :value => 12}
111
+ @d << {:name => 'def', :value => 18}
112
+ @d = @d.order(:name).limit(2)
113
+ @d.count.should == 2
114
+ @d.avg(:value).to_i.should == 9
115
+ @d.min(:value).to_i.should == 6
116
+ @d.reverse.min(:value).to_i.should == 12
117
+ @d.max(:value).to_i.should == 12
118
+ @d.sum(:value).to_i.should == 18
119
+ @d.interval(:value).to_i.should == 6
120
+ end
121
+
122
+ specify "should return the correct records" do
123
+ @d.to_a.should == []
124
+ @d << {:name => 'abc', :value => 123}
125
+ @d << {:name => 'abc', :value => 456}
126
+ @d << {:name => 'def', :value => 789}
127
+
128
+ @d.order(:value).to_a.should == [
129
+ {:name => 'abc', :value => 123},
130
+ {:name => 'abc', :value => 456},
131
+ {:name => 'def', :value => 789}
132
+ ]
133
+ end
134
+
135
+ specify "should update records correctly" do
136
+ @d << {:name => 'abc', :value => 123}
137
+ @d << {:name => 'abc', :value => 456}
138
+ @d << {:name => 'def', :value => 789}
139
+ @d.filter(:name => 'abc').update(:value => 530)
140
+ @d[:name => 'def'][:value].should == 789
141
+ @d.filter(:value => 530).count.should == 2
142
+ end
143
+
144
+ specify "should delete records correctly" do
145
+ @d << {:name => 'abc', :value => 123}
146
+ @d << {:name => 'abc', :value => 456}
147
+ @d << {:name => 'def', :value => 789}
148
+ @d.filter(:name => 'abc').delete
149
+ @d.count.should == 1
150
+ @d.first[:name].should == 'def'
151
+ end
152
+
153
+ specify "should be able to truncate the table" do
154
+ @d << {:name => 'abc', :value => 123}
155
+ @d << {:name => 'abc', :value => 456}
156
+ @d << {:name => 'def', :value => 789}
157
+ @d.count.should == 3
158
+ @d.truncate.should == nil
159
+ @d.count.should == 0
160
+ end
161
+
162
+ specify "should be able to literalize booleans" do
163
+ proc {@d.literal(true)}.should_not raise_error
164
+ proc {@d.literal(false)}.should_not raise_error
165
+ end
166
+ end
167
+
168
+ describe Sequel::Database do
169
+ specify "should correctly escape strings" do
170
+ INTEGRATION_DB.get("\\dingo".as(:a)) == "\\dingo"
171
+ end
172
+
173
+ specify "should correctly escape strings with quotes" do
174
+ INTEGRATION_DB.get("\\'dingo".as(:a)) == "\\'dingo"
175
+ end
176
+
177
+ specify "should properly escape binary data" do
178
+ INTEGRATION_DB.get("\1\2\3".to_sequel_blob.as(:a)) == "\1\2\3"
179
+ end
180
+
181
+ specify "should have a working table_exists?" do
182
+ t = :basdfdsafsaddsaf
183
+ INTEGRATION_DB.drop_table(t) rescue nil
184
+ INTEGRATION_DB.table_exists?(t).should == false
185
+ INTEGRATION_DB.create_table(t){Integer :a}
186
+ INTEGRATION_DB.table_exists?(t).should == true
187
+ end
188
+ end
189
+
190
+ context Sequel::Dataset do
191
+ before do
192
+ INTEGRATION_DB.create_table! :items do
193
+ primary_key :id
194
+ Integer :value
195
+ end
196
+ @d = INTEGRATION_DB[:items]
197
+ @d << {:value => 123}
198
+ @d << {:value => 456}
199
+ @d << {:value => 789}
200
+ end
201
+ after do
202
+ INTEGRATION_DB.drop_table(:items)
203
+ end
204
+
205
+ specify "should correctly return avg" do
206
+ @d.avg(:value).to_i.should == 456
207
+ end
208
+
209
+ specify "should correctly return sum" do
210
+ @d.sum(:value).to_i.should == 1368
211
+ end
212
+
213
+ specify "should correctly return max" do
214
+ @d.max(:value).to_i.should == 789
215
+ end
216
+
217
+ specify "should correctly return min" do
218
+ @d.min(:value).to_i.should == 123
219
+ end
220
+ end
221
+
222
+ describe "Simple Dataset operations" do
223
+ before do
224
+ INTEGRATION_DB.create_table!(:items) do
225
+ Integer :number
226
+ TrueClass :flag
227
+ end
228
+ @ds = INTEGRATION_DB[:items]
229
+ end
230
+ after do
231
+ INTEGRATION_DB.drop_table(:items)
232
+ end
233
+
234
+ specify "should deal with boolean conditions correctly" do
235
+ @ds.insert(:number=>1, :flag=>true)
236
+ @ds.insert(:number=>2, :flag=>false)
237
+ @ds.insert(:number=>3, :flag=>nil)
238
+ @ds.order!(:number)
239
+ @ds.filter(:flag=>true).map(:number).should == [1]
240
+ @ds.filter(:flag=>false).map(:number).should == [2]
241
+ @ds.filter(:flag=>nil).map(:number).should == [3]
242
+ @ds.exclude(:flag=>true).map(:number).should == [2, 3]
243
+ @ds.exclude(:flag=>false).map(:number).should == [1, 3]
244
+ @ds.exclude(:flag=>nil).map(:number).should == [1, 2]
245
+ end
246
+ end
247
+
248
+ describe "Simple Dataset operations in transactions" do
249
+ before do
250
+ INTEGRATION_DB.create_table!(:items_insert_in_transaction) do
251
+ primary_key :id
252
+ integer :number
253
+ end
254
+ @ds = INTEGRATION_DB[:items_insert_in_transaction]
255
+ clear_sqls
256
+ end
257
+ after do
258
+ INTEGRATION_DB.drop_table(:items_insert_in_transaction)
259
+ end
260
+
261
+ cspecify "should insert correctly with a primary key specified inside a transaction", :mssql do
262
+ INTEGRATION_DB.transaction do
263
+ @ds.insert(:id=>100, :number=>20)
264
+ @ds.count.should == 1
265
+ @ds.order(:id).all.should == [{:id=>100, :number=>20}]
266
+ end
267
+ end
268
+
269
+ specify "should have insert return primary key value inside a transaction" do
270
+ INTEGRATION_DB.transaction do
271
+ @ds.insert(:number=>20).should == 1
272
+ @ds.count.should == 1
273
+ @ds.order(:id).all.should == [{:id=>1, :number=>20}]
274
+ end
275
+ end
276
+
277
+ specify "should support for_update" do
278
+ INTEGRATION_DB.transaction{@ds.for_update.all.should == []}
279
+ end
280
+ end
281
+
282
+ describe "Dataset UNION, EXCEPT, and INTERSECT" do
283
+ before do
284
+ INTEGRATION_DB.create_table!(:i1){integer :number}
285
+ INTEGRATION_DB.create_table!(:i2){integer :number}
286
+ @ds1 = INTEGRATION_DB[:i1]
287
+ @ds1.insert(:number=>10)
288
+ @ds1.insert(:number=>20)
289
+ @ds2 = INTEGRATION_DB[:i2]
290
+ @ds2.insert(:number=>10)
291
+ @ds2.insert(:number=>30)
292
+ clear_sqls
293
+ end
294
+
295
+ specify "should give the correct results for simple UNION, EXCEPT, and INTERSECT" do
296
+ @ds1.union(@ds2).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 30'
297
+ if @ds1.supports_intersect_except?
298
+ @ds1.except(@ds2).order(:number).map{|x| x[:number].to_s}.should == %w'20'
299
+ @ds1.intersect(@ds2).order(:number).map{|x| x[:number].to_s}.should == %w'10'
300
+ end
301
+ end
302
+
303
+ cspecify "should give the correct results for UNION, EXCEPT, and INTERSECT when used with ordering and limits", :mssql do
304
+ @ds1.insert(:number=>8)
305
+ @ds2.insert(:number=>9)
306
+ @ds1.insert(:number=>38)
307
+ @ds2.insert(:number=>39)
308
+
309
+ @ds1.order(:number.desc).union(@ds2).order(:number).map{|x| x[:number].to_s}.should == %w'8 9 10 20 30 38 39'
310
+ @ds1.union(@ds2.order(:number.desc)).order(:number).map{|x| x[:number].to_s}.should == %w'8 9 10 20 30 38 39'
311
+
312
+ @ds1.order(:number.desc).limit(1).union(@ds2).order(:number).map{|x| x[:number].to_s}.should == %w'9 10 30 38 39'
313
+ @ds2.order(:number.desc).limit(1).union(@ds1).order(:number).map{|x| x[:number].to_s}.should == %w'8 10 20 38 39'
314
+
315
+ @ds1.union(@ds2.order(:number).limit(1)).order(:number).map{|x| x[:number].to_s}.should == %w'8 9 10 20 38'
316
+ @ds2.union(@ds1.order(:number).limit(1)).order(:number).map{|x| x[:number].to_s}.should == %w'8 9 10 30 39'
317
+
318
+ @ds1.union(@ds2).limit(2).order(:number).map{|x| x[:number].to_s}.should == %w'8 9'
319
+ @ds2.union(@ds1).order(:number.desc).limit(2).map{|x| x[:number].to_s}.should == %w'39 38'
320
+
321
+ @ds1.order(:number.desc).limit(2).union(@ds2.order(:number.desc).limit(2)).order(:number).limit(3).map{|x| x[:number].to_s}.should == %w'20 30 38'
322
+ @ds2.order(:number).limit(2).union(@ds1.order(:number).limit(2)).order(:number.desc).limit(3).map{|x| x[:number].to_s}.should == %w'10 9 8'
323
+ end
324
+
325
+ specify "should give the correct results for compound UNION, EXCEPT, and INTERSECT" do
326
+ INTEGRATION_DB.create_table!(:i3){integer :number}
327
+ @ds3 = INTEGRATION_DB[:i3]
328
+ @ds3.insert(:number=>10)
329
+ @ds3.insert(:number=>40)
330
+
331
+ @ds1.union(@ds2).union(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 30 40'
332
+ @ds1.union(@ds2.union(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 30 40'
333
+ if @ds1.supports_intersect_except?
334
+ @ds1.union(@ds2).except(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'20 30'
335
+ @ds1.union(@ds2.except(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 30'
336
+ @ds1.union(@ds2).intersect(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'10 '
337
+ @ds1.union(@ds2.intersect(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'10 20'
338
+
339
+ @ds1.except(@ds2).union(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 40'
340
+ @ds1.except(@ds2.union(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'20'
341
+ @ds1.except(@ds2).except(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'20'
342
+ @ds1.except(@ds2.except(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'10 20'
343
+ @ds1.except(@ds2).intersect(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w''
344
+ @ds1.except(@ds2.intersect(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'20'
345
+
346
+ @ds1.intersect(@ds2).union(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'10 40'
347
+ @ds1.intersect(@ds2.union(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'10'
348
+ @ds1.intersect(@ds2).except(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w''
349
+ @ds1.intersect(@ds2.except(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w''
350
+ @ds1.intersect(@ds2).intersect(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'10'
351
+ @ds1.intersect(@ds2.intersect(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'10'
352
+ end
353
+ end
354
+ end
355
+
356
+ if INTEGRATION_DB.dataset.supports_cte?
357
+ describe "Common Table Expressions" do
358
+ before do
359
+ @db = INTEGRATION_DB
360
+ @db.create_table!(:i1){Integer :id; Integer :parent_id}
361
+ @ds = @db[:i1]
362
+ @ds.insert(:id=>1)
363
+ @ds.insert(:id=>2)
364
+ @ds.insert(:id=>3, :parent_id=>1)
365
+ @ds.insert(:id=>4, :parent_id=>1)
366
+ @ds.insert(:id=>5, :parent_id=>3)
367
+ @ds.insert(:id=>6, :parent_id=>5)
368
+ end
369
+ after do
370
+ @db.drop_table(:i1)
371
+ end
372
+
373
+ specify "should give correct results for WITH" do
374
+ @db[:t].with(:t, @ds.filter(:parent_id=>nil).select(:id)).order(:id).map(:id).should == [1, 2]
375
+ end
376
+
377
+ specify "should give correct results for recursive WITH" do
378
+ ds = @db[:t].select(:i___id, :pi___parent_id).with_recursive(:t, @ds.filter(:parent_id=>nil), @ds.join(:t, :i=>:parent_id).select(:i1__id, :i1__parent_id), :args=>[:i, :pi])
379
+ ds.all.should == [{:parent_id=>nil, :id=>1}, {:parent_id=>nil, :id=>2}, {:parent_id=>1, :id=>3}, {:parent_id=>1, :id=>4}, {:parent_id=>3, :id=>5}, {:parent_id=>5, :id=>6}]
380
+ ps = @db[:t].select(:i___id, :pi___parent_id).with_recursive(:t, @ds.filter(:parent_id=>:$n), @ds.join(:t, :i=>:parent_id).filter(:t__i=>:parent_id).select(:i1__id, :i1__parent_id), :args=>[:i, :pi]).prepare(:select, :cte_sel)
381
+ ps.call(:n=>1).should == [{:id=>3, :parent_id=>1}, {:id=>4, :parent_id=>1}, {:id=>5, :parent_id=>3}, {:id=>6, :parent_id=>5}]
382
+ ps.call(:n=>3).should == [{:id=>5, :parent_id=>3}, {:id=>6, :parent_id=>5}]
383
+ ps.call(:n=>5).should == [{:id=>6, :parent_id=>5}]
384
+ end
385
+ end
386
+ end
387
+
388
+ if INTEGRATION_DB.dataset.supports_window_functions?
389
+ describe "Window Functions" do
390
+ before do
391
+ @db = INTEGRATION_DB
392
+ @db.create_table!(:i1){Integer :id; Integer :group_id; Integer :amount}
393
+ @ds = @db[:i1].order(:id)
394
+ @ds.insert(:id=>1, :group_id=>1, :amount=>1)
395
+ @ds.insert(:id=>2, :group_id=>1, :amount=>10)
396
+ @ds.insert(:id=>3, :group_id=>1, :amount=>100)
397
+ @ds.insert(:id=>4, :group_id=>2, :amount=>1000)
398
+ @ds.insert(:id=>5, :group_id=>2, :amount=>10000)
399
+ @ds.insert(:id=>6, :group_id=>2, :amount=>100000)
400
+ end
401
+ after do
402
+ @db.drop_table(:i1)
403
+ end
404
+
405
+ specify "should give correct results for aggregate window functions" do
406
+ @ds.select(:id){sum(:over, :args=>amount, :partition=>group_id){}.as(:sum)}.all.should ==
407
+ [{:sum=>111, :id=>1}, {:sum=>111, :id=>2}, {:sum=>111, :id=>3}, {:sum=>111000, :id=>4}, {:sum=>111000, :id=>5}, {:sum=>111000, :id=>6}]
408
+ @ds.select(:id){sum(:over, :args=>amount){}.as(:sum)}.all.should ==
409
+ [{:sum=>111111, :id=>1}, {:sum=>111111, :id=>2}, {:sum=>111111, :id=>3}, {:sum=>111111, :id=>4}, {:sum=>111111, :id=>5}, {:sum=>111111, :id=>6}]
410
+ end
411
+
412
+ specify "should give correct results for ranking window functions with orders" do
413
+ @ds.select(:id){rank(:over, :partition=>group_id, :order=>id){}.as(:rank)}.all.should ==
414
+ [{:rank=>1, :id=>1}, {:rank=>2, :id=>2}, {:rank=>3, :id=>3}, {:rank=>1, :id=>4}, {:rank=>2, :id=>5}, {:rank=>3, :id=>6}]
415
+ @ds.select(:id){rank(:over, :order=>id){}.as(:rank)}.all.should ==
416
+ [{:rank=>1, :id=>1}, {:rank=>2, :id=>2}, {:rank=>3, :id=>3}, {:rank=>4, :id=>4}, {:rank=>5, :id=>5}, {:rank=>6, :id=>6}]
417
+ end
418
+
419
+ cspecify "should give correct results for aggregate window functions with orders", :mssql do
420
+ @ds.select(:id){sum(:over, :args=>amount, :partition=>group_id, :order=>id){}.as(:sum)}.all.should ==
421
+ [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1000, :id=>4}, {:sum=>11000, :id=>5}, {:sum=>111000, :id=>6}]
422
+ @ds.select(:id){sum(:over, :args=>amount, :order=>id){}.as(:sum)}.all.should ==
423
+ [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1111, :id=>4}, {:sum=>11111, :id=>5}, {:sum=>111111, :id=>6}]
424
+ end
425
+
426
+ cspecify "should give correct results for aggregate window functions with frames", :mssql do
427
+ @ds.select(:id){sum(:over, :args=>amount, :partition=>group_id, :order=>id, :frame=>:all){}.as(:sum)}.all.should ==
428
+ [{:sum=>111, :id=>1}, {:sum=>111, :id=>2}, {:sum=>111, :id=>3}, {:sum=>111000, :id=>4}, {:sum=>111000, :id=>5}, {:sum=>111000, :id=>6}]
429
+ @ds.select(:id){sum(:over, :args=>amount, :partition=>group_id, :frame=>:all){}.as(:sum)}.all.should ==
430
+ [{:sum=>111, :id=>1}, {:sum=>111, :id=>2}, {:sum=>111, :id=>3}, {:sum=>111000, :id=>4}, {:sum=>111000, :id=>5}, {:sum=>111000, :id=>6}]
431
+ @ds.select(:id){sum(:over, :args=>amount, :order=>id, :frame=>:all){}.as(:sum)}.all.should ==
432
+ [{:sum=>111111, :id=>1}, {:sum=>111111, :id=>2}, {:sum=>111111, :id=>3}, {:sum=>111111, :id=>4}, {:sum=>111111, :id=>5}, {:sum=>111111, :id=>6}]
433
+ @ds.select(:id){sum(:over, :args=>amount, :frame=>:all){}.as(:sum)}.all.should ==
434
+ [{:sum=>111111, :id=>1}, {:sum=>111111, :id=>2}, {:sum=>111111, :id=>3}, {:sum=>111111, :id=>4}, {:sum=>111111, :id=>5}, {:sum=>111111, :id=>6}]
435
+
436
+ @ds.select(:id){sum(:over, :args=>amount, :partition=>group_id, :order=>id, :frame=>:rows){}.as(:sum)}.all.should ==
437
+ [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1000, :id=>4}, {:sum=>11000, :id=>5}, {:sum=>111000, :id=>6}]
438
+ @ds.select(:id){sum(:over, :args=>amount, :partition=>group_id, :frame=>:rows){}.as(:sum)}.all.should ==
439
+ [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1000, :id=>4}, {:sum=>11000, :id=>5}, {:sum=>111000, :id=>6}]
440
+ @ds.select(:id){sum(:over, :args=>amount, :order=>id, :frame=>:rows){}.as(:sum)}.all.should ==
441
+ [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1111, :id=>4}, {:sum=>11111, :id=>5}, {:sum=>111111, :id=>6}]
442
+ @ds.select(:id){sum(:over, :args=>amount, :frame=>:rows){}.as(:sum)}.all.should ==
443
+ [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1111, :id=>4}, {:sum=>11111, :id=>5}, {:sum=>111111, :id=>6}]
444
+ end
445
+ end
446
+ end
447
+
448
+ describe Sequel::SQL::Constants do
449
+ before do
450
+ @db = INTEGRATION_DB
451
+ @ds = @db[:constants]
452
+ @c = proc do |v|
453
+ case v
454
+ when Time
455
+ v
456
+ when DateTime, String
457
+ Time.parse(v.to_s)
458
+ else
459
+ v
460
+ end
461
+ end
462
+ @c2 = proc{|v| v.is_a?(Date) ? v : Date.parse(v) }
463
+ end
464
+ after do
465
+ @db.drop_table(:constants)
466
+ end
467
+
468
+ cspecify "should have working CURRENT_DATE", [:odbc, :mssql], [:jdbc, :sqlite] do
469
+ @db.create_table!(:constants){Date :d}
470
+ @ds.insert(:d=>Sequel::CURRENT_DATE)
471
+ Date.today.should == @c2[@ds.get(:d)]
472
+ end
473
+
474
+ cspecify "should have working CURRENT_TIME", [:do, :mysql], [:jdbc, :sqlite] do
475
+ @db.create_table!(:constants){Time :t, :only_time=>true}
476
+ @ds.insert(:t=>Sequel::CURRENT_TIME)
477
+ (Time.now - @c[@ds.get(:t)]).should be_close(0, 1)
478
+ end
479
+
480
+ cspecify "should have working CURRENT_TIMESTAMP", [:jdbc, :sqlite] do
481
+ @db.create_table!(:constants){DateTime :ts}
482
+ @ds.insert(:ts=>Sequel::CURRENT_TIMESTAMP)
483
+ (Time.now - @c[@ds.get(:ts)]).should be_close(0, 1)
484
+ end
485
+ end
486
+
487
+ describe "Sequel::Dataset#import and #multi_insert" do
488
+ before do
489
+ @db = INTEGRATION_DB
490
+ @db.create_table!(:imp){Integer :i}
491
+ @db.create_table!(:exp2){Integer :i}
492
+ @ids = @db[:imp].order(:i)
493
+ @eds = @db[:exp2]
494
+ end
495
+ after do
496
+ @db.drop_table(:imp, :exp2)
497
+ end
498
+
499
+ it "should import with multi_insert and an array of hashes" do
500
+ @ids.multi_insert([{:i=>10}, {:i=>20}])
501
+ @ids.all.should == [{:i=>10}, {:i=>20}]
502
+ end
503
+
504
+ it "should import with an array of arrays of values" do
505
+ @ids.import([:i], [[10], [20]])
506
+ @ids.all.should == [{:i=>10}, {:i=>20}]
507
+ end
508
+
509
+ it "should import with a dataset" do
510
+ @eds.import([:i], [[10], [20]])
511
+ @ids.import([:i], @eds)
512
+ @ids.all.should == [{:i=>10}, {:i=>20}]
513
+ end
514
+
515
+ it "should have import work with the :slice_size option" do
516
+ @ids.import([:i], [[10], [20], [30]], :slice_size=>1)
517
+ @ids.all.should == [{:i=>10}, {:i=>20}, {:i=>30}]
518
+ @ids.delete
519
+ @ids.import([:i], [[10], [20], [30]], :slice_size=>2)
520
+ @ids.all.should == [{:i=>10}, {:i=>20}, {:i=>30}]
521
+ @ids.delete
522
+ @ids.import([:i], [[10], [20], [30]], :slice_size=>3)
523
+ @ids.all.should == [{:i=>10}, {:i=>20}, {:i=>30}]
524
+ end
525
+ end
526
+
527
+ describe "Sequel::Dataset convenience methods" do
528
+ before do
529
+ @db = INTEGRATION_DB
530
+ @db.create_table!(:a){Integer :a; Integer :b}
531
+ @ds = @db[:a].order(:a)
532
+ end
533
+ after do
534
+ @db.drop_table(:a)
535
+ end
536
+
537
+ it "#[]= should update matching rows" do
538
+ @ds.insert(20, 10)
539
+ @ds[:a=>20] = {:b=>30}
540
+ @ds.all.should == [{:a=>20, :b=>30}]
541
+ end
542
+
543
+ it "#empty? should return whether the dataset returns no rows" do
544
+ @ds.empty?.should == true
545
+ @ds.insert(20, 10)
546
+ @ds.empty?.should == false
547
+ end
548
+
549
+ it "#group_and_count should return a grouping by count" do
550
+ @ds.group_and_count(:a).order(:count).all.should == []
551
+ @ds.insert(20, 10)
552
+ @ds.group_and_count(:a).order(:count).all.each{|h| h[:count] = h[:count].to_i}.should == [{:a=>20, :count=>1}]
553
+ @ds.insert(20, 30)
554
+ @ds.group_and_count(:a).order(:count).all.each{|h| h[:count] = h[:count].to_i}.should == [{:a=>20, :count=>2}]
555
+ @ds.insert(30, 30)
556
+ @ds.group_and_count(:a).order(:count).all.each{|h| h[:count] = h[:count].to_i}.should == [{:a=>30, :count=>1}, {:a=>20, :count=>2}]
557
+ end
558
+
559
+ it "#group_and_count should support column aliases" do
560
+ @ds.group_and_count(:a___c).order(:count).all.should == []
561
+ @ds.insert(20, 10)
562
+ @ds.group_and_count(:a___c).order(:count).all.each{|h| h[:count] = h[:count].to_i}.should == [{:c=>20, :count=>1}]
563
+ @ds.insert(20, 30)
564
+ @ds.group_and_count(:a___c).order(:count).all.each{|h| h[:count] = h[:count].to_i}.should == [{:c=>20, :count=>2}]
565
+ @ds.insert(30, 30)
566
+ @ds.group_and_count(:a___c).order(:count).all.each{|h| h[:count] = h[:count].to_i}.should == [{:c=>30, :count=>1}, {:c=>20, :count=>2}]
567
+ end
568
+
569
+ cspecify "#range should return the range between the maximum and minimum values", :sqlite do
570
+ @ds = @ds.unordered
571
+ @ds.insert(20, 10)
572
+ @ds.insert(30, 10)
573
+ @ds.range(:a).should == (20..30)
574
+ @ds.range(:b).should == (10..10)
575
+ end
576
+
577
+ it "#interval should return the different between the maximum and minimum values" do
578
+ @ds = @ds.unordered
579
+ @ds.insert(20, 10)
580
+ @ds.insert(30, 10)
581
+ @ds.interval(:a).to_i.should == 10
582
+ @ds.interval(:b).to_i.should == 0
583
+ end
584
+ end
585
+
586
+ describe "Sequel::Dataset main SQL methods" do
587
+ before do
588
+ @db = INTEGRATION_DB
589
+ @db.create_table!(:a){Integer :a; Integer :b}
590
+ @ds = @db[:a].order(:a)
591
+ end
592
+ after do
593
+ @db.drop_table(:a)
594
+ end
595
+
596
+ it "#exists should return a usable exists clause" do
597
+ @ds.filter(@db[:a___c].filter(:c__a=>:a__b).exists).all.should == []
598
+ @ds.insert(20, 30)
599
+ @ds.insert(10, 20)
600
+ @ds.filter(@db[:a___c].filter(:c__a=>:a__b).exists).all.should == [{:a=>10, :b=>20}]
601
+ end
602
+
603
+ it "#filter and #exclude should work with placeholder strings" do
604
+ @ds.insert(20, 30)
605
+ @ds.filter("a > ?", 15).all.should == [{:a=>20, :b=>30}]
606
+ @ds.exclude("b < ?", 15).all.should == [{:a=>20, :b=>30}]
607
+ @ds.filter("b < ?", 15).invert.all.should == [{:a=>20, :b=>30}]
608
+ end
609
+
610
+ it "#and and #or should work correctly" do
611
+ @ds.insert(20, 30)
612
+ @ds.filter(:a=>20).and(:b=>30).all.should == [{:a=>20, :b=>30}]
613
+ @ds.filter(:a=>20).and(:b=>15).all.should == []
614
+ @ds.filter(:a=>20).or(:b=>15).all.should == [{:a=>20, :b=>30}]
615
+ @ds.filter(:a=>10).or(:b=>15).all.should == []
616
+ end
617
+
618
+ it "#having should work correctly" do
619
+ @ds.unordered!
620
+ @ds.select{[b, max(a).as(c)]}.group(:b).having{max(a) > 30}.all.should == []
621
+ @ds.insert(20, 30)
622
+ @ds.select{[b, max(a).as(c)]}.group(:b).having{max(a) > 30}.all.should == []
623
+ @ds.insert(40, 20)
624
+ @ds.select{[b, max(a).as(c)]}.group(:b).having{max(a) > 30}.all.each{|h| h[:c] = h[:c].to_i}.should == [{:b=>20, :c=>40}]
625
+ end
626
+
627
+ cspecify "#having should work without a previous group", :sqlite do
628
+ @ds.unordered!
629
+ @ds.select{max(a).as(c)}.having{max(a) > 30}.all.should == []
630
+ @ds.insert(20, 30)
631
+ @ds.select{max(a).as(c)}.having{max(a) > 30}.all.should == []
632
+ @ds.insert(40, 20)
633
+ @ds.select{max(a).as(c)}.having{max(a) > 30}.all.each{|h| h[:c] = h[:c].to_i}.should == [{:c=>40}]
634
+ end
635
+ end
636
+
637
+ describe "Sequel::Dataset DSL support" do
638
+ before do
639
+ @db = INTEGRATION_DB
640
+ @db.create_table!(:a){Integer :a; Integer :b}
641
+ @ds = @db[:a].order(:a)
642
+ end
643
+ after do
644
+ @db.drop_table(:a)
645
+ end
646
+
647
+ it "should work with standard mathematical operators" do
648
+ @ds.insert(20, 10)
649
+ @ds.get{a + b}.to_i.should == 30
650
+ @ds.get{a - b}.to_i.should == 10
651
+ @ds.get{a * b}.to_i.should == 200
652
+ @ds.get{a / b}.to_i.should == 2
653
+ end
654
+
655
+ cspecify "should work with standard bitwise mathematical operators", :mssql, :h2 do
656
+ @ds.insert(24, 2)
657
+ @ds.get{a.sql_number << b}.to_i.should == 96
658
+ @ds.get{a.sql_number >> b}.to_i.should == 6
659
+ @ds.delete
660
+ @ds.insert(3, 5)
661
+ @ds.get{a.sql_number | b}.to_i.should == 7
662
+ @ds.get{a.sql_number & b}.to_i.should == 1
663
+ end
664
+
665
+ cspecify "should work with the bitwise compliment operator", :mysql, :h2 do
666
+ @ds.insert(3, 5)
667
+ @ds.get{~a.sql_number}.to_i.should == -4
668
+ end
669
+
670
+ cspecify "should work with inequality operators", :mssql do
671
+ @ds.insert(20, 20)
672
+ ['0', 0, false].should include(@ds.get{a > b})
673
+ ['0', 0, false].should include(@ds.get{a < b})
674
+ ['1', 1, true].should include(@ds.get{a <= b})
675
+ ['1', 1, true].should include(@ds.get{a >= b})
676
+ end
677
+
678
+ cspecify "should work with casting and string concatentation", :mssql do
679
+ @ds.insert(20, 20)
680
+ @ds.get{a.cast_string + b}.should == '2020'
681
+ end
682
+
683
+ it "should work with ordering" do
684
+ @ds.insert(10, 20)
685
+ @ds.insert(20, 10)
686
+ @ds.order(:a, :b).all.should == [{:a=>10, :b=>20}, {:a=>20, :b=>10}]
687
+ @ds.order(:a.asc, :b.asc).all.should == [{:a=>10, :b=>20}, {:a=>20, :b=>10}]
688
+ @ds.order(:a.desc, :b.desc).all.should == [{:a=>20, :b=>10}, {:a=>10, :b=>20}]
689
+ end
690
+
691
+ it "should work with qualifying" do
692
+ @ds.insert(10, 20)
693
+ @ds.get(:a__b).should == 20
694
+ @ds.get{a__b}.should == 20
695
+ @ds.get(:b.qualify(:a)).should == 20
696
+ end
697
+
698
+ it "should work with aliasing" do
699
+ @ds.insert(10, 20)
700
+ @ds.get(:a__b___c).should == 20
701
+ @ds.get{a__b.as(c)}.should == 20
702
+ @ds.get(:b.qualify(:a).as(:c)).should == 20
703
+ @ds.get(:b.as(:c)).should == 20
704
+ end
705
+
706
+ it "should work with selecting all columns of a table" do
707
+ @ds.insert(20, 10)
708
+ @ds.select(:a.*).all.should == [{:a=>20, :b=>10}]
709
+ end
710
+
711
+ it "should work with ranges as hash values" do
712
+ @ds.insert(20, 10)
713
+ @ds.filter(:a=>(10..30)).all.should == [{:a=>20, :b=>10}]
714
+ @ds.filter(:a=>(25..30)).all.should == []
715
+ @ds.filter(:a=>(10..15)).all.should == []
716
+ @ds.exclude(:a=>(10..30)).all.should == []
717
+ @ds.exclude(:a=>(25..30)).all.should == [{:a=>20, :b=>10}]
718
+ @ds.exclude(:a=>(10..15)).all.should == [{:a=>20, :b=>10}]
719
+ end
720
+
721
+ it "should work with nil as hash value" do
722
+ @ds.insert(20, nil)
723
+ @ds.filter(:a=>nil).all.should == []
724
+ @ds.filter(:b=>nil).all.should == [{:a=>20, :b=>nil}]
725
+ @ds.exclude(:b=>nil).all.should == []
726
+ @ds.exclude(:a=>nil).all.should == [{:a=>20, :b=>nil}]
727
+ end
728
+
729
+ it "should work with arrays as hash values" do
730
+ @ds.insert(20, 10)
731
+ @ds.filter(:a=>[10]).all.should == []
732
+ @ds.filter(:a=>[20, 10]).all.should == [{:a=>20, :b=>10}]
733
+ @ds.exclude(:a=>[10]).all.should == [{:a=>20, :b=>10}]
734
+ @ds.exclude(:a=>[20, 10]).all.should == []
735
+ end
736
+
737
+ it "should work with ranges as hash values" do
738
+ @ds.insert(20, 10)
739
+ @ds.filter(:a=>(10..30)).all.should == [{:a=>20, :b=>10}]
740
+ @ds.filter(:a=>(25..30)).all.should == []
741
+ @ds.filter(:a=>(10..15)).all.should == []
742
+ @ds.exclude(:a=>(10..30)).all.should == []
743
+ @ds.exclude(:a=>(25..30)).all.should == [{:a=>20, :b=>10}]
744
+ @ds.exclude(:a=>(10..15)).all.should == [{:a=>20, :b=>10}]
745
+ end
746
+
747
+ it "should work with CASE statements" do
748
+ @ds.insert(20, 10)
749
+ @ds.filter({{:a=>20}=>20}.case(0) > 0).all.should == [{:a=>20, :b=>10}]
750
+ @ds.filter({{:a=>15}=>20}.case(0) > 0).all.should == []
751
+ @ds.filter({20=>20}.case(0, :a) > 0).all.should == [{:a=>20, :b=>10}]
752
+ @ds.filter({15=>20}.case(0, :a) > 0).all.should == []
753
+ end
754
+
755
+ it "should work with multiple value arrays" do
756
+ @ds.insert(20, 10)
757
+ @ds.quote_identifiers = false
758
+ @ds.filter([:a, :b]=>[[20, 10]].sql_array).all.should == [{:a=>20, :b=>10}]
759
+ @ds.filter([:a, :b]=>[[10, 20]].sql_array).all.should == []
760
+ @ds.filter([:a, :b]=>[[20, 10], [1, 2]].sql_array).all.should == [{:a=>20, :b=>10}]
761
+ @ds.filter([:a, :b]=>[[10, 10], [20, 20]].sql_array).all.should == []
762
+
763
+ @ds.exclude([:a, :b]=>[[20, 10]].sql_array).all.should == []
764
+ @ds.exclude([:a, :b]=>[[10, 20]].sql_array).all.should == [{:a=>20, :b=>10}]
765
+ @ds.exclude([:a, :b]=>[[20, 10], [1, 2]].sql_array).all.should == []
766
+ @ds.exclude([:a, :b]=>[[10, 10], [20, 20]].sql_array).all.should == [{:a=>20, :b=>10}]
767
+ end
768
+
769
+ it "should work with IN/NOT in with datasets" do
770
+ @ds.insert(20, 10)
771
+ ds = @ds.unordered
772
+ @ds.quote_identifiers = false
773
+
774
+ @ds.filter(:a=>ds.select(:a)).all.should == [{:a=>20, :b=>10}]
775
+ @ds.filter(:a=>ds.select(:a).where(:a=>15)).all.should == []
776
+ @ds.exclude(:a=>ds.select(:a)).all.should == []
777
+ @ds.exclude(:a=>ds.select(:a).where(:a=>15)).all.should == [{:a=>20, :b=>10}]
778
+
779
+ @ds.filter([:a, :b]=>ds.select(:a, :b)).all.should == [{:a=>20, :b=>10}]
780
+ @ds.filter([:a, :b]=>ds.select(:b, :a)).all.should == []
781
+ @ds.exclude([:a, :b]=>ds.select(:a, :b)).all.should == []
782
+ @ds.exclude([:a, :b]=>ds.select(:b, :a)).all.should == [{:a=>20, :b=>10}]
783
+
784
+ @ds.filter([:a, :b]=>ds.select(:a, :b).where(:a=>15)).all.should == []
785
+ @ds.exclude([:a, :b]=>ds.select(:a, :b).where(:a=>15)).all.should == [{:a=>20, :b=>10}]
786
+ end
787
+
788
+ specify "should work empty arrays" do
789
+ @ds.insert(20, 10)
790
+ @ds.filter(:a=>[]).all.should == []
791
+ @ds.exclude(:a=>[]).all.should == [{:a=>20, :b=>10}]
792
+ @ds.filter([:a, :b]=>[]).all.should == []
793
+ @ds.exclude([:a, :b]=>[]).all.should == [{:a=>20, :b=>10}]
794
+ end
795
+
796
+ specify "should work empty arrays with nulls" do
797
+ @ds.insert(nil, nil)
798
+ @ds.filter(:a=>[]).all.should == []
799
+ @ds.exclude(:a=>[]).all.should == [{:a=>nil, :b=>nil}]
800
+ @ds.filter([:a, :b]=>[]).all.should == []
801
+ @ds.exclude([:a, :b]=>[]).all.should == [{:a=>nil, :b=>nil}]
802
+ end
803
+
804
+ it "should work multiple conditions" do
805
+ @ds.insert(20, 10)
806
+ @ds.filter(:a=>20, :b=>10).all.should == [{:a=>20, :b=>10}]
807
+ @ds.filter([[:a, 20], [:b, 10]]).all.should == [{:a=>20, :b=>10}]
808
+ @ds.filter({:a=>20} & {:b=>10}).all.should == [{:a=>20, :b=>10}]
809
+ @ds.filter({:a=>20} | {:b=>5}).all.should == [{:a=>20, :b=>10}]
810
+ @ds.filter(~{:a=>10}).all.should == [{:a=>20, :b=>10}]
811
+ end
812
+ end
813
+
814
+ describe "SQL Extract Function" do
815
+ before do
816
+ @db = INTEGRATION_DB
817
+ @db.create_table!(:a){DateTime :a}
818
+ @ds = @db[:a].order(:a)
819
+ end
820
+ after do
821
+ @db.drop_table(:a)
822
+ end
823
+
824
+ cspecify "should return the part of the datetime asked for", :sqlite, :mssql do
825
+ t = Time.now
826
+ @ds.insert(t)
827
+ @ds.get{a.extract(:year)}.should == t.year
828
+ @ds.get{a.extract(:month)}.should == t.month
829
+ @ds.get{a.extract(:day)}.should == t.day
830
+ end
831
+ end
832
+
833
+ describe "Dataset string methods" do
834
+ before do
835
+ @db = INTEGRATION_DB
836
+ @db.create_table!(:a){String :a; String :b}
837
+ @ds = @db[:a].order(:a)
838
+ end
839
+ after do
840
+ @db.drop_table(:a)
841
+ end
842
+
843
+ it "#grep should return matching rows" do
844
+ @ds.insert('foo', 'bar')
845
+ @ds.grep(:a, 'foo').all.should == [{:a=>'foo', :b=>'bar'}]
846
+ @ds.grep(:b, 'foo').all.should == []
847
+ @ds.grep(:b, 'bar').all.should == [{:a=>'foo', :b=>'bar'}]
848
+ @ds.grep(:a, 'bar').all.should == []
849
+ @ds.grep([:a, :b], %w'foo bar').all.should == [{:a=>'foo', :b=>'bar'}]
850
+ @ds.grep([:a, :b], %w'boo far').all.should == []
851
+ end
852
+
853
+ it "#like should return matching rows" do
854
+ @ds.insert('foo', 'bar')
855
+ @ds.filter(:a.like('foo')).all.should == [{:a=>'foo', :b=>'bar'}]
856
+ @ds.filter(:a.like('bar')).all.should == []
857
+ @ds.filter(:a.like('foo', 'bar')).all.should == [{:a=>'foo', :b=>'bar'}]
858
+ end
859
+
860
+ it "#ilike should return matching rows, in a case insensitive manner" do
861
+ @ds.insert('foo', 'bar')
862
+ @ds.filter(:a.ilike('Foo')).all.should == [{:a=>'foo', :b=>'bar'}]
863
+ @ds.filter(:a.ilike('baR')).all.should == []
864
+ @ds.filter(:a.ilike('FOO', 'BAR')).all.should == [{:a=>'foo', :b=>'bar'}]
865
+ end
866
+
867
+ it "should work with strings created with sql_string_join" do
868
+ @ds.insert('foo', 'bar')
869
+ @ds.get([:a, :b].sql_string_join).should == 'foobar'
870
+ @ds.get([:a, :b].sql_string_join(' ')).should == 'foo bar'
871
+ end
872
+ end
873
+
874
+ describe "Dataset identifier methods" do
875
+ before do
876
+ class ::String
877
+ def uprev
878
+ upcase.reverse
879
+ end
880
+ end
881
+ @db = INTEGRATION_DB
882
+ @db.create_table!(:a){Integer :ab}
883
+ @ds = @db[:a].order(:ab)
884
+ @ds.insert(1)
885
+ end
886
+ after do
887
+ @db.drop_table(:a)
888
+ end
889
+
890
+ it "#identifier_output_method should change how identifiers are output" do
891
+ @ds.identifier_output_method = :upcase
892
+ @ds.first.should == {:AB=>1}
893
+ @ds.identifier_output_method = :uprev
894
+ @ds.first.should == {:BA=>1}
895
+ end
896
+
897
+ it "should work when not quoting identifiers" do
898
+ @ds.quote_identifiers = false
899
+ @ds.first.should == {:ab=>1}
900
+ end
901
+ end
902
+
903
+ describe "Dataset defaults and overrides" do
904
+ before do
905
+ @db = INTEGRATION_DB
906
+ @db.create_table!(:a){Integer :a}
907
+ @ds = @db[:a].order(:a)
908
+ end
909
+ after do
910
+ @db.drop_table(:a)
911
+ end
912
+
913
+ it "#set_defaults should set defaults that can be overridden" do
914
+ @ds = @ds.set_defaults(:a=>10)
915
+ @ds.insert
916
+ @ds.insert(:a=>20)
917
+ @ds.all.should == [{:a=>10}, {:a=>20}]
918
+ end
919
+
920
+ it "#set_overrides should set defaults that cannot be overridden" do
921
+ @ds = @ds.set_overrides(:a=>10)
922
+ @ds.insert
923
+ @ds.insert(:a=>20)
924
+ @ds.all.should == [{:a=>10}, {:a=>10}]
925
+ end
926
+ end
927
+
928
+ if INTEGRATION_DB.dataset.supports_modifying_joins?
929
+ describe "Modifying joined datasets" do
930
+ before do
931
+ @db = INTEGRATION_DB
932
+ @db.create_table!(:a){Integer :a; Integer :d}
933
+ @db.create_table!(:b){Integer :b; Integer :e}
934
+ @db.create_table!(:c){Integer :c; Integer :f}
935
+ @ds = @db.from(:a, :b).join(:c, :c=>:e.identifier).where(:d=>:b, :f=>6)
936
+ @db[:a].insert(1, 2)
937
+ @db[:a].insert(3, 4)
938
+ @db[:b].insert(2, 5)
939
+ @db[:c].insert(5, 6)
940
+ @db[:b].insert(4, 7)
941
+ @db[:c].insert(7, 8)
942
+ end
943
+ after do
944
+ @db.drop_table(:a, :b, :c)
945
+ end
946
+
947
+ it "#update should allow updating joined datasets" do
948
+ @ds.update(:a=>10)
949
+ @ds.all.should == [{:c=>5, :b=>2, :a=>10, :d=>2, :e=>5, :f=>6}]
950
+ @db[:a].order(:a).all.should == [{:a=>3, :d=>4}, {:a=>10, :d=>2}]
951
+ @db[:b].order(:b).all.should == [{:b=>2, :e=>5}, {:b=>4, :e=>7}]
952
+ @db[:c].order(:c).all.should == [{:c=>5, :f=>6}, {:c=>7, :f=>8}]
953
+ end
954
+
955
+ it "#delete should allow deleting from joined datasets" do
956
+ @ds.delete
957
+ @ds.all.should == []
958
+ @db[:a].order(:a).all.should == [{:a=>3, :d=>4}]
959
+ @db[:b].order(:b).all.should == [{:b=>2, :e=>5}, {:b=>4, :e=>7}]
960
+ @db[:c].order(:c).all.should == [{:c=>5, :f=>6}, {:c=>7, :f=>8}]
961
+ end
962
+ end
963
+ end