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,417 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ context "Array#all_two_pairs?" do
4
+ specify "should return false if empty" do
5
+ [].all_two_pairs?.should == false
6
+ end
7
+
8
+ specify "should return false if any of the elements is not an array" do
9
+ [1].all_two_pairs?.should == false
10
+ [[1,2],1].all_two_pairs?.should == false
11
+ end
12
+
13
+ specify "should return false if any of the elements has a length other than two" do
14
+ [[1,2],[]].all_two_pairs?.should == false
15
+ [[1,2],[1]].all_two_pairs?.should == false
16
+ [[1,2],[1,2,3]].all_two_pairs?.should == false
17
+ end
18
+
19
+ specify "should return true if all of the elements are arrays with a length of two" do
20
+ [[1,2]].all_two_pairs?.should == true
21
+ [[1,2],[1,2]].all_two_pairs?.should == true
22
+ [[1,2],[1,2],[1,2]].all_two_pairs?.should == true
23
+ end
24
+ end
25
+
26
+ context "Array#case and Hash#case" do
27
+ before do
28
+ @d = Sequel::Dataset.new(nil)
29
+ end
30
+
31
+ specify "should return SQL CASE expression" do
32
+ @d.literal({:x=>:y}.case(:z)).should == '(CASE WHEN x THEN y ELSE z END)'
33
+ @d.literal({:x=>:y}.case(:z, :exp)).should == '(CASE exp WHEN x THEN y ELSE z END)'
34
+ ['(CASE WHEN x THEN y WHEN a THEN b ELSE z END)',
35
+ '(CASE WHEN a THEN b WHEN x THEN y ELSE z END)'].should(include(@d.literal({:x=>:y, :a=>:b}.case(:z))))
36
+ @d.literal([[:x, :y]].case(:z)).should == '(CASE WHEN x THEN y ELSE z END)'
37
+ @d.literal([[:x, :y], [:a, :b]].case(:z)).should == '(CASE WHEN x THEN y WHEN a THEN b ELSE z END)'
38
+ @d.literal([[:x, :y], [:a, :b]].case(:z, :exp)).should == '(CASE exp WHEN x THEN y WHEN a THEN b ELSE z END)'
39
+ @d.literal([[:x, :y], [:a, :b]].case(:z, :exp__w)).should == '(CASE exp.w WHEN x THEN y WHEN a THEN b ELSE z END)'
40
+ end
41
+
42
+ specify "should raise an error if an array that isn't all two pairs is used" do
43
+ proc{[:b].case(:a)}.should raise_error(Sequel::Error)
44
+ proc{[:b, :c].case(:a)}.should raise_error(Sequel::Error)
45
+ proc{[[:b, :c], :d].case(:a)}.should raise_error(Sequel::Error)
46
+ end
47
+
48
+ specify "should raise an error if an empty array/hash is used" do
49
+ proc{[].case(:a)}.should raise_error(Sequel::Error)
50
+ proc{{}.case(:a)}.should raise_error(Sequel::Error)
51
+ end
52
+ end
53
+
54
+ context "Array#sql_array" do
55
+ before do
56
+ @d = Sequel::Dataset.new(nil)
57
+ end
58
+
59
+ specify "should treat the array as an SQL array instead of conditions" do
60
+ @d.literal([[:x, 1], [:y, 2]]).should == '((x = 1) AND (y = 2))'
61
+ @d.literal([[:x, 1], [:y, 2]].sql_array).should == '((x, 1), (y, 2))'
62
+ @d.filter([:a, :b]=>[[:x, 1], [:y, 2]].sql_array).sql.should == 'SELECT * WHERE ((a, b) IN ((x, 1), (y, 2)))'
63
+ end
64
+ end
65
+
66
+ context "String#lit" do
67
+ specify "should return an LiteralString object" do
68
+ 'xyz'.lit.should be_a_kind_of(Sequel::LiteralString)
69
+ 'xyz'.lit.to_s.should == 'xyz'
70
+ end
71
+
72
+ specify "should inhibit string literalization" do
73
+ Sequel::Database.new[:t].update_sql(:stamp => "NOW()".lit).should == \
74
+ "UPDATE t SET stamp = NOW()"
75
+ end
76
+
77
+ specify "should return a PlaceholderLiteralString object if args are given" do
78
+ a = 'DISTINCT ?'.lit(:a)
79
+ a.should be_a_kind_of(Sequel::SQL::PlaceholderLiteralString)
80
+ ds = MockDatabase.new.dataset
81
+ ds.literal(a).should == 'DISTINCT a'
82
+ ds.quote_identifiers = true
83
+ ds.literal(a).should == 'DISTINCT "a"'
84
+ end
85
+
86
+ specify "should handle named placeholders if given a single argument hash" do
87
+ a = 'DISTINCT :b'.lit(:b=>:a)
88
+ a.should be_a_kind_of(Sequel::SQL::PlaceholderLiteralString)
89
+ ds = MockDatabase.new.dataset
90
+ ds.literal(a).should == 'DISTINCT a'
91
+ ds.quote_identifiers = true
92
+ ds.literal(a).should == 'DISTINCT "a"'
93
+ end
94
+ end
95
+
96
+ context "String#to_sequel_blob" do
97
+ specify "should return a Blob object" do
98
+ 'xyz'.to_sequel_blob.should be_a_kind_of(::Sequel::SQL::Blob)
99
+ 'xyz'.to_sequel_blob.should == 'xyz'
100
+ end
101
+
102
+ specify "should retain binary data" do
103
+ "\1\2\3\4".to_sequel_blob.should == "\1\2\3\4"
104
+ end
105
+ end
106
+
107
+ context "#desc" do
108
+ before do
109
+ @ds = Sequel::Dataset.new(nil)
110
+ end
111
+
112
+ specify "should format a DESC clause for a column ref" do
113
+ :test.desc.to_s(@ds).should == 'test DESC'
114
+
115
+ :items__price.desc.to_s(@ds).should == 'items.price DESC'
116
+ end
117
+
118
+ specify "should format a DESC clause for a function" do
119
+ :avg.sql_function(:test).desc.to_s(@ds).should == 'avg(test) DESC'
120
+ end
121
+ end
122
+
123
+ context "#asc" do
124
+ before do
125
+ @ds = Sequel::Dataset.new(nil)
126
+ end
127
+
128
+ specify "should format a ASC clause for a column ref" do
129
+ :test.asc.to_s(@ds).should == 'test ASC'
130
+
131
+ :items__price.asc.to_s(@ds).should == 'items.price ASC'
132
+ end
133
+
134
+ specify "should format a ASC clause for a function" do
135
+ :avg.sql_function(:test).asc.to_s(@ds).should == 'avg(test) ASC'
136
+ end
137
+ end
138
+
139
+ context "#as" do
140
+ before do
141
+ @ds = Sequel::Dataset.new(nil)
142
+ end
143
+
144
+ specify "should format a AS clause for a column ref" do
145
+ :test.as(:t).to_s(@ds).should == 'test AS t'
146
+
147
+ :items__price.as(:p).to_s(@ds).should == 'items.price AS p'
148
+ end
149
+
150
+ specify "should format a AS clause for a function" do
151
+ :avg.sql_function(:test).as(:avg).to_s(@ds).should == 'avg(test) AS avg'
152
+ end
153
+
154
+ specify "should format a AS clause for a literal value" do
155
+ 'abc'.as(:abc).to_s(@ds).should == "'abc' AS abc"
156
+ end
157
+ end
158
+
159
+ context "Column references" do
160
+ before do
161
+ @c = Class.new(Sequel::Dataset) do
162
+ def quoted_identifier(c); "`#{c}`"; end
163
+ end
164
+ @ds = @c.new(MockDatabase.new)
165
+ @ds.quote_identifiers = true
166
+ end
167
+
168
+ specify "should be quoted properly" do
169
+ @ds.literal(:xyz).should == "`xyz`"
170
+ @ds.literal(:xyz__abc).should == "`xyz`.`abc`"
171
+
172
+ @ds.literal(:xyz.as(:x)).should == "`xyz` AS `x`"
173
+ @ds.literal(:xyz__abc.as(:x)).should == "`xyz`.`abc` AS `x`"
174
+
175
+ @ds.literal(:xyz___x).should == "`xyz` AS `x`"
176
+ @ds.literal(:xyz__abc___x).should == "`xyz`.`abc` AS `x`"
177
+ end
178
+
179
+ specify "should be quoted properly in SQL functions" do
180
+ @ds.literal(:avg.sql_function(:xyz)).should == "avg(`xyz`)"
181
+ @ds.literal(:avg.sql_function(:xyz, 1)).should == "avg(`xyz`, 1)"
182
+ @ds.literal(:avg.sql_function(:xyz).as(:a)).should == "avg(`xyz`) AS `a`"
183
+ end
184
+
185
+ specify "should be quoted properly in ASC/DESC clauses" do
186
+ @ds.literal(:xyz.asc).should == "`xyz` ASC"
187
+ @ds.literal(:avg.sql_function(:xyz, 1).desc).should == "avg(`xyz`, 1) DESC"
188
+ end
189
+
190
+ specify "should be quoted properly in a cast function" do
191
+ @ds.literal(:x.cast(:integer)).should == "CAST(`x` AS integer)"
192
+ @ds.literal(:x__y.cast('varchar(20)')).should == "CAST(`x`.`y` AS varchar(20))"
193
+ end
194
+ end
195
+
196
+ context "Blob" do
197
+ specify "#to_sequel_blob should return self" do
198
+ blob = "x".to_sequel_blob
199
+ blob.to_sequel_blob.object_id.should == blob.object_id
200
+ end
201
+ end
202
+
203
+ if RUBY_VERSION < '1.9.0'
204
+ context "Symbol#[]" do
205
+ specify "should format an SQL Function" do
206
+ ds = Sequel::Dataset.new(nil)
207
+ ds.literal(:xyz[]).should == 'xyz()'
208
+ ds.literal(:xyz[1]).should == 'xyz(1)'
209
+ ds.literal(:xyz[1, 2, :abc[3]]).should == 'xyz(1, 2, abc(3))'
210
+ end
211
+ end
212
+ end
213
+
214
+ context "Symbol#*" do
215
+ before do
216
+ @ds = Sequel::Dataset.new(nil)
217
+ end
218
+
219
+ specify "should format a qualified wildcard if no argument" do
220
+ :xyz.*.to_s(@ds).should == 'xyz.*'
221
+ :abc.*.to_s(@ds).should == 'abc.*'
222
+ end
223
+
224
+ specify "should format a filter expression if an argument" do
225
+ :xyz.*(3).to_s(@ds).should == '(xyz * 3)'
226
+ :abc.*(5).to_s(@ds).should == '(abc * 5)'
227
+ end
228
+
229
+ specify "should support qualified symbols if no argument" do
230
+ :xyz__abc.*.to_s(@ds).should == 'xyz.abc.*'
231
+ end
232
+ end
233
+
234
+ context "Symbol" do
235
+ before do
236
+ @ds = Sequel::Dataset.new(nil)
237
+ @ds.quote_identifiers = true
238
+ @ds.identifier_input_method = :upcase
239
+ end
240
+
241
+ specify "#identifier should format an identifier" do
242
+ @ds.literal(:xyz__abc.identifier).should == '"XYZ__ABC"'
243
+ end
244
+
245
+ specify "#qualify should format a qualified column" do
246
+ @ds.literal(:xyz.qualify(:abc)).should == '"ABC"."XYZ"'
247
+ end
248
+
249
+ specify "#qualify should work on QualifiedIdentifiers" do
250
+ @ds.literal(:xyz.qualify(:abc).qualify(:def)).should == '"DEF"."ABC"."XYZ"'
251
+ end
252
+
253
+ specify "should be able to qualify an identifier" do
254
+ @ds.literal(:xyz.identifier.qualify(:xyz__abc)).should == '"XYZ"."ABC"."XYZ"'
255
+ end
256
+
257
+ specify "should be able to specify a schema.table.column" do
258
+ @ds.literal(:column.qualify(:table.qualify(:schema))).should == '"SCHEMA"."TABLE"."COLUMN"'
259
+ @ds.literal(:column.qualify(:table__name.identifier.qualify(:schema))).should == '"SCHEMA"."TABLE__NAME"."COLUMN"'
260
+ end
261
+
262
+ specify "should be able to specify order" do
263
+ @oe = :xyz.desc
264
+ @oe.class.should == Sequel::SQL::OrderedExpression
265
+ @oe.descending.should == true
266
+ @oe = :xyz.asc
267
+ @oe.class.should == Sequel::SQL::OrderedExpression
268
+ @oe.descending.should == false
269
+ end
270
+ end
271
+
272
+ context "Dataset#literal" do
273
+ before do
274
+ @ds = MockDataset.new(nil)
275
+ end
276
+
277
+ specify "should convert qualified symbol notation into dot notation" do
278
+ @ds.literal(:abc__def).should == 'abc.def'
279
+ end
280
+
281
+ specify "should convert AS symbol notation into SQL AS notation" do
282
+ @ds.literal(:xyz___x).should == 'xyz AS x'
283
+ @ds.literal(:abc__def___x).should == 'abc.def AS x'
284
+ end
285
+
286
+ specify "should support names with digits" do
287
+ @ds.literal(:abc2).should == 'abc2'
288
+ @ds.literal(:xx__yy3).should == 'xx.yy3'
289
+ @ds.literal(:ab34__temp3_4ax).should == 'ab34.temp3_4ax'
290
+ @ds.literal(:x1___y2).should == 'x1 AS y2'
291
+ @ds.literal(:abc2__def3___ggg4).should == 'abc2.def3 AS ggg4'
292
+ end
293
+
294
+ specify "should support upper case and lower case" do
295
+ @ds.literal(:ABC).should == 'ABC'
296
+ @ds.literal(:Zvashtoy__aBcD).should == 'Zvashtoy.aBcD'
297
+ end
298
+
299
+ specify "should support spaces inside column names" do
300
+ @ds.quote_identifiers = true
301
+ @ds.literal(:"AB C").should == '"AB C"'
302
+ @ds.literal(:"Zvas htoy__aB cD").should == '"Zvas htoy"."aB cD"'
303
+ @ds.literal(:"aB cD___XX XX").should == '"aB cD" AS "XX XX"'
304
+ @ds.literal(:"Zva shtoy__aB cD___XX XX").should == '"Zva shtoy"."aB cD" AS "XX XX"'
305
+ end
306
+ end
307
+
308
+ context "Symbol" do
309
+ before do
310
+ @ds = Sequel::Dataset.new(MockDatabase.new)
311
+ end
312
+
313
+ specify "should support upper case outer functions" do
314
+ :COUNT.sql_function('1').to_s(@ds).should == "COUNT('1')"
315
+ end
316
+
317
+ specify "should inhibit string literalization" do
318
+ db = Sequel::Database.new
319
+ ds = db[:t]
320
+ ds.select(:COUNT.sql_function('1')).sql.should == "SELECT COUNT('1') FROM t"
321
+ end
322
+
323
+ specify "should support cast method" do
324
+ :abc.cast(:integer).to_s(@ds).should == "CAST(abc AS integer)"
325
+ end
326
+
327
+ specify "should support sql array accesses via sql_subscript" do
328
+ @ds.literal(:abc.sql_subscript(1)).should == "abc[1]"
329
+ @ds.literal(:abc__def.sql_subscript(1)).should == "abc.def[1]"
330
+ @ds.literal(:abc.sql_subscript(1)|2).should == "abc[1, 2]"
331
+ end
332
+
333
+ specify "should support cast_numeric and cast_string" do
334
+ x = :abc.cast_numeric
335
+ x.should be_a_kind_of(Sequel::SQL::NumericExpression)
336
+ x.to_s(@ds).should == "CAST(abc AS integer)"
337
+
338
+ x = :abc.cast_numeric(:real)
339
+ x.should be_a_kind_of(Sequel::SQL::NumericExpression)
340
+ x.to_s(@ds).should == "CAST(abc AS real)"
341
+
342
+ x = :abc.cast_string
343
+ x.should be_a_kind_of(Sequel::SQL::StringExpression)
344
+ x.to_s(@ds).should == "CAST(abc AS varchar(255))"
345
+
346
+ x = :abc.cast_string(:varchar)
347
+ x.should be_a_kind_of(Sequel::SQL::StringExpression)
348
+ x.to_s(@ds).should == "CAST(abc AS varchar(255))"
349
+ end
350
+
351
+ specify "should allow database independent types when casting" do
352
+ m = MockDatabase.new
353
+ m.instance_eval do
354
+ def cast_type_literal(type)
355
+ return :foo if type == Integer
356
+ return :bar if type == String
357
+ type
358
+ end
359
+ end
360
+ @ds2 = Sequel::Dataset.new(m)
361
+ :abc.cast(String).to_s(@ds).should == "CAST(abc AS varchar(255))"
362
+ :abc.cast(String).to_s(@ds2).should == "CAST(abc AS bar)"
363
+ :abc.cast(String).to_s(@ds2).should == "CAST(abc AS bar)"
364
+ :abc.cast_string.to_s(@ds2).should == "CAST(abc AS bar)"
365
+ :abc.cast_string(Integer).to_s(@ds2).should == "CAST(abc AS foo)"
366
+ :abc.cast_numeric.to_s(@ds2).should == "CAST(abc AS foo)"
367
+ :abc.cast_numeric(String).to_s(@ds2).should == "CAST(abc AS bar)"
368
+ end
369
+
370
+ specify "should support SQL EXTRACT function via #extract " do
371
+ :abc.extract(:year).to_s(@ds).should == "extract(year FROM abc)"
372
+ end
373
+ end
374
+
375
+ context "Sequel::SQL::Function#==" do
376
+ specify "should be true for functions with the same name and arguments, false otherwise" do
377
+ a = :date.sql_function(:t)
378
+ b = :date.sql_function(:t)
379
+ a.should == b
380
+ (a == b).should == true
381
+ c = :date.sql_function(:c)
382
+ a.should_not == c
383
+ (a == c).should == false
384
+ d = :time.sql_function(:c)
385
+ a.should_not == d
386
+ c.should_not == d
387
+ (a == d).should == false
388
+ (c == d).should == false
389
+ end
390
+ end
391
+
392
+ context "Sequel::SQL::OrderedExpression" do
393
+ specify "should #desc" do
394
+ @oe = :column.asc
395
+ @oe.descending.should == false
396
+ @oe.desc.descending.should == true
397
+ end
398
+
399
+ specify "should #asc" do
400
+ @oe = :column.desc
401
+ @oe.descending.should == true
402
+ @oe.asc.descending.should == false
403
+ end
404
+
405
+ specify "should #invert" do
406
+ @oe = :column.desc
407
+ @oe.invert.descending.should == false
408
+ @oe.invert.invert.descending.should == true
409
+ end
410
+ end
411
+
412
+ context "Expression" do
413
+ specify "should ==" do
414
+ :column.qualify(:table).cast(:type).*(:numeric_column).asc.should == :column.qualify(:table).cast(:type).*(:numeric_column).asc
415
+ :other_column.qualify(:table).cast(:type).*(:numeric_column).asc.should_not == :column.qualify(:table).cast(:type).*(:numeric_column).asc
416
+ end
417
+ end
@@ -0,0 +1,1662 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ context "A new Database" do
4
+ before do
5
+ @db = Sequel::Database.new(1 => 2, :logger => 3)
6
+ end
7
+ after do
8
+ Sequel.quote_identifiers = false
9
+ Sequel.identifier_input_method = nil
10
+ Sequel.identifier_output_method = nil
11
+ end
12
+
13
+ specify "should receive options" do
14
+ @db.opts[1].should == 2
15
+ @db.opts[:logger].should == 3
16
+ end
17
+
18
+ specify "should set the logger from opts[:logger] and opts[:loggers]" do
19
+ @db.loggers.should == [3]
20
+ Sequel::Database.new(1 => 2, :loggers => 3).loggers.should == [3]
21
+ Sequel::Database.new(1 => 2, :loggers => [3]).loggers.should == [3]
22
+ Sequel::Database.new(1 => 2, :logger => 4, :loggers => 3).loggers.should == [4,3]
23
+ Sequel::Database.new(1 => 2, :logger => [4], :loggers => [3]).loggers.should == [4,3]
24
+ end
25
+
26
+ specify "should create a connection pool" do
27
+ @db.pool.should be_a_kind_of(Sequel::ConnectionPool)
28
+ @db.pool.max_size.should == 4
29
+
30
+ Sequel::Database.new(:max_connections => 10).pool.max_size.should == 10
31
+ end
32
+
33
+ specify "should pass the supplied block to the connection pool" do
34
+ cc = nil
35
+ d = Sequel::Database.new {1234}
36
+ d.synchronize {|c| cc = c}
37
+ cc.should == 1234
38
+ end
39
+
40
+ specify "should respect the :single_threaded option" do
41
+ db = Sequel::Database.new(:single_threaded=>true){123}
42
+ db.pool.should be_a_kind_of(Sequel::SingleConnectionPool)
43
+ db = Sequel::Database.new(:single_threaded=>'t'){123}
44
+ db.pool.should be_a_kind_of(Sequel::SingleConnectionPool)
45
+ db = Sequel::Database.new(:single_threaded=>'1'){123}
46
+ db.pool.should be_a_kind_of(Sequel::SingleConnectionPool)
47
+ db = Sequel::Database.new(:single_threaded=>false){123}
48
+ db.pool.should be_a_kind_of(Sequel::ConnectionPool)
49
+ db = Sequel::Database.new(:single_threaded=>'f'){123}
50
+ db.pool.should be_a_kind_of(Sequel::ConnectionPool)
51
+ db = Sequel::Database.new(:single_threaded=>'0'){123}
52
+ db.pool.should be_a_kind_of(Sequel::ConnectionPool)
53
+ end
54
+
55
+ specify "should respect the :quote_identifiers option" do
56
+ db = Sequel::Database.new(:quote_identifiers=>false)
57
+ db.quote_identifiers?.should == false
58
+ db = Sequel::Database.new(:quote_identifiers=>true)
59
+ db.quote_identifiers?.should == true
60
+ end
61
+
62
+ specify "should upcase on input and downcase on output by default" do
63
+ db = Sequel::Database.new
64
+ db.send(:identifier_input_method_default).should == :upcase
65
+ db.send(:identifier_output_method_default).should == :downcase
66
+ end
67
+
68
+ specify "should respect the :identifier_input_method option" do
69
+ Sequel.identifier_input_method = nil
70
+ Sequel::Database.identifier_input_method.should == ""
71
+ db = Sequel::Database.new(:identifier_input_method=>nil)
72
+ db.identifier_input_method.should == nil
73
+ db.identifier_input_method = :downcase
74
+ db.identifier_input_method.should == :downcase
75
+ db = Sequel::Database.new(:identifier_input_method=>:upcase)
76
+ db.identifier_input_method.should == :upcase
77
+ db.identifier_input_method = nil
78
+ db.identifier_input_method.should == nil
79
+ Sequel.identifier_input_method = :downcase
80
+ Sequel::Database.identifier_input_method.should == :downcase
81
+ db = Sequel::Database.new(:identifier_input_method=>nil)
82
+ db.identifier_input_method.should == nil
83
+ db.identifier_input_method = :upcase
84
+ db.identifier_input_method.should == :upcase
85
+ db = Sequel::Database.new(:identifier_input_method=>:upcase)
86
+ db.identifier_input_method.should == :upcase
87
+ db.identifier_input_method = nil
88
+ db.identifier_input_method.should == nil
89
+ end
90
+
91
+ specify "should respect the :identifier_output_method option" do
92
+ Sequel.identifier_output_method = nil
93
+ Sequel::Database.identifier_output_method.should == ""
94
+ db = Sequel::Database.new(:identifier_output_method=>nil)
95
+ db.identifier_output_method.should == nil
96
+ db.identifier_output_method = :downcase
97
+ db.identifier_output_method.should == :downcase
98
+ db = Sequel::Database.new(:identifier_output_method=>:upcase)
99
+ db.identifier_output_method.should == :upcase
100
+ db.identifier_output_method = nil
101
+ db.identifier_output_method.should == nil
102
+ Sequel.identifier_output_method = :downcase
103
+ Sequel::Database.identifier_output_method.should == :downcase
104
+ db = Sequel::Database.new(:identifier_output_method=>nil)
105
+ db.identifier_output_method.should == nil
106
+ db.identifier_output_method = :upcase
107
+ db.identifier_output_method.should == :upcase
108
+ db = Sequel::Database.new(:identifier_output_method=>:upcase)
109
+ db.identifier_output_method.should == :upcase
110
+ db.identifier_output_method = nil
111
+ db.identifier_output_method.should == nil
112
+ end
113
+
114
+ specify "should use the default Sequel.quote_identifiers value" do
115
+ Sequel.quote_identifiers = true
116
+ Sequel::Database.new({}).quote_identifiers?.should == true
117
+ Sequel.quote_identifiers = false
118
+ Sequel::Database.new({}).quote_identifiers?.should == false
119
+ Sequel::Database.quote_identifiers = true
120
+ Sequel::Database.new({}).quote_identifiers?.should == true
121
+ Sequel::Database.quote_identifiers = false
122
+ Sequel::Database.new({}).quote_identifiers?.should == false
123
+ end
124
+
125
+ specify "should use the default Sequel.identifier_input_method value" do
126
+ Sequel.identifier_input_method = :downcase
127
+ Sequel::Database.new({}).identifier_input_method.should == :downcase
128
+ Sequel.identifier_input_method = :upcase
129
+ Sequel::Database.new({}).identifier_input_method.should == :upcase
130
+ Sequel::Database.identifier_input_method = :downcase
131
+ Sequel::Database.new({}).identifier_input_method.should == :downcase
132
+ Sequel::Database.identifier_input_method = :upcase
133
+ Sequel::Database.new({}).identifier_input_method.should == :upcase
134
+ end
135
+
136
+ specify "should use the default Sequel.identifier_output_method value" do
137
+ Sequel.identifier_output_method = :downcase
138
+ Sequel::Database.new({}).identifier_output_method.should == :downcase
139
+ Sequel.identifier_output_method = :upcase
140
+ Sequel::Database.new({}).identifier_output_method.should == :upcase
141
+ Sequel::Database.identifier_output_method = :downcase
142
+ Sequel::Database.new({}).identifier_output_method.should == :downcase
143
+ Sequel::Database.identifier_output_method = :upcase
144
+ Sequel::Database.new({}).identifier_output_method.should == :upcase
145
+ end
146
+
147
+ specify "should respect the quote_indentifiers_default method if Sequel.quote_identifiers = nil" do
148
+ Sequel.quote_identifiers = nil
149
+ Sequel::Database.new({}).quote_identifiers?.should == true
150
+ x = Class.new(Sequel::Database){def quote_identifiers_default; false end}
151
+ x.new({}).quote_identifiers?.should == false
152
+ y = Class.new(Sequel::Database){def quote_identifiers_default; true end}
153
+ y.new({}).quote_identifiers?.should == true
154
+ end
155
+
156
+ specify "should respect the identifier_input_method_default method" do
157
+ class Sequel::Database
158
+ @@identifier_input_method = nil
159
+ end
160
+ x = Class.new(Sequel::Database){def identifier_input_method_default; :downcase end}
161
+ x.new({}).identifier_input_method.should == :downcase
162
+ y = Class.new(Sequel::Database){def identifier_input_method_default; :camelize end}
163
+ y.new({}).identifier_input_method.should == :camelize
164
+ end
165
+
166
+ specify "should respect the identifier_output_method_default method if Sequel.identifier_output_method is not called" do
167
+ class Sequel::Database
168
+ @@identifier_output_method = nil
169
+ end
170
+ x = Class.new(Sequel::Database){def identifier_output_method_default; :upcase end}
171
+ x.new({}).identifier_output_method.should == :upcase
172
+ y = Class.new(Sequel::Database){def identifier_output_method_default; :underscore end}
173
+ y.new({}).identifier_output_method.should == :underscore
174
+ end
175
+
176
+ specify "should just use a :uri option for jdbc with the full connection string" do
177
+ Sequel::Database.should_receive(:adapter_class).once.with(:jdbc).and_return(Sequel::Database)
178
+ db = Sequel.connect('jdbc:test://host/db_name')
179
+ db.should be_a_kind_of(Sequel::Database)
180
+ db.opts[:uri].should == 'jdbc:test://host/db_name'
181
+ end
182
+
183
+ specify "should just use a :uri option for do with the full connection string" do
184
+ Sequel::Database.should_receive(:adapter_class).once.with(:do).and_return(Sequel::Database)
185
+ db = Sequel.connect('do:test://host/db_name')
186
+ db.should be_a_kind_of(Sequel::Database)
187
+ db.opts[:uri].should == 'do:test://host/db_name'
188
+ end
189
+ end
190
+
191
+ context "Database#disconnect" do
192
+ specify "should call pool.disconnect" do
193
+ d = Sequel::Database.new
194
+ p = d.pool
195
+ p.should_receive(:disconnect).once.with({}).and_return(2)
196
+ d.disconnect.should == 2
197
+ end
198
+ end
199
+
200
+ context "Sequel.extension" do
201
+ specify "should attempt to load the given extension" do
202
+ proc{Sequel.extension :blah}.should raise_error(LoadError)
203
+ end
204
+ end
205
+
206
+ context "Database#connect" do
207
+ specify "should raise NotImplementedError" do
208
+ proc {Sequel::Database.new.connect(:default)}.should raise_error(NotImplementedError)
209
+ end
210
+ end
211
+
212
+ context "Database#log_info" do
213
+ before do
214
+ @o = Object.new
215
+ def @o.logs; @logs || []; end
216
+ def @o.method_missing(*args); (@logs ||= []) << args; end
217
+ @db = Sequel::Database.new(:logger=>@o)
218
+ end
219
+
220
+ specify "should log message at info level to all loggers" do
221
+ @db.log_info('blah')
222
+ @o.logs.should == [[:info, 'blah']]
223
+ end
224
+
225
+ specify "should log message with args at info level to all loggers" do
226
+ @db.log_info('blah', [1, 2])
227
+ @o.logs.should == [[:info, 'blah; [1, 2]']]
228
+ end
229
+ end
230
+
231
+ context "Database#log_yield" do
232
+ before do
233
+ @o = Object.new
234
+ def @o.logs; @logs || []; end
235
+ def @o.warn(*args); (@logs ||= []) << [:warn] + args; end
236
+ def @o.method_missing(*args); (@logs ||= []) << args; end
237
+ @db = Sequel::Database.new(:logger=>@o)
238
+ end
239
+
240
+ specify "should yield to the passed block" do
241
+ a = nil
242
+ @db.log_yield('blah'){a = 1}
243
+ a.should == 1
244
+ end
245
+
246
+ specify "should raise an exception if a block is not passed" do
247
+ proc{@db.log_yield('blah')}.should raise_error
248
+ end
249
+
250
+ specify "should log message with duration at info level to all loggers" do
251
+ @db.log_yield('blah'){}
252
+ @o.logs.length.should == 1
253
+ @o.logs.first.length.should == 2
254
+ @o.logs.first.first.should == :info
255
+ @o.logs.first.last.should =~ /\A\(\d\.\d{6}s\) blah\z/
256
+ end
257
+
258
+ specify "should log message with duration at warn level if duration greater than log_warn_duration" do
259
+ @db.log_warn_duration = 0
260
+ @db.log_yield('blah'){}
261
+ @o.logs.length.should == 1
262
+ @o.logs.first.length.should == 2
263
+ @o.logs.first.first.should == :warn
264
+ @o.logs.first.last.should =~ /\A\(\d\.\d{6}s\) blah\z/
265
+ end
266
+
267
+ specify "should log message with duration at info level if duration less than log_warn_duration" do
268
+ @db.log_warn_duration = 1000
269
+ @db.log_yield('blah'){}
270
+ @o.logs.length.should == 1
271
+ @o.logs.first.length.should == 2
272
+ @o.logs.first.first.should == :info
273
+ @o.logs.first.last.should =~ /\A\(\d\.\d{6}s\) blah\z/
274
+ end
275
+
276
+ specify "should log message at error level if block raises an error" do
277
+ @db.log_warn_duration = 0
278
+ proc{@db.log_yield('blah'){raise Sequel::Error, 'adsf'}}.should raise_error
279
+ @o.logs.length.should == 1
280
+ @o.logs.first.length.should == 2
281
+ @o.logs.first.first.should == :error
282
+ @o.logs.first.last.should =~ /\ASequel::Error: adsf: blah\z/
283
+ end
284
+
285
+ specify "should include args with message if args passed" do
286
+ @db.log_yield('blah', [1, 2]){}
287
+ @o.logs.length.should == 1
288
+ @o.logs.first.length.should == 2
289
+ @o.logs.first.first.should == :info
290
+ @o.logs.first.last.should =~ /\A\(\d\.\d{6}s\) blah; \[1, 2\]\z/
291
+ end
292
+ end
293
+
294
+ context "Database#uri" do
295
+ before do
296
+ @c = Class.new(Sequel::Database) do
297
+ set_adapter_scheme :mau
298
+ end
299
+
300
+ @db = Sequel.connect('mau://user:pass@localhost:9876/maumau')
301
+ end
302
+
303
+ specify "should return the connection URI for the database" do
304
+ @db.uri.should == 'mau://user:pass@localhost:9876/maumau'
305
+ end
306
+
307
+ specify "should be aliased as #url" do
308
+ @db.url.should == 'mau://user:pass@localhost:9876/maumau'
309
+ end
310
+ end
311
+
312
+ context "Database.adapter_scheme" do
313
+ specify "should return the database schema" do
314
+ Sequel::Database.adapter_scheme.should be_nil
315
+
316
+ @c = Class.new(Sequel::Database) do
317
+ set_adapter_scheme :mau
318
+ end
319
+
320
+ @c.adapter_scheme.should == :mau
321
+ end
322
+ end
323
+
324
+ context "Database#dataset" do
325
+ before do
326
+ @db = Sequel::Database.new
327
+ @ds = @db.dataset
328
+ end
329
+
330
+ specify "should provide a blank dataset through #dataset" do
331
+ @ds.should be_a_kind_of(Sequel::Dataset)
332
+ @ds.opts.should == {}
333
+ @ds.db.should be(@db)
334
+ end
335
+
336
+ specify "should provide a #from dataset" do
337
+ d = @db.from(:mau)
338
+ d.should be_a_kind_of(Sequel::Dataset)
339
+ d.sql.should == 'SELECT * FROM mau'
340
+
341
+ e = @db[:miu]
342
+ e.should be_a_kind_of(Sequel::Dataset)
343
+ e.sql.should == 'SELECT * FROM miu'
344
+ end
345
+
346
+ specify "should provide a filtered #from dataset if a block is given" do
347
+ d = @db.from(:mau) {:x.sql_number > 100}
348
+ d.should be_a_kind_of(Sequel::Dataset)
349
+ d.sql.should == 'SELECT * FROM mau WHERE (x > 100)'
350
+ end
351
+
352
+ specify "should provide a #select dataset" do
353
+ d = @db.select(:a, :b, :c).from(:mau)
354
+ d.should be_a_kind_of(Sequel::Dataset)
355
+ d.sql.should == 'SELECT a, b, c FROM mau'
356
+ end
357
+
358
+ specify "should allow #select to take a block" do
359
+ d = @db.select(:a, :b){c}.from(:mau)
360
+ d.should be_a_kind_of(Sequel::Dataset)
361
+ d.sql.should == 'SELECT a, b, c FROM mau'
362
+ end
363
+ end
364
+
365
+ context "Database#execute" do
366
+ specify "should raise NotImplementedError" do
367
+ proc {Sequel::Database.new.execute('blah blah')}.should raise_error(NotImplementedError)
368
+ proc {Sequel::Database.new << 'blah blah'}.should raise_error(NotImplementedError)
369
+ end
370
+ end
371
+
372
+ context "Database#<< and run" do
373
+ before do
374
+ sqls = @sqls = []
375
+ @c = Class.new(Sequel::Database) do
376
+ define_method(:execute_ddl){|sql, *opts| sqls.clear; sqls << sql; sqls.concat(opts)}
377
+ end
378
+ @db = @c.new({})
379
+ end
380
+
381
+ specify "should pass the supplied sql to #execute_ddl" do
382
+ (@db << "DELETE FROM items")
383
+ @sqls.should == ["DELETE FROM items", {}]
384
+ @db.run("DELETE FROM items2")
385
+ @sqls.should == ["DELETE FROM items2", {}]
386
+ end
387
+
388
+ specify "should return nil" do
389
+ (@db << "DELETE FROM items").should == nil
390
+ @db.run("DELETE FROM items").should == nil
391
+ end
392
+
393
+ specify "should accept options passed to execute_ddl" do
394
+ @db.run("DELETE FROM items", :server=>:s1)
395
+ @sqls.should == ["DELETE FROM items", {:server=>:s1}]
396
+ end
397
+ end
398
+
399
+ context "Database#synchronize" do
400
+ before do
401
+ @db = Sequel::Database.new(:max_connections => 1){12345}
402
+ end
403
+
404
+ specify "should wrap the supplied block in pool.hold" do
405
+ stop = false
406
+ c1, c2 = nil
407
+ t1 = Thread.new {@db.synchronize {|c| c1 = c; while !stop;sleep 0.1;end}}
408
+ while !c1;end
409
+ c1.should == 12345
410
+ t2 = Thread.new {@db.synchronize {|c| c2 = c}}
411
+ sleep 0.2
412
+ @db.pool.available_connections.should be_empty
413
+ c2.should be_nil
414
+ stop = true
415
+ t1.join
416
+ sleep 0.1
417
+ c2.should == 12345
418
+ t2.join
419
+ end
420
+ end
421
+
422
+ context "Database#test_connection" do
423
+ before do
424
+ @db = Sequel::Database.new{@test = rand(100)}
425
+ end
426
+
427
+ specify "should pool#hold" do
428
+ @db.test_connection
429
+ @test.should_not be_nil
430
+ end
431
+
432
+ specify "should return true if successful" do
433
+ @db.test_connection.should be_true
434
+ end
435
+
436
+ specify "should raise an error if the attempting to connect raises an error" do
437
+ proc{Sequel::Database.new{raise Sequel::Error, 'blah'}.test_connection}.should raise_error(Sequel::Error)
438
+ end
439
+ end
440
+
441
+ class DummyDataset < Sequel::Dataset
442
+ def first
443
+ raise if @opts[:from] == [:a]
444
+ true
445
+ end
446
+ end
447
+
448
+ class DummyDatabase < Sequel::Database
449
+ attr_reader :sqls
450
+
451
+ def execute(sql, opts={})
452
+ @sqls ||= []
453
+ @sqls << sql
454
+ end
455
+
456
+ def transaction; yield; end
457
+
458
+ def dataset
459
+ DummyDataset.new(self)
460
+ end
461
+ end
462
+
463
+ context "Database#create_table" do
464
+ before do
465
+ @db = DummyDatabase.new
466
+ end
467
+
468
+ specify "should construct proper SQL" do
469
+ @db.create_table :test do
470
+ primary_key :id, :integer, :null => false
471
+ column :name, :text
472
+ index :name, :unique => true
473
+ end
474
+ @db.sqls.should == [
475
+ 'CREATE TABLE test (id integer NOT NULL PRIMARY KEY AUTOINCREMENT, name text)',
476
+ 'CREATE UNIQUE INDEX test_name_index ON test (name)'
477
+ ]
478
+ end
479
+
480
+ specify "should create a temporary table" do
481
+ @db.create_table :test_tmp, :temp => true do
482
+ primary_key :id, :integer, :null => false
483
+ column :name, :text
484
+ index :name, :unique => true
485
+ end
486
+
487
+ @db.sqls.should == [
488
+ 'CREATE TEMPORARY TABLE test_tmp (id integer NOT NULL PRIMARY KEY AUTOINCREMENT, name text)',
489
+ 'CREATE UNIQUE INDEX test_tmp_name_index ON test_tmp (name)'
490
+ ]
491
+ end
492
+ end
493
+
494
+ context "Database#alter_table" do
495
+ before do
496
+ @db = DummyDatabase.new
497
+ end
498
+
499
+ specify "should construct proper SQL" do
500
+ @db.alter_table :xyz do
501
+ add_column :aaa, :text, :null => false, :unique => true
502
+ drop_column :bbb
503
+ rename_column :ccc, :ddd
504
+ set_column_type :eee, :integer
505
+ set_column_default :hhh, 'abcd'
506
+
507
+ add_index :fff, :unique => true
508
+ drop_index :ggg
509
+ end
510
+
511
+ @db.sqls.should == [
512
+ 'ALTER TABLE xyz ADD COLUMN aaa text UNIQUE NOT NULL',
513
+ 'ALTER TABLE xyz DROP COLUMN bbb',
514
+ 'ALTER TABLE xyz RENAME COLUMN ccc TO ddd',
515
+ 'ALTER TABLE xyz ALTER COLUMN eee TYPE integer',
516
+ "ALTER TABLE xyz ALTER COLUMN hhh SET DEFAULT 'abcd'",
517
+
518
+ 'CREATE UNIQUE INDEX xyz_fff_index ON xyz (fff)',
519
+ 'DROP INDEX xyz_ggg_index'
520
+ ]
521
+ end
522
+ end
523
+
524
+ context "Database#add_column" do
525
+ before do
526
+ @db = DummyDatabase.new
527
+ end
528
+
529
+ specify "should construct proper SQL" do
530
+ @db.add_column :test, :name, :text, :unique => true
531
+ @db.sqls.should == [
532
+ 'ALTER TABLE test ADD COLUMN name text UNIQUE'
533
+ ]
534
+ end
535
+ end
536
+
537
+ context "Database#drop_column" do
538
+ before do
539
+ @db = DummyDatabase.new
540
+ end
541
+
542
+ specify "should construct proper SQL" do
543
+ @db.drop_column :test, :name
544
+ @db.sqls.should == [
545
+ 'ALTER TABLE test DROP COLUMN name'
546
+ ]
547
+ end
548
+ end
549
+
550
+ context "Database#rename_column" do
551
+ before do
552
+ @db = DummyDatabase.new
553
+ end
554
+
555
+ specify "should construct proper SQL" do
556
+ @db.rename_column :test, :abc, :def
557
+ @db.sqls.should == [
558
+ 'ALTER TABLE test RENAME COLUMN abc TO def'
559
+ ]
560
+ end
561
+ end
562
+
563
+ context "Database#set_column_type" do
564
+ before do
565
+ @db = DummyDatabase.new
566
+ end
567
+
568
+ specify "should construct proper SQL" do
569
+ @db.set_column_type :test, :name, :integer
570
+ @db.sqls.should == [
571
+ 'ALTER TABLE test ALTER COLUMN name TYPE integer'
572
+ ]
573
+ end
574
+ end
575
+
576
+ context "Database#set_column_default" do
577
+ before do
578
+ @db = DummyDatabase.new
579
+ end
580
+
581
+ specify "should construct proper SQL" do
582
+ @db.set_column_default :test, :name, 'zyx'
583
+ @db.sqls.should == [
584
+ "ALTER TABLE test ALTER COLUMN name SET DEFAULT 'zyx'"
585
+ ]
586
+ end
587
+ end
588
+
589
+ context "Database#add_index" do
590
+ before do
591
+ @db = DummyDatabase.new
592
+ end
593
+
594
+ specify "should construct proper SQL" do
595
+ @db.add_index :test, :name, :unique => true
596
+ @db.sqls.should == [
597
+ 'CREATE UNIQUE INDEX test_name_index ON test (name)'
598
+ ]
599
+ end
600
+
601
+ specify "should accept multiple columns" do
602
+ @db.add_index :test, [:one, :two]
603
+ @db.sqls.should == [
604
+ 'CREATE INDEX test_one_two_index ON test (one, two)'
605
+ ]
606
+ end
607
+ end
608
+
609
+ context "Database#drop_index" do
610
+ before do
611
+ @db = DummyDatabase.new
612
+ end
613
+
614
+ specify "should construct proper SQL" do
615
+ @db.drop_index :test, :name
616
+ @db.sqls.should == [
617
+ 'DROP INDEX test_name_index'
618
+ ]
619
+ end
620
+
621
+ end
622
+
623
+ class Dummy2Database < Sequel::Database
624
+ attr_reader :sql
625
+ def execute(sql); @sql = sql; end
626
+ def transaction; yield; end
627
+ end
628
+
629
+ context "Database#drop_table" do
630
+ before do
631
+ @db = DummyDatabase.new
632
+ end
633
+
634
+ specify "should construct proper SQL" do
635
+ @db.drop_table :test
636
+ @db.sqls.should == ['DROP TABLE test']
637
+ end
638
+
639
+ specify "should accept multiple table names" do
640
+ @db.drop_table :a, :bb, :ccc
641
+ @db.sqls.should == [
642
+ 'DROP TABLE a',
643
+ 'DROP TABLE bb',
644
+ 'DROP TABLE ccc'
645
+ ]
646
+ end
647
+ end
648
+
649
+ context "Database#rename_table" do
650
+ before do
651
+ @db = DummyDatabase.new
652
+ end
653
+
654
+ specify "should construct proper SQL" do
655
+ @db.rename_table :abc, :xyz
656
+ @db.sqls.should == ['ALTER TABLE abc RENAME TO xyz']
657
+ end
658
+ end
659
+
660
+ context "Database#table_exists?" do
661
+ specify "should try to select the first record from the table's dataset" do
662
+ db2 = DummyDatabase.new
663
+ db2.table_exists?(:a).should be_false
664
+ db2.table_exists?(:b).should be_true
665
+ end
666
+ end
667
+
668
+ class Dummy3Database < Sequel::Database
669
+ attr_reader :sql, :transactions
670
+ def execute(sql, opts={}); @sql ||= []; @sql << sql; end
671
+
672
+ class DummyConnection
673
+ def initialize(db); @db = db; end
674
+ def execute(sql); @db.execute(sql); end
675
+ end
676
+ end
677
+
678
+ context "Database#transaction" do
679
+ before do
680
+ @db = Dummy3Database.new{Dummy3Database::DummyConnection.new(@db)}
681
+ end
682
+
683
+ specify "should wrap the supplied block with BEGIN + COMMIT statements" do
684
+ @db.transaction {@db.execute 'DROP TABLE test;'}
685
+ @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
686
+ end
687
+
688
+ specify "should handle returning inside of the block by committing" do
689
+ def @db.ret_commit
690
+ transaction do
691
+ execute 'DROP TABLE test;'
692
+ return
693
+ execute 'DROP TABLE test2;';
694
+ end
695
+ end
696
+ @db.ret_commit
697
+ @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
698
+ end
699
+
700
+ specify "should issue ROLLBACK if an exception is raised, and re-raise" do
701
+ @db.transaction {@db.execute 'DROP TABLE test'; raise RuntimeError} rescue nil
702
+ @db.sql.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
703
+
704
+ proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
705
+ end
706
+
707
+ specify "should issue ROLLBACK if Sequel::Rollback is called in the transaction" do
708
+ @db.transaction do
709
+ @db.drop_table(:a)
710
+ raise Sequel::Rollback
711
+ @db.drop_table(:b)
712
+ end
713
+
714
+ @db.sql.should == ['BEGIN', 'DROP TABLE a', 'ROLLBACK']
715
+ end
716
+
717
+ specify "should raise database errors when commiting a transaction as Sequel::DatabaseError" do
718
+ @db.meta_def(:commit_transaction){raise ArgumentError}
719
+ lambda{@db.transaction{}}.should raise_error(ArgumentError)
720
+
721
+ @db.meta_def(:database_error_classes){[ArgumentError]}
722
+ lambda{@db.transaction{}}.should raise_error(Sequel::DatabaseError)
723
+ end
724
+
725
+ specify "should be re-entrant" do
726
+ stop = false
727
+ cc = nil
728
+ t = Thread.new do
729
+ @db.transaction {@db.transaction {@db.transaction {|c|
730
+ cc = c
731
+ while !stop; sleep 0.1; end
732
+ }}}
733
+ end
734
+ while cc.nil?; sleep 0.1; end
735
+ cc.should be_a_kind_of(Dummy3Database::DummyConnection)
736
+ @db.transactions.should == [t]
737
+ stop = true
738
+ t.join
739
+ @db.transactions.should be_empty
740
+ end
741
+ end
742
+
743
+ context "Database#transaction with savepoints" do
744
+ before do
745
+ @db = Dummy3Database.new{Dummy3Database::DummyConnection.new(@db)}
746
+ @db.meta_def(:supports_savepoints?){true}
747
+ end
748
+
749
+ specify "should wrap the supplied block with BEGIN + COMMIT statements" do
750
+ @db.transaction {@db.execute 'DROP TABLE test;'}
751
+ @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
752
+ end
753
+
754
+ specify "should use savepoints if given the :savepoint option" do
755
+ @db.transaction{@db.transaction(:savepoint=>true){@db.execute 'DROP TABLE test;'}}
756
+ @db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test;', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT']
757
+ end
758
+
759
+ specify "should not use a savepoints if no transaction is in progress" do
760
+ @db.transaction(:savepoint=>true){@db.execute 'DROP TABLE test;'}
761
+ @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
762
+ end
763
+
764
+ specify "should reuse the current transaction if no :savepoint option is given" do
765
+ @db.transaction{@db.transaction{@db.execute 'DROP TABLE test;'}}
766
+ @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
767
+ end
768
+
769
+ specify "should handle returning inside of the block by committing" do
770
+ def @db.ret_commit
771
+ transaction do
772
+ execute 'DROP TABLE test;'
773
+ return
774
+ execute 'DROP TABLE test2;';
775
+ end
776
+ end
777
+ @db.ret_commit
778
+ @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
779
+ end
780
+
781
+ specify "should handle returning inside of a savepoint by committing" do
782
+ def @db.ret_commit
783
+ transaction do
784
+ transaction(:savepoint=>true) do
785
+ execute 'DROP TABLE test;'
786
+ return
787
+ execute 'DROP TABLE test2;';
788
+ end
789
+ end
790
+ end
791
+ @db.ret_commit
792
+ @db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test;', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT']
793
+ end
794
+
795
+ specify "should issue ROLLBACK if an exception is raised, and re-raise" do
796
+ @db.transaction {@db.execute 'DROP TABLE test'; raise RuntimeError} rescue nil
797
+ @db.sql.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
798
+
799
+ proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
800
+ end
801
+
802
+ specify "should issue ROLLBACK SAVEPOINT if an exception is raised inside a savepoint, and re-raise" do
803
+ @db.transaction{@db.transaction(:savepoint=>true){@db.execute 'DROP TABLE test'; raise RuntimeError}} rescue nil
804
+ @db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test', 'ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK']
805
+
806
+ proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
807
+ end
808
+
809
+ specify "should issue ROLLBACK if Sequel::Rollback is called in the transaction" do
810
+ @db.transaction do
811
+ @db.drop_table(:a)
812
+ raise Sequel::Rollback
813
+ @db.drop_table(:b)
814
+ end
815
+
816
+ @db.sql.should == ['BEGIN', 'DROP TABLE a', 'ROLLBACK']
817
+ end
818
+
819
+ specify "should issue ROLLBACK SAVEPOINT if Sequel::Rollback is called in a savepoint" do
820
+ @db.transaction do
821
+ @db.transaction(:savepoint=>true) do
822
+ @db.drop_table(:a)
823
+ raise Sequel::Rollback
824
+ end
825
+ @db.drop_table(:b)
826
+ end
827
+
828
+ @db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE a', 'ROLLBACK TO SAVEPOINT autopoint_1', 'DROP TABLE b', 'COMMIT']
829
+ end
830
+
831
+ specify "should raise database errors when commiting a transaction as Sequel::DatabaseError" do
832
+ @db.meta_def(:commit_transaction){raise ArgumentError}
833
+ lambda{@db.transaction{}}.should raise_error(ArgumentError)
834
+ lambda{@db.transaction{@db.transaction(:savepoint=>true){}}}.should raise_error(ArgumentError)
835
+
836
+ @db.meta_def(:database_error_classes){[ArgumentError]}
837
+ lambda{@db.transaction{}}.should raise_error(Sequel::DatabaseError)
838
+ lambda{@db.transaction{@db.transaction(:savepoint=>true){}}}.should raise_error(Sequel::DatabaseError)
839
+ end
840
+ end
841
+
842
+ context "A Database adapter with a scheme" do
843
+ before do
844
+ class CCC < Sequel::Database
845
+ if defined?(DISCONNECTS)
846
+ DISCONNECTS.clear
847
+ else
848
+ DISCONNECTS = []
849
+ end
850
+ set_adapter_scheme :ccc
851
+ def disconnect
852
+ DISCONNECTS << self
853
+ end
854
+ end
855
+ end
856
+
857
+ specify "should be registered in the ADAPTER_MAP" do
858
+ Sequel::ADAPTER_MAP[:ccc].should == CCC
859
+ end
860
+
861
+ specify "should give the database_type as the adapter scheme by default" do
862
+ CCC.new.database_type.should == :ccc
863
+ end
864
+
865
+ specify "should be instantiated when its scheme is specified" do
866
+ c = Sequel::Database.connect('ccc://localhost/db')
867
+ c.should be_a_kind_of(CCC)
868
+ c.opts[:host].should == 'localhost'
869
+ c.opts[:database].should == 'db'
870
+ end
871
+
872
+ specify "should be accessible through Sequel.connect" do
873
+ c = Sequel.connect 'ccc://localhost/db'
874
+ c.should be_a_kind_of(CCC)
875
+ c.opts[:host].should == 'localhost'
876
+ c.opts[:database].should == 'db'
877
+ end
878
+
879
+ specify "should be accessible through Sequel.connect via a block" do
880
+ x = nil
881
+ y = nil
882
+ z = nil
883
+ returnValue = 'anything'
884
+
885
+ p = proc do |c|
886
+ c.should be_a_kind_of(CCC)
887
+ c.opts[:host].should == 'localhost'
888
+ c.opts[:database].should == 'db'
889
+ z = y
890
+ y = x
891
+ x = c
892
+ returnValue
893
+ end
894
+ Sequel::Database.connect('ccc://localhost/db', &p).should == returnValue
895
+ CCC::DISCONNECTS.should == [x]
896
+
897
+ Sequel.connect('ccc://localhost/db', &p).should == returnValue
898
+ CCC::DISCONNECTS.should == [y, x]
899
+
900
+ Sequel.send(:def_adapter_method, :ccc)
901
+ Sequel.ccc('db', :host=>'localhost', &p).should == returnValue
902
+ CCC::DISCONNECTS.should == [z, y, x]
903
+ end
904
+
905
+ specify "should be accessible through Sequel.<adapter>" do
906
+ Sequel.send(:def_adapter_method, :ccc)
907
+
908
+ # invalid parameters
909
+ proc {Sequel.ccc('abc', 'def')}.should raise_error(Sequel::Error)
910
+
911
+ c = Sequel.ccc('mydb')
912
+ p = proc{c.opts.delete_if{|k,v| k == :disconnection_proc || k == :single_threaded}}
913
+ c.should be_a_kind_of(CCC)
914
+ p.call.should == {:adapter=>:ccc, :database => 'mydb'}
915
+
916
+ c = Sequel.ccc('mydb', :host => 'localhost')
917
+ c.should be_a_kind_of(CCC)
918
+ p.call.should == {:adapter=>:ccc, :database => 'mydb', :host => 'localhost'}
919
+
920
+ c = Sequel.ccc
921
+ c.should be_a_kind_of(CCC)
922
+ p.call.should == {:adapter=>:ccc}
923
+
924
+ c = Sequel.ccc(:database => 'mydb', :host => 'localhost')
925
+ c.should be_a_kind_of(CCC)
926
+ p.call.should == {:adapter=>:ccc, :database => 'mydb', :host => 'localhost'}
927
+ end
928
+
929
+ specify "should be accessible through Sequel.connect with options" do
930
+ c = Sequel.connect(:adapter => :ccc, :database => 'mydb')
931
+ c.should be_a_kind_of(CCC)
932
+ c.opts[:adapter].should == :ccc
933
+ end
934
+
935
+ specify "should be accessible through Sequel.connect with URL parameters" do
936
+ c = Sequel.connect 'ccc:///db?host=/tmp&user=test'
937
+ c.should be_a_kind_of(CCC)
938
+ c.opts[:host].should == '/tmp'
939
+ c.opts[:database].should == 'db'
940
+ c.opts[:user].should == 'test'
941
+ end
942
+
943
+ specify "should have URL parameters take precedence over fixed URL parts" do
944
+ c = Sequel.connect 'ccc://localhost/db?host=a&database=b'
945
+ c.should be_a_kind_of(CCC)
946
+ c.opts[:host].should == 'a'
947
+ c.opts[:database].should == 'b'
948
+ end
949
+
950
+ specify "should have hash options take predence over URL parameters or parts" do
951
+ c = Sequel.connect 'ccc://localhost/db?host=/tmp', :host=>'a', :database=>'b', :user=>'c'
952
+ c.should be_a_kind_of(CCC)
953
+ c.opts[:host].should == 'a'
954
+ c.opts[:database].should == 'b'
955
+ c.opts[:user].should == 'c'
956
+ end
957
+
958
+ specify "should unescape values of URL parameters and parts" do
959
+ c = Sequel.connect 'ccc:///d%5bb%5d?host=domain%5cinstance'
960
+ c.should be_a_kind_of(CCC)
961
+ c.opts[:database].should == 'd[b]'
962
+ c.opts[:host].should == 'domain\\instance'
963
+ end
964
+
965
+ specify "should test the connection if test parameter is truthy" do
966
+ proc{Sequel.connect 'ccc:///d%5bb%5d?test=t'}.should raise_error(Sequel::DatabaseConnectionError)
967
+ proc{Sequel.connect 'ccc:///d%5bb%5d?test=1'}.should raise_error(Sequel::DatabaseConnectionError)
968
+ proc{Sequel.connect 'ccc:///d%5bb%5d', :test=>true}.should raise_error(Sequel::DatabaseConnectionError)
969
+ proc{Sequel.connect 'ccc:///d%5bb%5d', :test=>'t'}.should raise_error(Sequel::DatabaseConnectionError)
970
+ end
971
+
972
+ specify "should not test the connection if test parameter is not truthy" do
973
+ proc{Sequel.connect 'ccc:///d%5bb%5d?test=f'}.should_not raise_error
974
+ proc{Sequel.connect 'ccc:///d%5bb%5d?test=0'}.should_not raise_error
975
+ proc{Sequel.connect 'ccc:///d%5bb%5d', :test=>false}.should_not raise_error
976
+ proc{Sequel.connect 'ccc:///d%5bb%5d', :test=>'f'}.should_not raise_error
977
+ end
978
+ end
979
+
980
+ context "Sequel::Database.connect" do
981
+ specify "should raise an Error if not given a String or Hash" do
982
+ proc{Sequel::Database.connect(nil)}.should raise_error(Sequel::Error)
983
+ proc{Sequel::Database.connect(Object.new)}.should raise_error(Sequel::Error)
984
+ end
985
+ end
986
+
987
+ context "An unknown database scheme" do
988
+ specify "should raise an error in Sequel::Database.connect" do
989
+ proc {Sequel::Database.connect('ddd://localhost/db')}.should raise_error(Sequel::AdapterNotFound)
990
+ end
991
+
992
+ specify "should raise an error in Sequel.connect" do
993
+ proc {Sequel.connect('ddd://localhost/db')}.should raise_error(Sequel::AdapterNotFound)
994
+ end
995
+ end
996
+
997
+ context "A broken adapter (lib is there but the class is not)" do
998
+ before do
999
+ @fn = File.join(File.dirname(__FILE__), '../../lib/sequel/adapters/blah.rb')
1000
+ File.open(@fn,'a'){}
1001
+ end
1002
+
1003
+ after do
1004
+ File.delete(@fn)
1005
+ end
1006
+
1007
+ specify "should raise an error" do
1008
+ proc {Sequel.connect('blah://blow')}.should raise_error(Sequel::AdapterNotFound)
1009
+ end
1010
+ end
1011
+
1012
+ context "A single threaded database" do
1013
+ after do
1014
+ Sequel::Database.single_threaded = false
1015
+ end
1016
+
1017
+ specify "should use a SingleConnectionPool instead of a ConnectionPool" do
1018
+ db = Sequel::Database.new(:single_threaded => true){123}
1019
+ db.pool.should be_a_kind_of(Sequel::SingleConnectionPool)
1020
+ end
1021
+
1022
+ specify "should be constructable using :single_threaded => true option" do
1023
+ db = Sequel::Database.new(:single_threaded => true){123}
1024
+ db.pool.should be_a_kind_of(Sequel::SingleConnectionPool)
1025
+ end
1026
+
1027
+ specify "should be constructable using Database.single_threaded = true" do
1028
+ Sequel::Database.single_threaded = true
1029
+ db = Sequel::Database.new{123}
1030
+ db.pool.should be_a_kind_of(Sequel::SingleConnectionPool)
1031
+ end
1032
+
1033
+ specify "should be constructable using Sequel.single_threaded = true" do
1034
+ Sequel.single_threaded = true
1035
+ db = Sequel::Database.new{123}
1036
+ db.pool.should be_a_kind_of(Sequel::SingleConnectionPool)
1037
+ end
1038
+ end
1039
+
1040
+ context "A single threaded database" do
1041
+ before do
1042
+ conn = 1234567
1043
+ @db = Sequel::Database.new(:single_threaded => true) do
1044
+ conn += 1
1045
+ end
1046
+ end
1047
+
1048
+ specify "should invoke connection_proc only once" do
1049
+ @db.pool.hold {|c| c.should == 1234568}
1050
+ @db.pool.hold {|c| c.should == 1234568}
1051
+ end
1052
+
1053
+ specify "should disconnect correctly" do
1054
+ def @db.disconnect_connection(c); @dc = c end
1055
+ def @db.dc; @dc end
1056
+ x = nil
1057
+ @db.pool.hold{|c| x = c}
1058
+ @db.pool.hold{|c| c.should == x}
1059
+ proc{@db.disconnect}.should_not raise_error
1060
+ @db.dc.should == x
1061
+ end
1062
+
1063
+ specify "should convert an Exception on connection into a DatabaseConnectionError" do
1064
+ db = Sequel::Database.new(:single_threaded => true, :servers=>{}){raise Exception}
1065
+ proc {db.pool.hold {|c|}}.should raise_error(Sequel::DatabaseConnectionError)
1066
+ end
1067
+
1068
+ specify "should raise a DatabaseConnectionError if the connection proc returns nil" do
1069
+ db = Sequel::Database.new(:single_threaded => true, :servers=>{}){nil}
1070
+ proc {db.pool.hold {|c|}}.should raise_error(Sequel::DatabaseConnectionError)
1071
+ end
1072
+ end
1073
+
1074
+ context "A database" do
1075
+ before do
1076
+ Sequel::Database.single_threaded = false
1077
+ end
1078
+
1079
+ after do
1080
+ Sequel::Database.single_threaded = false
1081
+ end
1082
+
1083
+ specify "should have single_threaded? respond to true if in single threaded mode" do
1084
+ db = Sequel::Database.new(:single_threaded => true){1234}
1085
+ db.should be_single_threaded
1086
+
1087
+ db = Sequel::Database.new(:max_options => 1)
1088
+ db.should_not be_single_threaded
1089
+
1090
+ db = Sequel::Database.new
1091
+ db.should_not be_single_threaded
1092
+
1093
+ Sequel::Database.single_threaded = true
1094
+
1095
+ db = Sequel::Database.new{123}
1096
+ db.should be_single_threaded
1097
+
1098
+ db = Sequel::Database.new(:max_options => 4){123}
1099
+ db.should be_single_threaded
1100
+ end
1101
+
1102
+ specify "should be able to set loggers via the logger= and loggers= methods" do
1103
+ db = Sequel::Database.new
1104
+ s = "I'm a logger"
1105
+ db.logger = s
1106
+ db.loggers.should == [s]
1107
+ db.logger = nil
1108
+ db.loggers.should == []
1109
+
1110
+ db.loggers = [s]
1111
+ db.loggers.should == [s]
1112
+ db.loggers = []
1113
+ db.loggers.should == []
1114
+
1115
+ t = "I'm also a logger"
1116
+ db.loggers = [s, t]
1117
+ db.loggers.should == [s,t]
1118
+ end
1119
+ end
1120
+
1121
+ context "Database#fetch" do
1122
+ before do
1123
+ @db = Sequel::Database.new
1124
+ c = Class.new(Sequel::Dataset) do
1125
+ def fetch_rows(sql); yield({:sql => sql}); end
1126
+ end
1127
+ @db.meta_def(:dataset) {c.new(self)}
1128
+ end
1129
+
1130
+ specify "should create a dataset and invoke its fetch_rows method with the given sql" do
1131
+ sql = nil
1132
+ @db.fetch('select * from xyz') {|r| sql = r[:sql]}
1133
+ sql.should == 'select * from xyz'
1134
+ end
1135
+
1136
+ specify "should format the given sql with any additional arguments" do
1137
+ sql = nil
1138
+ @db.fetch('select * from xyz where x = ? and y = ?', 15, 'abc') {|r| sql = r[:sql]}
1139
+ sql.should == "select * from xyz where x = 15 and y = 'abc'"
1140
+
1141
+ @db.fetch('select name from table where name = ? or id in ?', 'aman', [3,4,7]) {|r| sql = r[:sql]}
1142
+ sql.should == "select name from table where name = 'aman' or id in (3, 4, 7)"
1143
+ end
1144
+
1145
+ specify "should format the given sql with named arguments" do
1146
+ sql = nil
1147
+ @db.fetch('select * from xyz where x = :x and y = :y', :x=>15, :y=>'abc') {|r| sql = r[:sql]}
1148
+ sql.should == "select * from xyz where x = 15 and y = 'abc'"
1149
+ end
1150
+
1151
+ specify "should return the dataset if no block is given" do
1152
+ @db.fetch('select * from xyz').should be_a_kind_of(Sequel::Dataset)
1153
+
1154
+ @db.fetch('select a from b').map {|r| r[:sql]}.should == ['select a from b']
1155
+
1156
+ @db.fetch('select c from d').inject([]) {|m, r| m << r; m}.should == \
1157
+ [{:sql => 'select c from d'}]
1158
+ end
1159
+
1160
+ specify "should return a dataset that always uses the given sql for SELECTs" do
1161
+ ds = @db.fetch('select * from xyz')
1162
+ ds.select_sql.should == 'select * from xyz'
1163
+ ds.sql.should == 'select * from xyz'
1164
+
1165
+ ds.filter!(:price.sql_number < 100)
1166
+ ds.select_sql.should == 'select * from xyz'
1167
+ ds.sql.should == 'select * from xyz'
1168
+ end
1169
+ end
1170
+
1171
+
1172
+ context "Database#[]" do
1173
+ before do
1174
+ @db = Sequel::Database.new
1175
+ end
1176
+
1177
+ specify "should return a dataset when symbols are given" do
1178
+ ds = @db[:items]
1179
+ ds.class.should == Sequel::Dataset
1180
+ ds.opts[:from].should == [:items]
1181
+ end
1182
+
1183
+ specify "should return a dataset when a string is given" do
1184
+ c = Class.new(Sequel::Dataset) do
1185
+ def fetch_rows(sql); yield({:sql => sql}); end
1186
+ end
1187
+ @db.meta_def(:dataset) {c.new(self)}
1188
+
1189
+ sql = nil
1190
+ @db['select * from xyz where x = ? and y = ?', 15, 'abc'].each {|r| sql = r[:sql]}
1191
+ sql.should == "select * from xyz where x = 15 and y = 'abc'"
1192
+ end
1193
+ end
1194
+
1195
+ context "Database#create_view" do
1196
+ before do
1197
+ @db = DummyDatabase.new
1198
+ end
1199
+
1200
+ specify "should construct proper SQL with raw SQL" do
1201
+ @db.create_view :test, "SELECT * FROM xyz"
1202
+ @db.sqls.should == ['CREATE VIEW test AS SELECT * FROM xyz']
1203
+ @db.sqls.clear
1204
+ @db.create_view :test.identifier, "SELECT * FROM xyz"
1205
+ @db.sqls.should == ['CREATE VIEW test AS SELECT * FROM xyz']
1206
+ end
1207
+
1208
+ specify "should construct proper SQL with dataset" do
1209
+ @db.create_view :test, @db[:items].select(:a, :b).order(:c)
1210
+ @db.sqls.should == ['CREATE VIEW test AS SELECT a, b FROM items ORDER BY c']
1211
+ @db.sqls.clear
1212
+ @db.create_view :test.qualify(:sch), @db[:items].select(:a, :b).order(:c)
1213
+ @db.sqls.should == ['CREATE VIEW sch.test AS SELECT a, b FROM items ORDER BY c']
1214
+ end
1215
+ end
1216
+
1217
+ context "Database#create_or_replace_view" do
1218
+ before do
1219
+ @db = DummyDatabase.new
1220
+ end
1221
+
1222
+ specify "should construct proper SQL with raw SQL" do
1223
+ @db.create_or_replace_view :test, "SELECT * FROM xyz"
1224
+ @db.sqls.should == ['CREATE OR REPLACE VIEW test AS SELECT * FROM xyz']
1225
+ @db.sqls.clear
1226
+ @db.create_or_replace_view :sch__test, "SELECT * FROM xyz"
1227
+ @db.sqls.should == ['CREATE OR REPLACE VIEW sch.test AS SELECT * FROM xyz']
1228
+ end
1229
+
1230
+ specify "should construct proper SQL with dataset" do
1231
+ @db.create_or_replace_view :test, @db[:items].select(:a, :b).order(:c)
1232
+ @db.sqls.should == ['CREATE OR REPLACE VIEW test AS SELECT a, b FROM items ORDER BY c']
1233
+ @db.sqls.clear
1234
+ @db.create_or_replace_view :test.identifier, @db[:items].select(:a, :b).order(:c)
1235
+ @db.sqls.should == ['CREATE OR REPLACE VIEW test AS SELECT a, b FROM items ORDER BY c']
1236
+ end
1237
+ end
1238
+
1239
+ context "Database#drop_view" do
1240
+ before do
1241
+ @db = DummyDatabase.new
1242
+ end
1243
+
1244
+ specify "should construct proper SQL" do
1245
+ @db.drop_view :test
1246
+ @db.drop_view :test.identifier
1247
+ @db.drop_view :sch__test
1248
+ @db.drop_view :test.qualify(:sch)
1249
+ @db.sqls.should == ['DROP VIEW test', 'DROP VIEW test', 'DROP VIEW sch.test', 'DROP VIEW sch.test']
1250
+ end
1251
+ end
1252
+
1253
+ context "Database#alter_table_sql" do
1254
+ before do
1255
+ @db = DummyDatabase.new
1256
+ end
1257
+
1258
+ specify "should raise error for an invalid op" do
1259
+ proc {@db.send(:alter_table_sql, :mau, :op => :blah)}.should raise_error(Sequel::Error)
1260
+ end
1261
+ end
1262
+
1263
+ context "Database#inspect" do
1264
+ before do
1265
+ @db = DummyDatabase.new
1266
+
1267
+ @db.meta_def(:uri) {'blah://blahblah/blah'}
1268
+ end
1269
+
1270
+ specify "should include the class name and the connection url" do
1271
+ @db.inspect.should == '#<DummyDatabase: "blah://blahblah/blah">'
1272
+ end
1273
+ end
1274
+
1275
+ context "Database#get" do
1276
+ before do
1277
+ @c = Class.new(DummyDatabase) do
1278
+ def dataset
1279
+ ds = super
1280
+ def ds.get(*args, &block)
1281
+ @db.execute select(*args, &block).sql
1282
+ args
1283
+ end
1284
+ ds
1285
+ end
1286
+ end
1287
+
1288
+ @db = @c.new
1289
+ end
1290
+
1291
+ specify "should use Dataset#get to get a single value" do
1292
+ @db.get(1).should == [1]
1293
+ @db.sqls.last.should == 'SELECT 1'
1294
+
1295
+ @db.get(:version.sql_function)
1296
+ @db.sqls.last.should == 'SELECT version()'
1297
+ end
1298
+
1299
+ specify "should accept a block" do
1300
+ @db.get{1}
1301
+ @db.sqls.last.should == 'SELECT 1'
1302
+
1303
+ @db.get{version(1)}
1304
+ @db.sqls.last.should == 'SELECT version(1)'
1305
+ end
1306
+ end
1307
+
1308
+ context "Database#call" do
1309
+ specify "should call the prepared statement with the given name" do
1310
+ db = MockDatabase.new
1311
+ db[:items].prepare(:select, :select_all)
1312
+ db.call(:select_all).should == [{:id => 1, :x => 1}]
1313
+ db[:items].filter(:n=>:$n).prepare(:select, :select_n)
1314
+ db.call(:select_n, :n=>1).should == [{:id => 1, :x => 1}]
1315
+ db.sqls.should == ['SELECT * FROM items', 'SELECT * FROM items WHERE (n = 1)']
1316
+ end
1317
+ end
1318
+
1319
+ context "Database#server_opts" do
1320
+ specify "should return the general opts if no :servers option is used" do
1321
+ opts = {:host=>1, :database=>2}
1322
+ MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 1
1323
+ end
1324
+
1325
+ specify "should return the general opts if entry for the server is present in the :servers option" do
1326
+ opts = {:host=>1, :database=>2, :servers=>{}}
1327
+ MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 1
1328
+ end
1329
+
1330
+ specify "should return the general opts merged with the specific opts if given as a hash" do
1331
+ opts = {:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}}}
1332
+ MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 3
1333
+ end
1334
+
1335
+ specify "should return the sgeneral opts merged with the specific opts if given as a proc" do
1336
+ opts = {:host=>1, :database=>2, :servers=>{:server1=>proc{|db| {:host=>4}}}}
1337
+ MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 4
1338
+ end
1339
+
1340
+ specify "should raise an error if the specific opts is not a proc or hash" do
1341
+ opts = {:host=>1, :database=>2, :servers=>{:server1=>2}}
1342
+ proc{MockDatabase.new(opts).send(:server_opts, :server1)}.should raise_error(Sequel::Error)
1343
+ end
1344
+ end
1345
+
1346
+ context "Database#add_servers" do
1347
+ before do
1348
+ @db = MockDatabase.new(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}})
1349
+ def @db.connect(server)
1350
+ server_opts(server)
1351
+ end
1352
+ def @db.disconnect_connection(c)
1353
+ end
1354
+ end
1355
+
1356
+ specify "should add new servers to the connection pool" do
1357
+ @db.synchronize{|c| c[:host].should == 1}
1358
+ @db.synchronize(:server1){|c| c[:host].should == 3}
1359
+ @db.synchronize(:server2){|c| c[:host].should == 1}
1360
+
1361
+ @db.add_servers(:server2=>{:host=>6})
1362
+ @db.synchronize{|c| c[:host].should == 1}
1363
+ @db.synchronize(:server1){|c| c[:host].should == 3}
1364
+ @db.synchronize(:server2){|c| c[:host].should == 6}
1365
+
1366
+ @db.disconnect
1367
+ @db.synchronize{|c| c[:host].should == 1}
1368
+ @db.synchronize(:server1){|c| c[:host].should == 3}
1369
+ @db.synchronize(:server2){|c| c[:host].should == 6}
1370
+ end
1371
+
1372
+ specify "should replace options for future connections to existing servers" do
1373
+ @db.synchronize{|c| c[:host].should == 1}
1374
+ @db.synchronize(:server1){|c| c[:host].should == 3}
1375
+ @db.synchronize(:server2){|c| c[:host].should == 1}
1376
+
1377
+ @db.add_servers(:default=>proc{{:host=>4}}, :server1=>{:host=>8})
1378
+ @db.synchronize{|c| c[:host].should == 1}
1379
+ @db.synchronize(:server1){|c| c[:host].should == 3}
1380
+ @db.synchronize(:server2){|c| c[:host].should == 1}
1381
+
1382
+ @db.disconnect
1383
+ @db.synchronize{|c| c[:host].should == 4}
1384
+ @db.synchronize(:server1){|c| c[:host].should == 8}
1385
+ @db.synchronize(:server2){|c| c[:host].should == 4}
1386
+ end
1387
+ end
1388
+
1389
+ context "Database#remove_servers" do
1390
+ before do
1391
+ @db = MockDatabase.new(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}, :server2=>{:host=>4}})
1392
+ def @db.connect(server)
1393
+ server_opts(server)
1394
+ end
1395
+ def @db.disconnect_connection(c)
1396
+ end
1397
+ end
1398
+
1399
+ specify "should remove servers from the connection pool" do
1400
+ @db.synchronize{|c| c[:host].should == 1}
1401
+ @db.synchronize(:server1){|c| c[:host].should == 3}
1402
+ @db.synchronize(:server2){|c| c[:host].should == 4}
1403
+
1404
+ @db.remove_servers(:server1, :server2)
1405
+ @db.synchronize{|c| c[:host].should == 1}
1406
+ @db.synchronize(:server1){|c| c[:host].should == 1}
1407
+ @db.synchronize(:server2){|c| c[:host].should == 1}
1408
+ end
1409
+
1410
+ specify "should accept arrays of symbols" do
1411
+ @db.remove_servers([:server1, :server2])
1412
+ @db.synchronize{|c| c[:host].should == 1}
1413
+ @db.synchronize(:server1){|c| c[:host].should == 1}
1414
+ @db.synchronize(:server2){|c| c[:host].should == 1}
1415
+ end
1416
+
1417
+ specify "should allow removal while connections are still open" do
1418
+ @db.synchronize do |c1|
1419
+ c1[:host].should == 1
1420
+ @db.synchronize(:server1) do |c2|
1421
+ c2[:host].should == 3
1422
+ @db.synchronize(:server2) do |c3|
1423
+ c3[:host].should == 4
1424
+ @db.remove_servers(:server1, :server2)
1425
+ @db.synchronize(:server1) do |c4|
1426
+ c4.should_not == c2
1427
+ c4.should == c1
1428
+ c4[:host].should == 1
1429
+ @db.synchronize(:server2) do |c5|
1430
+ c5.should_not == c3
1431
+ c5.should == c1
1432
+ c5[:host].should == 1
1433
+ end
1434
+ end
1435
+ c3[:host].should == 4
1436
+ end
1437
+ c2[:host].should == 3
1438
+ end
1439
+ c1[:host].should == 1
1440
+ end
1441
+ end
1442
+ end
1443
+
1444
+ context "Database#each_server" do
1445
+ before do
1446
+ @db = Sequel.connect(:adapter=>:mock, :host=>1, :database=>2, :servers=>{:server1=>{:host=>3}, :server2=>{:host=>4}})
1447
+ def @db.connect(server)
1448
+ server_opts(server)
1449
+ end
1450
+ def @db.disconnect_connection(c)
1451
+ end
1452
+ end
1453
+
1454
+ specify "should yield a separate database object for each server" do
1455
+ hosts = []
1456
+ @db.each_server do |db|
1457
+ db.should be_a_kind_of(Sequel::Database)
1458
+ db.should_not == @db
1459
+ db.opts[:database].should == 2
1460
+ hosts << db.opts[:host]
1461
+ end
1462
+ hosts.sort.should == [1, 3, 4]
1463
+ end
1464
+
1465
+ specify "should disconnect and remove entry from Sequel::DATABASES after use" do
1466
+ dbs = []
1467
+ dcs = []
1468
+ @db.each_server do |db|
1469
+ dbs << db
1470
+ Sequel::DATABASES.should include(db)
1471
+ db.meta_def(:disconnect){dcs << db}
1472
+ end
1473
+ dbs.each do |db|
1474
+ Sequel::DATABASES.should_not include(db)
1475
+ end
1476
+ dbs.should == dcs
1477
+ end
1478
+ end
1479
+
1480
+ context "Database#raise_error" do
1481
+ specify "should reraise if the exception class is not in opts[:classes]" do
1482
+ e = Class.new(StandardError)
1483
+ proc{MockDatabase.new.send(:raise_error, e.new(''), :classes=>[])}.should raise_error(e)
1484
+ end
1485
+
1486
+ specify "should convert the exception to a DatabaseError if the exception class is not in opts[:classes]" do
1487
+ proc{MockDatabase.new.send(:raise_error, Interrupt.new(''), :classes=>[Interrupt])}.should raise_error(Sequel::DatabaseError)
1488
+ end
1489
+
1490
+ specify "should convert the exception to a DatabaseError if opts[:classes] if not present" do
1491
+ proc{MockDatabase.new.send(:raise_error, Interrupt.new(''))}.should raise_error(Sequel::DatabaseError)
1492
+ end
1493
+
1494
+ specify "should convert the exception to a DatabaseDisconnectError if opts[:disconnect] is true" do
1495
+ proc{MockDatabase.new.send(:raise_error, Interrupt.new(''), :disconnect=>true)}.should raise_error(Sequel::DatabaseDisconnectError)
1496
+ end
1497
+ end
1498
+
1499
+ context "Database#typecast_value" do
1500
+ before do
1501
+ @db = Sequel::Database.new
1502
+ end
1503
+
1504
+ specify "should raise an InvalidValue when given an invalid value" do
1505
+ proc{@db.typecast_value(:integer, "13a")}.should raise_error(Sequel::InvalidValue)
1506
+ proc{@db.typecast_value(:float, "4.e2")}.should raise_error(Sequel::InvalidValue)
1507
+ proc{@db.typecast_value(:decimal, :invalid_value)}.should raise_error(Sequel::InvalidValue)
1508
+ proc{@db.typecast_value(:date, Object.new)}.should raise_error(Sequel::InvalidValue)
1509
+ proc{@db.typecast_value(:date, 'a')}.should raise_error(Sequel::InvalidValue)
1510
+ proc{@db.typecast_value(:time, Date.new)}.should raise_error(Sequel::InvalidValue)
1511
+ proc{@db.typecast_value(:datetime, 4)}.should raise_error(Sequel::InvalidValue)
1512
+ end
1513
+
1514
+ specify "should have an underlying exception class available at wrapped_exception" do
1515
+ begin
1516
+ @db.typecast_value(:date, 'a')
1517
+ true.should == false
1518
+ rescue Sequel::InvalidValue => e
1519
+ e.wrapped_exception.should be_a_kind_of(ArgumentError)
1520
+ end
1521
+ end
1522
+
1523
+ specify "should include underlying exception class in #inspect" do
1524
+ begin
1525
+ @db.typecast_value(:date, 'a')
1526
+ true.should == false
1527
+ rescue Sequel::InvalidValue => e
1528
+ e.inspect.should =~ /\A#<Sequel::InvalidValue: ArgumentError: .*>\z/
1529
+ end
1530
+ end
1531
+ end
1532
+
1533
+ context "Database#blank_object?" do
1534
+ specify "should return whether the object is considered blank" do
1535
+ db = Sequel::Database.new
1536
+ c = lambda{|meth, value| Class.new{define_method(meth){value}}.new}
1537
+
1538
+ db.send(:blank_object?, "").should == true
1539
+ db.send(:blank_object?, " ").should == true
1540
+ db.send(:blank_object?, nil).should == true
1541
+ db.send(:blank_object?, false).should == true
1542
+ db.send(:blank_object?, []).should == true
1543
+ db.send(:blank_object?, {}).should == true
1544
+ db.send(:blank_object?, c[:empty?, true]).should == true
1545
+ db.send(:blank_object?, c[:blank?, true]).should == true
1546
+
1547
+ db.send(:blank_object?, " a ").should == false
1548
+ db.send(:blank_object?, 1).should == false
1549
+ db.send(:blank_object?, 1.0).should == false
1550
+ db.send(:blank_object?, true).should == false
1551
+ db.send(:blank_object?, [1]).should == false
1552
+ db.send(:blank_object?, {1.0=>2.0}).should == false
1553
+ db.send(:blank_object?, c[:empty?, false]).should == false
1554
+ db.send(:blank_object?, c[:blank?, false]).should == false
1555
+ end
1556
+ end
1557
+
1558
+ context "Database#schema_autoincrementing_primary_key?" do
1559
+ specify "should whether the parsed schema row indicates a primary key" do
1560
+ m = Sequel::Database.new.method(:schema_autoincrementing_primary_key?)
1561
+ m.call(:primary_key=>true).should == true
1562
+ m.call(:primary_key=>false).should == false
1563
+ end
1564
+ end
1565
+
1566
+ context "Database#supports_savepoints?" do
1567
+ specify "should be false by default" do
1568
+ Sequel::Database.new.supports_savepoints?.should == false
1569
+ end
1570
+ end
1571
+
1572
+ context "Database#input_identifier_meth" do
1573
+ specify "should be the input_identifer method of a default dataset for this database" do
1574
+ db = Sequel::Database.new
1575
+ db.send(:input_identifier_meth).call(:a).should == 'a'
1576
+ db.identifier_input_method = :upcase
1577
+ db.send(:input_identifier_meth).call(:a).should == 'A'
1578
+ end
1579
+ end
1580
+
1581
+ context "Database#output_identifier_meth" do
1582
+ specify "should be the output_identifer method of a default dataset for this database" do
1583
+ db = Sequel::Database.new
1584
+ db.send(:output_identifier_meth).call('A').should == :A
1585
+ db.identifier_output_method = :downcase
1586
+ db.send(:output_identifier_meth).call('A').should == :a
1587
+ end
1588
+ end
1589
+
1590
+ context "Database#metadata_dataset" do
1591
+ specify "should be a dataset with the default settings for identifier_input_method and identifier_output_method" do
1592
+ ds = Sequel::Database.new.send(:metadata_dataset)
1593
+ ds.literal(:a).should == 'A'
1594
+ ds.send(:output_identifier, 'A').should == :a
1595
+ end
1596
+ end
1597
+
1598
+ context "Database#column_schema_to_ruby_default" do
1599
+ specify "should handle converting many default formats" do
1600
+ db = Sequel::Database.new
1601
+ m = db.method(:column_schema_to_ruby_default)
1602
+ p = lambda{|d,t| m.call(d,t)}
1603
+ p[nil, :integer].should == nil
1604
+ p['1', :integer].should == 1
1605
+ p['-1', :integer].should == -1
1606
+ p['1.0', :float].should == 1.0
1607
+ p['-1.0', :float].should == -1.0
1608
+ p['1.0', :decimal].should == BigDecimal.new('1.0')
1609
+ p['-1.0', :decimal].should == BigDecimal.new('-1.0')
1610
+ p['1', :boolean].should == true
1611
+ p['0', :boolean].should == false
1612
+ p['true', :boolean].should == true
1613
+ p['false', :boolean].should == false
1614
+ p["'t'", :boolean].should == true
1615
+ p["'f'", :boolean].should == false
1616
+ p["'a'", :string].should == 'a'
1617
+ p["'a'", :blob].should == 'a'.to_sequel_blob
1618
+ p["'a'", :blob].should be_a_kind_of(Sequel::SQL::Blob)
1619
+ p["''", :string].should == ''
1620
+ p["'\\a''b'", :string].should == "\\a'b"
1621
+ p["'NULL'", :string].should == "NULL"
1622
+ p["'2009-10-29'", :date].should == Date.new(2009,10,29)
1623
+ p["CURRENT_TIMESTAMP", :date].should == nil
1624
+ p["today()", :date].should == nil
1625
+ p["'2009-10-29T10:20:30-07:00'", :datetime].should == DateTime.parse('2009-10-29T10:20:30-07:00')
1626
+ p["'2009-10-29 10:20:30'", :datetime].should == DateTime.parse('2009-10-29 10:20:30')
1627
+ p["'10:20:30'", :time].should == Time.parse('10:20:30')
1628
+ p["NaN", :float].should == nil
1629
+
1630
+ db.meta_def(:database_type){:postgres}
1631
+ p["''::text", :string].should == ""
1632
+ p["'\\a''b'::character varying", :string].should == "\\a'b"
1633
+ p["'a'::bpchar", :string].should == "a"
1634
+ p["(-1)", :integer].should == -1
1635
+ p["(-1.0)", :float].should == -1.0
1636
+ p['(-1.0)', :decimal].should == BigDecimal.new('-1.0')
1637
+ p["'a'::bytea", :blob].should == 'a'.to_sequel_blob
1638
+ p["'a'::bytea", :blob].should be_a_kind_of(Sequel::SQL::Blob)
1639
+ p["'2009-10-29'::date", :date].should == Date.new(2009,10,29)
1640
+ p["'2009-10-29 10:20:30.241343'::timestamp without time zone", :datetime].should == DateTime.parse('2009-10-29 10:20:30.241343')
1641
+ p["'10:20:30'::time without time zone", :time].should == Time.parse('10:20:30')
1642
+
1643
+ db.meta_def(:database_type){:mysql}
1644
+ p["\\a'b", :string].should == "\\a'b"
1645
+ p["a", :string].should == "a"
1646
+ p["NULL", :string].should == "NULL"
1647
+ p["-1", :float].should == -1.0
1648
+ p['-1', :decimal].should == BigDecimal.new('-1.0')
1649
+ p["2009-10-29", :date].should == Date.new(2009,10,29)
1650
+ p["2009-10-29 10:20:30", :datetime].should == DateTime.parse('2009-10-29 10:20:30')
1651
+ p["10:20:30", :time].should == Time.parse('10:20:30')
1652
+ p["CURRENT_DATE", :date].should == nil
1653
+ p["CURRENT_TIMESTAMP", :datetime].should == nil
1654
+ p["a", :enum].should == "a"
1655
+
1656
+ db.meta_def(:database_type){:mssql}
1657
+ p["(N'a')", :string].should == "a"
1658
+ p["((-12))", :integer].should == -12
1659
+ p["((12.1))", :float].should == 12.1
1660
+ p["((-12.1))", :decimal].should == BigDecimal.new('-12.1')
1661
+ end
1662
+ end