sequel 3.21.0 → 3.36.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 (311) hide show
  1. data/CHANGELOG +855 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +23 -7
  4. data/Rakefile +42 -24
  5. data/bin/sequel +26 -8
  6. data/doc/active_record.rdoc +1 -0
  7. data/doc/advanced_associations.rdoc +171 -11
  8. data/doc/association_basics.rdoc +293 -43
  9. data/doc/dataset_basics.rdoc +5 -5
  10. data/doc/dataset_filtering.rdoc +8 -0
  11. data/doc/mass_assignment.rdoc +56 -0
  12. data/doc/migration.rdoc +85 -552
  13. data/doc/model_hooks.rdoc +78 -27
  14. data/doc/object_model.rdoc +541 -0
  15. data/doc/opening_databases.rdoc +97 -50
  16. data/doc/prepared_statements.rdoc +40 -14
  17. data/doc/querying.rdoc +28 -4
  18. data/doc/reflection.rdoc +30 -4
  19. data/doc/release_notes/3.22.0.txt +39 -0
  20. data/doc/release_notes/3.23.0.txt +172 -0
  21. data/doc/release_notes/3.24.0.txt +420 -0
  22. data/doc/release_notes/3.25.0.txt +88 -0
  23. data/doc/release_notes/3.26.0.txt +88 -0
  24. data/doc/release_notes/3.27.0.txt +82 -0
  25. data/doc/release_notes/3.28.0.txt +304 -0
  26. data/doc/release_notes/3.29.0.txt +459 -0
  27. data/doc/release_notes/3.30.0.txt +135 -0
  28. data/doc/release_notes/3.31.0.txt +146 -0
  29. data/doc/release_notes/3.32.0.txt +202 -0
  30. data/doc/release_notes/3.33.0.txt +157 -0
  31. data/doc/release_notes/3.34.0.txt +671 -0
  32. data/doc/release_notes/3.35.0.txt +144 -0
  33. data/doc/release_notes/3.36.0.txt +245 -0
  34. data/doc/schema_modification.rdoc +572 -0
  35. data/doc/sharding.rdoc +95 -3
  36. data/doc/testing.rdoc +129 -0
  37. data/doc/thread_safety.rdoc +17 -0
  38. data/doc/transactions.rdoc +137 -0
  39. data/doc/virtual_rows.rdoc +51 -2
  40. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  41. data/lib/sequel/adapters/ado.rb +40 -18
  42. data/lib/sequel/adapters/amalgalite.rb +17 -9
  43. data/lib/sequel/adapters/db2.rb +175 -86
  44. data/lib/sequel/adapters/dbi.rb +15 -15
  45. data/lib/sequel/adapters/do/mysql.rb +17 -11
  46. data/lib/sequel/adapters/do/postgres.rb +1 -57
  47. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  48. data/lib/sequel/adapters/do.rb +18 -36
  49. data/lib/sequel/adapters/firebird.rb +27 -208
  50. data/lib/sequel/adapters/ibmdb.rb +453 -0
  51. data/lib/sequel/adapters/informix.rb +6 -23
  52. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  53. data/lib/sequel/adapters/jdbc/db2.rb +47 -0
  54. data/lib/sequel/adapters/jdbc/derby.rb +287 -0
  55. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  56. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  57. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  58. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  59. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  61. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  62. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
  64. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  65. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  66. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  67. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  68. data/lib/sequel/adapters/jdbc.rb +265 -94
  69. data/lib/sequel/adapters/mock.rb +361 -0
  70. data/lib/sequel/adapters/mysql.rb +118 -222
  71. data/lib/sequel/adapters/mysql2.rb +58 -57
  72. data/lib/sequel/adapters/odbc/mssql.rb +15 -7
  73. data/lib/sequel/adapters/odbc.rb +28 -9
  74. data/lib/sequel/adapters/openbase.rb +3 -5
  75. data/lib/sequel/adapters/oracle.rb +349 -53
  76. data/lib/sequel/adapters/postgres.rb +356 -107
  77. data/lib/sequel/adapters/shared/access.rb +32 -9
  78. data/lib/sequel/adapters/shared/db2.rb +342 -0
  79. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  80. data/lib/sequel/adapters/shared/informix.rb +53 -0
  81. data/lib/sequel/adapters/shared/mssql.rb +335 -118
  82. data/lib/sequel/adapters/shared/mysql.rb +340 -76
  83. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  84. data/lib/sequel/adapters/shared/oracle.rb +243 -45
  85. data/lib/sequel/adapters/shared/postgres.rb +524 -279
  86. data/lib/sequel/adapters/shared/progress.rb +5 -8
  87. data/lib/sequel/adapters/shared/sqlite.rb +265 -89
  88. data/lib/sequel/adapters/sqlite.rb +101 -39
  89. data/lib/sequel/adapters/swift/mysql.rb +9 -12
  90. data/lib/sequel/adapters/swift/postgres.rb +9 -74
  91. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  92. data/lib/sequel/adapters/swift.rb +8 -7
  93. data/lib/sequel/adapters/tinytds.rb +161 -21
  94. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  95. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  96. data/lib/sequel/ast_transformer.rb +194 -0
  97. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  98. data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
  99. data/lib/sequel/connection_pool/single.rb +5 -0
  100. data/lib/sequel/connection_pool/threaded.rb +38 -14
  101. data/lib/sequel/connection_pool.rb +10 -2
  102. data/lib/sequel/core.rb +118 -11
  103. data/lib/sequel/database/connecting.rb +38 -28
  104. data/lib/sequel/database/dataset.rb +4 -4
  105. data/lib/sequel/database/dataset_defaults.rb +63 -2
  106. data/lib/sequel/database/logging.rb +7 -2
  107. data/lib/sequel/database/misc.rb +150 -46
  108. data/lib/sequel/database/query.rb +215 -65
  109. data/lib/sequel/database/schema_generator.rb +64 -20
  110. data/lib/sequel/database/schema_methods.rb +212 -54
  111. data/lib/sequel/dataset/actions.rb +322 -85
  112. data/lib/sequel/dataset/features.rb +96 -8
  113. data/lib/sequel/dataset/graph.rb +38 -11
  114. data/lib/sequel/dataset/misc.rb +50 -23
  115. data/lib/sequel/dataset/mutation.rb +12 -7
  116. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  117. data/lib/sequel/dataset/query.rb +237 -44
  118. data/lib/sequel/dataset/sql.rb +698 -278
  119. data/lib/sequel/dataset.rb +8 -0
  120. data/lib/sequel/exceptions.rb +4 -0
  121. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  122. data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
  123. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  124. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
  125. data/lib/sequel/extensions/migration.rb +97 -11
  126. data/lib/sequel/extensions/named_timezones.rb +5 -0
  127. data/lib/sequel/extensions/null_dataset.rb +90 -0
  128. data/lib/sequel/extensions/pg_array.rb +460 -0
  129. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  130. data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
  131. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  132. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  133. data/lib/sequel/extensions/pg_inet.rb +89 -0
  134. data/lib/sequel/extensions/pg_json.rb +178 -0
  135. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  136. data/lib/sequel/extensions/pretty_table.rb +5 -71
  137. data/lib/sequel/extensions/query.rb +1 -1
  138. data/lib/sequel/extensions/query_literals.rb +79 -0
  139. data/lib/sequel/extensions/schema_caching.rb +76 -0
  140. data/lib/sequel/extensions/schema_dumper.rb +251 -37
  141. data/lib/sequel/extensions/select_remove.rb +35 -0
  142. data/lib/sequel/extensions/server_block.rb +139 -0
  143. data/lib/sequel/extensions/sql_expr.rb +4 -110
  144. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  145. data/lib/sequel/extensions/to_dot.rb +95 -83
  146. data/lib/sequel/model/associations.rb +1187 -393
  147. data/lib/sequel/model/base.rb +578 -170
  148. data/lib/sequel/model/errors.rb +1 -1
  149. data/lib/sequel/model/exceptions.rb +5 -1
  150. data/lib/sequel/model/inflections.rb +1 -1
  151. data/lib/sequel/model.rb +30 -11
  152. data/lib/sequel/no_core_ext.rb +2 -0
  153. data/lib/sequel/plugins/active_model.rb +13 -1
  154. data/lib/sequel/plugins/association_pks.rb +22 -4
  155. data/lib/sequel/plugins/caching.rb +25 -18
  156. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  157. data/lib/sequel/plugins/composition.rb +44 -12
  158. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  159. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  160. data/lib/sequel/plugins/dirty.rb +214 -0
  161. data/lib/sequel/plugins/eager_each.rb +59 -0
  162. data/lib/sequel/plugins/force_encoding.rb +6 -6
  163. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  164. data/lib/sequel/plugins/identity_map.rb +123 -8
  165. data/lib/sequel/plugins/instance_filters.rb +10 -0
  166. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  167. data/lib/sequel/plugins/json_serializer.rb +58 -6
  168. data/lib/sequel/plugins/list.rb +12 -2
  169. data/lib/sequel/plugins/many_through_many.rb +79 -28
  170. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  171. data/lib/sequel/plugins/nested_attributes.rb +139 -63
  172. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  173. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  174. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  175. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  176. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  177. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  178. data/lib/sequel/plugins/schema.rb +7 -2
  179. data/lib/sequel/plugins/serialization.rb +98 -49
  180. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  181. data/lib/sequel/plugins/sharding.rb +21 -54
  182. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  183. data/lib/sequel/plugins/static_cache.rb +99 -0
  184. data/lib/sequel/plugins/subclasses.rb +10 -2
  185. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  186. data/lib/sequel/plugins/timestamps.rb +1 -1
  187. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  188. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  189. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  190. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  191. data/lib/sequel/sql.rb +526 -59
  192. data/lib/sequel/timezones.rb +57 -38
  193. data/lib/sequel/version.rb +1 -1
  194. data/spec/adapters/db2_spec.rb +146 -0
  195. data/spec/adapters/firebird_spec.rb +1 -1
  196. data/spec/adapters/mssql_spec.rb +142 -40
  197. data/spec/adapters/mysql_spec.rb +380 -234
  198. data/spec/adapters/oracle_spec.rb +67 -77
  199. data/spec/adapters/postgres_spec.rb +1050 -212
  200. data/spec/adapters/spec_helper.rb +8 -5
  201. data/spec/adapters/sqlite_spec.rb +139 -27
  202. data/spec/core/connection_pool_spec.rb +196 -93
  203. data/spec/core/database_spec.rb +879 -510
  204. data/spec/core/dataset_spec.rb +1415 -1086
  205. data/spec/core/expression_filters_spec.rb +554 -109
  206. data/spec/core/mock_adapter_spec.rb +449 -0
  207. data/spec/core/object_graph_spec.rb +86 -114
  208. data/spec/core/schema_generator_spec.rb +4 -4
  209. data/spec/core/schema_spec.rb +482 -38
  210. data/spec/core/spec_helper.rb +6 -48
  211. data/spec/extensions/active_model_spec.rb +13 -0
  212. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  213. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  214. data/spec/extensions/association_dependencies_spec.rb +15 -25
  215. data/spec/extensions/association_pks_spec.rb +66 -32
  216. data/spec/extensions/association_proxies_spec.rb +4 -4
  217. data/spec/extensions/caching_spec.rb +47 -51
  218. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  219. data/spec/extensions/columns_introspection_spec.rb +91 -0
  220. data/spec/extensions/composition_spec.rb +18 -13
  221. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
  222. data/spec/extensions/dataset_associations_spec.rb +199 -0
  223. data/spec/extensions/defaults_setter_spec.rb +64 -0
  224. data/spec/extensions/dirty_spec.rb +155 -0
  225. data/spec/extensions/eager_each_spec.rb +34 -0
  226. data/spec/extensions/force_encoding_spec.rb +4 -2
  227. data/spec/extensions/hook_class_methods_spec.rb +69 -92
  228. data/spec/extensions/identity_map_spec.rb +131 -22
  229. data/spec/extensions/inflector_spec.rb +0 -4
  230. data/spec/extensions/instance_filters_spec.rb +11 -21
  231. data/spec/extensions/instance_hooks_spec.rb +72 -0
  232. data/spec/extensions/json_serializer_spec.rb +49 -6
  233. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  234. data/spec/extensions/list_spec.rb +48 -39
  235. data/spec/extensions/many_through_many_spec.rb +283 -250
  236. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  237. data/spec/extensions/migration_spec.rb +140 -35
  238. data/spec/extensions/named_timezones_spec.rb +29 -11
  239. data/spec/extensions/nested_attributes_spec.rb +251 -89
  240. data/spec/extensions/null_dataset_spec.rb +85 -0
  241. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  242. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  243. data/spec/extensions/pg_array_spec.rb +196 -0
  244. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  245. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  246. data/spec/extensions/pg_hstore_spec.rb +195 -0
  247. data/spec/extensions/pg_inet_spec.rb +44 -0
  248. data/spec/extensions/pg_json_spec.rb +101 -0
  249. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  250. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  251. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  252. data/spec/extensions/prepared_statements_spec.rb +87 -0
  253. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  254. data/spec/extensions/pretty_table_spec.rb +7 -6
  255. data/spec/extensions/query_literals_spec.rb +168 -0
  256. data/spec/extensions/query_spec.rb +6 -0
  257. data/spec/extensions/rcte_tree_spec.rb +50 -43
  258. data/spec/extensions/schema_caching_spec.rb +41 -0
  259. data/spec/extensions/schema_dumper_spec.rb +429 -22
  260. data/spec/extensions/schema_spec.rb +25 -21
  261. data/spec/extensions/select_remove_spec.rb +38 -0
  262. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  263. data/spec/extensions/serialization_spec.rb +85 -43
  264. data/spec/extensions/server_block_spec.rb +90 -0
  265. data/spec/extensions/sharding_spec.rb +69 -143
  266. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  267. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  268. data/spec/extensions/spec_helper.rb +22 -61
  269. data/spec/extensions/sql_expr_spec.rb +29 -60
  270. data/spec/extensions/static_cache_spec.rb +145 -0
  271. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  272. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  273. data/spec/extensions/timestamps_spec.rb +6 -6
  274. data/spec/extensions/to_dot_spec.rb +4 -7
  275. data/spec/extensions/touch_spec.rb +13 -14
  276. data/spec/extensions/tree_spec.rb +11 -26
  277. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  278. data/spec/extensions/update_primary_key_spec.rb +30 -24
  279. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  280. data/spec/extensions/validation_helpers_spec.rb +16 -35
  281. data/spec/extensions/xml_serializer_spec.rb +29 -4
  282. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  283. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  284. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  285. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  286. data/spec/integration/associations_test.rb +659 -21
  287. data/spec/integration/database_test.rb +9 -3
  288. data/spec/integration/dataset_test.rb +605 -91
  289. data/spec/integration/eager_loader_test.rb +19 -21
  290. data/spec/integration/migrator_test.rb +2 -3
  291. data/spec/integration/model_test.rb +81 -2
  292. data/spec/integration/plugin_test.rb +454 -143
  293. data/spec/integration/prepared_statement_test.rb +263 -128
  294. data/spec/integration/schema_test.rb +309 -43
  295. data/spec/integration/spec_helper.rb +40 -30
  296. data/spec/integration/timezone_test.rb +38 -12
  297. data/spec/integration/transaction_test.rb +183 -8
  298. data/spec/integration/type_test.rb +18 -4
  299. data/spec/model/association_reflection_spec.rb +201 -7
  300. data/spec/model/associations_spec.rb +1067 -661
  301. data/spec/model/base_spec.rb +329 -95
  302. data/spec/model/dataset_methods_spec.rb +7 -27
  303. data/spec/model/eager_loading_spec.rb +677 -676
  304. data/spec/model/hooks_spec.rb +349 -67
  305. data/spec/model/model_spec.rb +240 -168
  306. data/spec/model/plugins_spec.rb +24 -13
  307. data/spec/model/record_spec.rb +490 -232
  308. data/spec/model/spec_helper.rb +14 -71
  309. data/spec/model/validations_spec.rb +11 -0
  310. data/spec/rcov.opts +2 -0
  311. metadata +178 -80
@@ -6,7 +6,6 @@ String.send(:include, Sequel::SQL::StringMethods)
6
6
  describe "Blockless Ruby Filters" do
7
7
  before do
8
8
  db = Sequel::Database.new
9
- db.quote_identifiers = false
10
9
  @d = db[:items]
11
10
  def @d.l(*args, &block)
12
11
  literal(filter_expr(*args, &block))
@@ -186,31 +185,26 @@ describe "Blockless Ruby Filters" do
186
185
  @d.l(~((((:x - :y)/(:x + :y))*:z) <= 100)).should == '((((x - y) / (x + y)) * z) > 100)'
187
186
  end
188
187
 
189
- it "should not allow negation of string expressions" do
190
- proc{~:x.sql_string}.should raise_error
191
- proc{~([:x, :y].sql_string_join)}.should raise_error
188
+ it "should not add ~ method to string expressions" do
189
+ proc{~:x.sql_string}.should raise_error(NoMethodError)
192
190
  end
193
191
 
194
- it "should not allow mathematical or string operations on true, false, or nil" do
195
- proc{:x + 1}.should_not raise_error
196
- proc{:x - true}.should raise_error(Sequel::Error)
197
- proc{:x / false}.should raise_error(Sequel::Error)
198
- proc{:x * nil}.should raise_error(Sequel::Error)
199
- proc{[:x, nil].sql_string_join}.should raise_error(Sequel::Error)
192
+ it "should allow mathematical or string operations on true, false, or nil" do
193
+ @d.lit(:x + 1).should == '(x + 1)'
194
+ @d.lit(:x - true).should == "(x - 't')"
195
+ @d.lit(:x / false).should == "(x / 'f')"
196
+ @d.lit(:x * nil).should == '(x * NULL)'
197
+ @d.lit([:x, nil].sql_string_join).should == '(x || NULL)'
200
198
  end
201
199
 
202
- it "should not allow mathematical or string operations on boolean complex expressions" do
203
- proc{:x + (:y + 1)}.should_not raise_error
204
- proc{:x - (~:y)}.should raise_error(Sequel::Error)
205
- proc{:x / (:y & :z)}.should raise_error(Sequel::Error)
206
- proc{:x * (:y | :z)}.should raise_error(Sequel::Error)
207
- proc{:x + :y.like('a')}.should raise_error(Sequel::Error)
208
- proc{:x - :y.like(/a/)}.should raise_error(Sequel::Error)
209
- proc{:x * :y.like(/a/i)}.should raise_error(Sequel::Error)
210
- proc{:x + ~:y.like('a')}.should raise_error(Sequel::Error)
211
- proc{:x - ~:y.like(/a/)}.should raise_error(Sequel::Error)
212
- proc{:x * ~:y.like(/a/i)}.should raise_error(Sequel::Error)
213
- proc{[:x, ~:y.like(/a/i)].sql_string_join}.should raise_error(Sequel::Error)
200
+ it "should allow mathematical or string operations on boolean complex expressions" do
201
+ @d.lit(:x + (:y + 1)).should == '(x + y + 1)'
202
+ @d.lit(:x - ~:y).should == '(x - NOT y)'
203
+ @d.lit(:x / (:y & :z)).should == '(x / (y AND z))'
204
+ @d.lit(:x * (:y | :z)).should == '(x * (y OR z))'
205
+ @d.lit(:x + :y.like('a')).should == "(x + (y LIKE 'a'))"
206
+ @d.lit(:x - ~:y.like('a')).should == "(x - (y NOT LIKE 'a'))"
207
+ @d.lit([:x, ~:y.like('a')].sql_string_join).should == "(x || (y NOT LIKE 'a'))"
214
208
  end
215
209
 
216
210
  it "should support AND conditions via &" do
@@ -218,8 +212,6 @@ describe "Blockless Ruby Filters" do
218
212
  @d.l(:x.sql_boolean & :y).should == '(x AND y)'
219
213
  @d.l(:x & :y & :z).should == '(x AND y AND z)'
220
214
  @d.l(:x & {:y => :z}).should == '(x AND (y = z))'
221
- @d.l({:y => :z} & :x).should == '((y = z) AND x)'
222
- @d.l({:x => :a} & {:y => :z}).should == '((x = a) AND (y = z))'
223
215
  @d.l((:x + 200 < 0) & (:y - 200 < 0)).should == '(((x + 200) < 0) AND ((y - 200) < 0))'
224
216
  @d.l(:x & ~:y).should == '(x AND NOT y)'
225
217
  @d.l(~:x & :y).should == '(NOT x AND y)'
@@ -231,8 +223,6 @@ describe "Blockless Ruby Filters" do
231
223
  @d.l(:x.sql_boolean | :y).should == '(x OR y)'
232
224
  @d.l(:x | :y | :z).should == '(x OR y OR z)'
233
225
  @d.l(:x | {:y => :z}).should == '(x OR (y = z))'
234
- @d.l({:y => :z} | :x).should == '((y = z) OR x)'
235
- @d.l({:x => :a} | {:y => :z}).should == '((x = a) OR (y = z))'
236
226
  @d.l((:x.sql_number > 200) | (:y.sql_number < 200)).should == '((x > 200) OR (y < 200))'
237
227
  end
238
228
 
@@ -273,60 +263,12 @@ describe "Blockless Ruby Filters" do
273
263
  @d.l(:x => nil, :y => [1,2,3])[1...-1].split(' AND ').sort.should == ['(x IS NULL)', '(y IN (1, 2, 3))']
274
264
  end
275
265
 
276
- it "should support sql_expr on hashes" do
277
- @d.l({:x => 100, :y => 'a'}.sql_expr)[1...-1].split(' AND ').sort.should == ['(x = 100)', '(y = \'a\')']
278
- @d.l({:x => true, :y => false}.sql_expr)[1...-1].split(' AND ').sort.should == ['(x IS TRUE)', '(y IS FALSE)']
279
- @d.l({:x => nil, :y => [1,2,3]}.sql_expr)[1...-1].split(' AND ').sort.should == ['(x IS NULL)', '(y IN (1, 2, 3))']
280
- end
281
-
282
- it "should support sql_negate on hashes" do
283
- @d.l({:x => 100, :y => 'a'}.sql_negate)[1...-1].split(' AND ').sort.should == ['(x != 100)', '(y != \'a\')']
284
- @d.l({:x => true, :y => false}.sql_negate)[1...-1].split(' AND ').sort.should == ['(x IS NOT TRUE)', '(y IS NOT FALSE)']
285
- @d.l({:x => nil, :y => [1,2,3]}.sql_negate)[1...-1].split(' AND ').sort.should == ['(x IS NOT NULL)', '(y NOT IN (1, 2, 3))']
286
- end
287
-
288
- it "should support ~ on hashes" do
289
- @d.l(~{:x => 100, :y => 'a'})[1...-1].split(' OR ').sort.should == ['(x != 100)', '(y != \'a\')']
290
- @d.l(~{:x => true, :y => false})[1...-1].split(' OR ').sort.should == ['(x IS NOT TRUE)', '(y IS NOT FALSE)']
291
- @d.l(~{:x => nil, :y => [1,2,3]})[1...-1].split(' OR ').sort.should == ['(x IS NOT NULL)', '(y NOT IN (1, 2, 3))']
292
- end
293
-
294
- it "should support sql_or on hashes" do
295
- @d.l({:x => 100, :y => 'a'}.sql_or)[1...-1].split(' OR ').sort.should == ['(x = 100)', '(y = \'a\')']
296
- @d.l({:x => true, :y => false}.sql_or)[1...-1].split(' OR ').sort.should == ['(x IS TRUE)', '(y IS FALSE)']
297
- @d.l({:x => nil, :y => [1,2,3]}.sql_or)[1...-1].split(' OR ').sort.should == ['(x IS NULL)', '(y IN (1, 2, 3))']
298
- end
299
-
300
266
  it "should support arrays with all two pairs the same as hashes" do
301
267
  @d.l([[:x, 100],[:y, 'a']]).should == '((x = 100) AND (y = \'a\'))'
302
268
  @d.l([[:x, true], [:y, false]]).should == '((x IS TRUE) AND (y IS FALSE))'
303
269
  @d.l([[:x, nil], [:y, [1,2,3]]]).should == '((x IS NULL) AND (y IN (1, 2, 3)))'
304
270
  end
305
271
 
306
- it "should support sql_expr on arrays with all two pairs" do
307
- @d.l([[:x, 100],[:y, 'a']].sql_expr).should == '((x = 100) AND (y = \'a\'))'
308
- @d.l([[:x, true], [:y, false]].sql_expr).should == '((x IS TRUE) AND (y IS FALSE))'
309
- @d.l([[:x, nil], [:y, [1,2,3]]].sql_expr).should == '((x IS NULL) AND (y IN (1, 2, 3)))'
310
- end
311
-
312
- it "should support sql_negate on arrays with all two pairs" do
313
- @d.l([[:x, 100],[:y, 'a']].sql_negate).should == '((x != 100) AND (y != \'a\'))'
314
- @d.l([[:x, true], [:y, false]].sql_negate).should == '((x IS NOT TRUE) AND (y IS NOT FALSE))'
315
- @d.l([[:x, nil], [:y, [1,2,3]]].sql_negate).should == '((x IS NOT NULL) AND (y NOT IN (1, 2, 3)))'
316
- end
317
-
318
- it "should support ~ on arrays with all two pairs" do
319
- @d.l(~[[:x, 100],[:y, 'a']]).should == '((x != 100) OR (y != \'a\'))'
320
- @d.l(~[[:x, true], [:y, false]]).should == '((x IS NOT TRUE) OR (y IS NOT FALSE))'
321
- @d.l(~[[:x, nil], [:y, [1,2,3]]]).should == '((x IS NOT NULL) OR (y NOT IN (1, 2, 3)))'
322
- end
323
-
324
- it "should support sql_or on arrays with all two pairs" do
325
- @d.l([[:x, 100],[:y, 'a']].sql_or).should == '((x = 100) OR (y = \'a\'))'
326
- @d.l([[:x, true], [:y, false]].sql_or).should == '((x IS TRUE) OR (y IS FALSE))'
327
- @d.l([[:x, nil], [:y, [1,2,3]]].sql_or).should == '((x IS NULL) OR (y IN (1, 2, 3)))'
328
- end
329
-
330
272
  it "should emulate columns for array values" do
331
273
  @d.l([:x, :y]=>[[1,2], [3,4]].sql_array).should == '((x, y) IN ((1, 2), (3, 4)))'
332
274
  @d.l([:x, :y, :z]=>[[1,2,5], [3,4,6]]).should == '((x, y, z) IN ((1, 2, 5), (3, 4, 6)))'
@@ -338,21 +280,6 @@ describe "Blockless Ruby Filters" do
338
280
  @d.l([:x, :y, :z]=>[[1,2,5], [3,4,6]]).should == '(((x = 1) AND (y = 2) AND (z = 5)) OR ((x = 3) AND (y = 4) AND (z = 6)))'
339
281
  end
340
282
 
341
- it "should support Array#sql_string_join for concatenation of SQL strings" do
342
- @d.lit([:x].sql_string_join).should == '(x)'
343
- @d.lit([:x].sql_string_join(', ')).should == '(x)'
344
- @d.lit([:x, :y].sql_string_join).should == '(x || y)'
345
- @d.lit([:x, :y].sql_string_join(', ')).should == "(x || ', ' || y)"
346
- @d.lit([:x.sql_function(1), :y.sql_subscript(1)].sql_string_join).should == '(x(1) || y[1])'
347
- @d.lit([:x.sql_function(1), 'y.z'.lit].sql_string_join(', ')).should == "(x(1) || ', ' || y.z)"
348
- @d.lit([:x, 1, :y].sql_string_join).should == "(x || '1' || y)"
349
- @d.lit([:x, 1, :y].sql_string_join(', ')).should == "(x || ', ' || '1' || ', ' || y)"
350
- @d.lit([:x, 1, :y].sql_string_join(:y__z)).should == "(x || y.z || '1' || y.z || y)"
351
- @d.lit([:x, 1, :y].sql_string_join(1)).should == "(x || '1' || '1' || '1' || y)"
352
- @d.lit([:x, :y].sql_string_join('y.x || x.y'.lit)).should == "(x || y.x || x.y || y)"
353
- @d.lit([[:x, :y].sql_string_join, [:a, :b].sql_string_join].sql_string_join).should == "(x || y || a || b)"
354
- end
355
-
356
283
  it "should support StringExpression#+ for concatenation of SQL strings" do
357
284
  @d.lit(:x.sql_string + :y).should == '(x || y)'
358
285
  @d.lit([:x].sql_string_join + :y).should == '(x || y)'
@@ -382,12 +309,12 @@ describe "Blockless Ruby Filters" do
382
309
  @d.l((:x + 1) & (:x + 2) > 100).should == '(((x + 1) & (x + 2)) > 100)'
383
310
  end
384
311
 
385
- it "should raise an error if use a Bitwise method on a ComplexExpression that isn't a NumericExpression" do
386
- proc{(:x + 1) & (:x & 2)}.should raise_error(Sequel::Error)
312
+ it "should allow using a Bitwise method on a ComplexExpression that isn't a NumericExpression" do
313
+ @d.lit((:x + 1) & (:x + '2')).should == "((x + 1) & (x || '2'))"
387
314
  end
388
315
 
389
- it "should raise an error if use a Boolean method on a ComplexExpression that isn't a BooleanExpression" do
390
- proc{:x & (:x + 2)}.should raise_error(Sequel::Error)
316
+ it "should allow using a Boolean method on a ComplexExpression that isn't a BooleanExpression" do
317
+ @d.l(:x & (:x + '2')).should == "(x AND (x || '2'))"
391
318
  end
392
319
 
393
320
  it "should raise an error if attempting to invert a ComplexExpression that isn't a BooleanExpression" do
@@ -424,10 +351,94 @@ describe "Blockless Ruby Filters" do
424
351
  @d.l(~{:x => Sequel::SQLFALSE}).should == '(x IS NOT FALSE)'
425
352
  end
426
353
 
354
+ it "should support direct negation of SQL::Constants" do
355
+ @d.l({:x => ~Sequel::NULL}).should == '(x IS NOT NULL)'
356
+ @d.l({:x => ~Sequel::NOTNULL}).should == '(x IS NULL)'
357
+ @d.l({:x => ~Sequel::TRUE}).should == '(x IS FALSE)'
358
+ @d.l({:x => ~Sequel::FALSE}).should == '(x IS TRUE)'
359
+ @d.l({:x => ~Sequel::SQLTRUE}).should == '(x IS FALSE)'
360
+ @d.l({:x => ~Sequel::SQLFALSE}).should == '(x IS TRUE)'
361
+ end
362
+
363
+ it "should raise an error if trying to invert an invalid SQL::Constant" do
364
+ proc{~Sequel::CURRENT_DATE}.should raise_error(Sequel::Error)
365
+ end
366
+
427
367
  it "should raise an error if trying to create an invalid complex expression" do
428
368
  proc{Sequel::SQL::ComplexExpression.new(:BANG, 1, 2)}.should raise_error(Sequel::Error)
429
369
  end
430
370
 
371
+ it "should use a string concatentation for + if given a string" do
372
+ @d.lit(:x + '1').should == "(x || '1')"
373
+ @d.lit(:x + '1' + '1').should == "(x || '1' || '1')"
374
+ end
375
+
376
+ it "should use an addition for + if given a literal string" do
377
+ @d.lit(:x + '1'.lit).should == "(x + 1)"
378
+ @d.lit(:x + '1'.lit + '1'.lit).should == "(x + 1 + 1)"
379
+ end
380
+
381
+ it "should use a bitwise operator for & and | if given an integer" do
382
+ @d.lit(:x & 1).should == "(x & 1)"
383
+ @d.lit(:x | 1).should == "(x | 1)"
384
+ @d.lit(:x & 1 & 1).should == "(x & 1 & 1)"
385
+ @d.lit(:x | 1 | 1).should == "(x | 1 | 1)"
386
+ end
387
+
388
+ it "should allow adding a string to an integer expression" do
389
+ @d.lit(:x + 1 + 'a').should == "(x + 1 + 'a')"
390
+ end
391
+
392
+ it "should allow adding an integer to an string expression" do
393
+ @d.lit(:x + 'a' + 1).should == "(x || 'a' || 1)"
394
+ end
395
+
396
+ it "should allow adding a boolean to an integer expression" do
397
+ @d.lit(:x + 1 + true).should == "(x + 1 + 't')"
398
+ end
399
+
400
+ it "should allow adding a boolean to an string expression" do
401
+ @d.lit(:x + 'a' + true).should == "(x || 'a' || 't')"
402
+ end
403
+
404
+ it "should allow using a boolean operation with an integer on an boolean expression" do
405
+ @d.lit(:x & :a & 1).should == "(x AND a AND 1)"
406
+ end
407
+
408
+ it "should allow using a boolean operation with a string on an boolean expression" do
409
+ @d.lit(:x & :a & 'a').should == "(x AND a AND 'a')"
410
+ end
411
+
412
+ it "should allowing AND of boolean expression and literal string" do
413
+ @d.lit(:x & :a & 'a'.lit).should == "(x AND a AND a)"
414
+ end
415
+
416
+ it "should allowing + of integer expression and literal string" do
417
+ @d.lit(:x + :a + 'a'.lit).should == "(x + a + a)"
418
+ end
419
+
420
+ it "should allowing + of string expression and literal string" do
421
+ @d.lit(:x + 'a' + 'a'.lit).should == "(x || 'a' || a)"
422
+ end
423
+
424
+ it "should allow sql_{string,boolean,number} methods on numeric expressions" do
425
+ @d.lit((:x + 1).sql_string + 'a').should == "((x + 1) || 'a')"
426
+ @d.lit((:x + 1).sql_boolean & 1).should == "((x + 1) AND 1)"
427
+ @d.lit((:x + 1).sql_number + 'a').should == "(x + 1 + 'a')"
428
+ end
429
+
430
+ it "should allow sql_{string,boolean,number} methods on string expressions" do
431
+ @d.lit((:x + 'a').sql_string + 'a').should == "(x || 'a' || 'a')"
432
+ @d.lit((:x + 'a').sql_boolean & 1).should == "((x || 'a') AND 1)"
433
+ @d.lit((:x + 'a').sql_number + 'a').should == "((x || 'a') + 'a')"
434
+ end
435
+
436
+ it "should allow sql_{string,boolean,number} methods on boolean expressions" do
437
+ @d.lit((:x & :y).sql_string + 'a').should == "((x AND y) || 'a')"
438
+ @d.lit((:x & :y).sql_boolean & 1).should == "(x AND y AND 1)"
439
+ @d.lit((:x & :y).sql_number + 'a').should == "((x AND y) + 'a')"
440
+ end
441
+
431
442
  it "should raise an error if trying to literalize an invalid complex expression" do
432
443
  ce = :x + 1
433
444
  ce.instance_variable_set(:@op, :BANG)
@@ -439,26 +450,65 @@ describe "Blockless Ruby Filters" do
439
450
  e2 = ~:comment.like('%:hidden:%')
440
451
  e1.should == e2
441
452
  end
453
+
454
+ it "should support expression filter methods on Datasets" do
455
+ d = @d.select(:a)
456
+
457
+ @d.lit(d + 1).should == '((SELECT a FROM items) + 1)'
458
+ @d.lit(d - 1).should == '((SELECT a FROM items) - 1)'
459
+ @d.lit(d * 1).should == '((SELECT a FROM items) * 1)'
460
+ @d.lit(d / 1).should == '((SELECT a FROM items) / 1)'
461
+
462
+ @d.lit(d => 1).should == '((SELECT a FROM items) = 1)'
463
+ @d.lit(~{d => 1}).should == '((SELECT a FROM items) != 1)'
464
+ @d.lit(d > 1).should == '((SELECT a FROM items) > 1)'
465
+ @d.lit(d < 1).should == '((SELECT a FROM items) < 1)'
466
+ @d.lit(d >= 1).should == '((SELECT a FROM items) >= 1)'
467
+ @d.lit(d <= 1).should == '((SELECT a FROM items) <= 1)'
468
+
469
+ @d.lit(d.as(:b)).should == '(SELECT a FROM items) AS b'
470
+
471
+ @d.lit(d & :b).should == '((SELECT a FROM items) AND b)'
472
+ @d.lit(d | :b).should == '((SELECT a FROM items) OR b)'
473
+ @d.lit(~d).should == 'NOT (SELECT a FROM items)'
474
+
475
+ @d.lit(d.cast(Integer)).should == 'CAST((SELECT a FROM items) AS integer)'
476
+ @d.lit(d.cast_numeric).should == 'CAST((SELECT a FROM items) AS integer)'
477
+ @d.lit(d.cast_string).should == 'CAST((SELECT a FROM items) AS varchar(255))'
478
+ @d.lit(d.cast_numeric << :b).should == '(CAST((SELECT a FROM items) AS integer) << b)'
479
+ @d.lit(d.cast_string + :b).should == '(CAST((SELECT a FROM items) AS varchar(255)) || b)'
480
+
481
+ @d.lit(d.extract(:year)).should == 'extract(year FROM (SELECT a FROM items))'
482
+ @d.lit(d.sql_boolean & :b).should == '((SELECT a FROM items) AND b)'
483
+ @d.lit(d.sql_number << :b).should == '((SELECT a FROM items) << b)'
484
+ @d.lit(d.sql_string + :b).should == '((SELECT a FROM items) || b)'
485
+
486
+ @d.lit(d.asc).should == '(SELECT a FROM items) ASC'
487
+ @d.lit(d.desc).should == '(SELECT a FROM items) DESC'
488
+
489
+ @d.lit(d.like(:b)).should == '((SELECT a FROM items) LIKE b)'
490
+ @d.lit(d.ilike(:b)).should == '((SELECT a FROM items) ILIKE b)'
491
+ end
442
492
 
443
493
  if RUBY_VERSION < '1.9.0'
444
494
  it "should not allow inequality operations on true, false, or nil" do
445
- proc{:x > 1}.should_not raise_error
446
- proc{:x < true}.should raise_error(Sequel::Error)
447
- proc{:x >= false}.should raise_error(Sequel::Error)
448
- proc{:x <= nil}.should raise_error(Sequel::Error)
495
+ @d.lit(:x > 1).should == "(x > 1)"
496
+ @d.lit(:x < true).should == "(x < 't')"
497
+ @d.lit(:x >= false).should == "(x >= 'f')"
498
+ @d.lit(:x <= nil).should == "(x <= NULL)"
449
499
  end
450
500
 
451
501
  it "should not allow inequality operations on boolean complex expressions" do
452
- proc{:x > (:y > 5)}.should raise_error(Sequel::Error)
453
- proc{:x < (:y < 5)}.should raise_error(Sequel::Error)
454
- proc{:x >= (:y >= 5)}.should raise_error(Sequel::Error)
455
- proc{:x <= (:y <= 5)}.should raise_error(Sequel::Error)
456
- proc{:x > {:y => nil}}.should raise_error(Sequel::Error)
457
- proc{:x < ~{:y => nil}}.should raise_error(Sequel::Error)
458
- proc{:x >= {:y => 5}}.should raise_error(Sequel::Error)
459
- proc{:x <= ~{:y => 5}}.should raise_error(Sequel::Error)
460
- proc{:x >= {:y => [1,2,3]}}.should raise_error(Sequel::Error)
461
- proc{:x <= ~{:y => [1,2,3]}}.should raise_error(Sequel::Error)
502
+ @d.lit(:x > (:y > 5)).should == "(x > (y > 5))"
503
+ @d.lit(:x < (:y < 5)).should == "(x < (y < 5))"
504
+ @d.lit(:x >= (:y >= 5)).should == "(x >= (y >= 5))"
505
+ @d.lit(:x <= (:y <= 5)).should == "(x <= (y <= 5))"
506
+ @d.lit(:x > {:y => nil}).should == "(x > (y IS NULL))"
507
+ @d.lit(:x < ~{:y => nil}).should == "(x < (y IS NOT NULL))"
508
+ @d.lit(:x >= {:y => 5}).should == "(x >= (y = 5))"
509
+ @d.lit(:x <= ~{:y => 5}).should == "(x <= (y != 5))"
510
+ @d.lit(:x >= {:y => [1,2,3]}).should == "(x >= (y IN (1, 2, 3)))"
511
+ @d.lit(:x <= ~{:y => [1,2,3]}).should == "(x <= (y NOT IN (1, 2, 3)))"
462
512
  end
463
513
 
464
514
  it "should support >, <, >=, and <= via Symbol#>,<,>=,<=" do
@@ -553,7 +603,7 @@ describe Sequel::SQL::VirtualRow do
553
603
  end
554
604
 
555
605
  it "should support :frame=>:rows option for window function calls" do
556
- @d.l{rank(:over, :frame=>:rows){}}.should == 'rank() OVER (ROWS UNBOUNDED PRECEDING)'
606
+ @d.l{rank(:over, :frame=>:rows){}}.should == 'rank() OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)'
557
607
  end
558
608
 
559
609
  it "should support :frame=>'some string' option for window function calls" do
@@ -565,7 +615,7 @@ describe Sequel::SQL::VirtualRow do
565
615
  end
566
616
 
567
617
  it "should support all these options together" do
568
- @d.l{count(:over, :* =>true, :partition=>a, :order=>b, :window=>:win, :frame=>:rows){}}.should == 'count(*) OVER ("win" PARTITION BY "a" ORDER BY "b" ROWS UNBOUNDED PRECEDING)'
618
+ @d.l{count(:over, :* =>true, :partition=>a, :order=>b, :window=>:win, :frame=>:rows){}}.should == 'count(*) OVER ("win" PARTITION BY "a" ORDER BY "b" ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)'
569
619
  end
570
620
 
571
621
  it "should raise an error if window functions are not supported" do
@@ -596,4 +646,399 @@ describe Sequel::SQL::VirtualRow do
596
646
  Sequel::BasicObject.remove_methods!
597
647
  @d.l{a > adsoiwemlsdaf2}.should == '("a" > "adsoiwemlsdaf2")'
598
648
  end
649
+
650
+ it "should have operator methods defined that produce Sequel expression objects" do
651
+ @d.l{|o| o.&({:a=>1}, :b)}.should == '(("a" = 1) AND "b")'
652
+ @d.l{|o| o.|({:a=>1}, :b)}.should == '(("a" = 1) OR "b")'
653
+ @d.l{|o| o.+(1, :b) > 2}.should == '((1 + "b") > 2)'
654
+ @d.l{|o| o.-(1, :b) < 2}.should == '((1 - "b") < 2)'
655
+ @d.l{|o| o.*(1, :b) >= 2}.should == '((1 * "b") >= 2)'
656
+ @d.l{|o| o./(1, :b) <= 2}.should == '((1 / "b") <= 2)'
657
+ @d.l{|o| o.~(:a=>1)}.should == '("a" != 1)'
658
+ @d.l{|o| o.~([[:a, 1], [:b, 2]])}.should == '(("a" != 1) OR ("b" != 2))'
659
+ @d.l{|o| o.<(1, :b)}.should == '(1 < "b")'
660
+ @d.l{|o| o.>(1, :b)}.should == '(1 > "b")'
661
+ @d.l{|o| o.<=(1, :b)}.should == '(1 <= "b")'
662
+ @d.l{|o| o.>=(1, :b)}.should == '(1 >= "b")'
663
+ end
664
+
665
+ it "should have have ` produce literal strings" do
666
+ @d.l{a > `some SQL`}.should == '("a" > some SQL)'
667
+ @d.l{|o| o.a > o.`('some SQL')}.should == '("a" > some SQL)' #`
668
+ end
669
+ end
670
+
671
+ describe "Sequel core extension replacements" do
672
+ before do
673
+ @db = Sequel::Database.new
674
+ @o = Object.new
675
+ def @o.sql_literal(ds) 'foo' end
676
+ end
677
+
678
+ def l(arg, should)
679
+ @db.literal(arg).should == should
680
+ end
681
+
682
+ it "Sequel.expr should return an appropriate wrapped object" do
683
+ l(Sequel.expr(1) + 1, "(1 + 1)")
684
+ l(Sequel.expr('a') + 'b', "('a' || 'b')")
685
+ l(Sequel.expr(:b) & nil, "(b AND NULL)")
686
+ l(Sequel.expr(nil) & true, "(NULL AND 't')")
687
+ l(Sequel.expr(false) & true, "('f' AND 't')")
688
+ l(Sequel.expr(true) | false, "('t' OR 'f')")
689
+ l(Sequel.expr(@o) + 1, "(foo + 1)")
690
+ end
691
+
692
+ it "Sequel.expr should handle condition specifiers" do
693
+ l(Sequel.expr(:a=>1) & nil, "((a = 1) AND NULL)")
694
+ l(Sequel.expr([[:a, 1]]) & nil, "((a = 1) AND NULL)")
695
+ l(Sequel.expr([[:a, 1], [:b, 2]]) & nil, "((a = 1) AND (b = 2) AND NULL)")
696
+ end
697
+
698
+ it "Sequel.expr should handle arrays that are not condition specifiers" do
699
+ l(Sequel.expr([1]), "(1)")
700
+ l(Sequel.expr([1, 2]), "(1, 2)")
701
+ end
702
+
703
+ it "Sequel.expr should treat blocks/procs as virtual rows and wrap the output" do
704
+ l(Sequel.expr{1} + 1, "(1 + 1)")
705
+ l(Sequel.expr{o__a} + 1, "(o.a + 1)")
706
+ l(Sequel.expr{[[:a, 1]]} & nil, "((a = 1) AND NULL)")
707
+ l(Sequel.expr{|v| @o} + 1, "(foo + 1)")
708
+
709
+ l(Sequel.expr(proc{1}) + 1, "(1 + 1)")
710
+ l(Sequel.expr(proc{o__a}) + 1, "(o.a + 1)")
711
+ l(Sequel.expr(proc{[[:a, 1]]}) & nil, "((a = 1) AND NULL)")
712
+ l(Sequel.expr(proc{|v| @o}) + 1, "(foo + 1)")
713
+ end
714
+
715
+ it "Sequel.expr should raise an error if given an argument and a block" do
716
+ proc{Sequel.expr(nil){}}.should raise_error(Sequel::Error)
717
+ end
718
+
719
+ it "Sequel.expr should raise an error if given neither an argument nor a block" do
720
+ proc{Sequel.expr}.should raise_error(Sequel::Error)
721
+ end
722
+
723
+ it "Sequel.expr should return existing Sequel expressions directly" do
724
+ o = Sequel.expr(1)
725
+ Sequel.expr(o).should equal(o)
726
+ o = Sequel.lit('1')
727
+ Sequel.expr(o).should equal(o)
728
+ end
729
+
730
+ it "Sequel.~ should invert the given object" do
731
+ l(Sequel.~(nil), 'NOT NULL')
732
+ l(Sequel.~(:a=>1), "(a != 1)")
733
+ l(Sequel.~([[:a, 1]]), "(a != 1)")
734
+ l(Sequel.~([[:a, 1], [:b, 2]]), "((a != 1) OR (b != 2))")
735
+ l(Sequel.~(Sequel.expr([[:a, 1], [:b, 2]]) & nil), "((a != 1) OR (b != 2) OR NOT NULL)")
736
+ end
737
+
738
+ it "Sequel.case should use a CASE expression" do
739
+ l(Sequel.case({:a=>1}, 2), "(CASE WHEN a THEN 1 ELSE 2 END)")
740
+ l(Sequel.case({:a=>1}, 2, :b), "(CASE b WHEN a THEN 1 ELSE 2 END)")
741
+ l(Sequel.case([[:a, 1]], 2), "(CASE WHEN a THEN 1 ELSE 2 END)")
742
+ l(Sequel.case([[:a, 1]], 2, :b), "(CASE b WHEN a THEN 1 ELSE 2 END)")
743
+ l(Sequel.case([[:a, 1], [:c, 3]], 2), "(CASE WHEN a THEN 1 WHEN c THEN 3 ELSE 2 END)")
744
+ l(Sequel.case([[:a, 1], [:c, 3]], 2, :b), "(CASE b WHEN a THEN 1 WHEN c THEN 3 ELSE 2 END)")
745
+ end
746
+
747
+ it "Sequel.case should raise an error if not given a condition specifier" do
748
+ proc{Sequel.case(1, 2)}.should raise_error(Sequel::Error)
749
+ end
750
+
751
+ it "Sequel.value_list should use an SQL value list" do
752
+ l(Sequel.value_list([[1, 2]]), "((1, 2))")
753
+ end
754
+
755
+ it "Sequel.value_list raise an error if not given an array" do
756
+ proc{Sequel.value_list(1)}.should raise_error(Sequel::Error)
757
+ end
758
+
759
+ it "Sequel.negate should negate all entries in conditions specifier and join with AND" do
760
+ l(Sequel.negate(:a=>1), "(a != 1)")
761
+ l(Sequel.negate([[:a, 1]]), "(a != 1)")
762
+ l(Sequel.negate([[:a, 1], [:b, 2]]), "((a != 1) AND (b != 2))")
763
+ end
764
+
765
+ it "Sequel.negate should raise an error if not given a conditions specifier" do
766
+ proc{Sequel.negate(1)}.should raise_error(Sequel::Error)
767
+ end
768
+
769
+ it "Sequel.or should join all entries in conditions specifier with OR" do
770
+ l(Sequel.or(:a=>1), "(a = 1)")
771
+ l(Sequel.or([[:a, 1]]), "(a = 1)")
772
+ l(Sequel.or([[:a, 1], [:b, 2]]), "((a = 1) OR (b = 2))")
773
+ end
774
+
775
+ it "Sequel.or should raise an error if not given a conditions specifier" do
776
+ proc{Sequel.or(1)}.should raise_error(Sequel::Error)
777
+ end
778
+
779
+ it "Sequel.join should should use SQL string concatenation to join array" do
780
+ l(Sequel.join([]), "''")
781
+ l(Sequel.join(['a']), "('a')")
782
+ l(Sequel.join(['a', 'b']), "('a' || 'b')")
783
+ l(Sequel.join(['a', 'b'], 'c'), "('a' || 'c' || 'b')")
784
+ l(Sequel.join([true, :b], :c), "('t' || c || b)")
785
+ l(Sequel.join([false, nil], Sequel.lit('c')), "('f' || c || NULL)")
786
+ l(Sequel.join([Sequel.expr('a'), Sequel.lit('d')], 'c'), "('a' || 'c' || d)")
787
+ end
788
+
789
+ it "Sequel.join should raise an error if not given an array" do
790
+ proc{Sequel.join(1)}.should raise_error(Sequel::Error)
791
+ end
792
+
793
+ it "Sequel.& should join all arguments given with AND" do
794
+ l(Sequel.&(:a), "(a)")
795
+ l(Sequel.&(:a, :b=>:c), "(a AND (b = c))")
796
+ l(Sequel.&(:a, {:b=>:c}, Sequel.lit('d')), "(a AND (b = c) AND d)")
797
+ end
798
+
799
+ it "Sequel.& should raise an error if given no arguments" do
800
+ proc{Sequel.&}.should raise_error(Sequel::Error)
801
+ end
802
+
803
+ it "Sequel.| should join all arguments given with OR" do
804
+ l(Sequel.|(:a), "(a)")
805
+ l(Sequel.|(:a, :b=>:c), "(a OR (b = c))")
806
+ l(Sequel.|(:a, {:b=>:c}, Sequel.lit('d')), "(a OR (b = c) OR d)")
807
+ end
808
+
809
+ it "Sequel.| should raise an error if given no arguments" do
810
+ proc{Sequel.|}.should raise_error(Sequel::Error)
811
+ end
812
+
813
+ it "Sequel.as should return an aliased expression" do
814
+ l(Sequel.as(:a, :b), "a AS b")
815
+ end
816
+
817
+ it "Sequel.cast should return a CAST expression" do
818
+ l(Sequel.cast(:a, :int), "CAST(a AS int)")
819
+ l(Sequel.cast(:a, Integer), "CAST(a AS integer)")
820
+ end
821
+
822
+ it "Sequel.cast_numeric should return a CAST expression treated as a number" do
823
+ l(Sequel.cast_numeric(:a), "CAST(a AS integer)")
824
+ l(Sequel.cast_numeric(:a, :int), "CAST(a AS int)")
825
+ l(Sequel.cast_numeric(:a) << 2, "(CAST(a AS integer) << 2)")
826
+ end
827
+
828
+ it "Sequel.cast_string should return a CAST expression treated as a string" do
829
+ l(Sequel.cast_string(:a), "CAST(a AS varchar(255))")
830
+ l(Sequel.cast_string(:a, :text), "CAST(a AS text)")
831
+ l(Sequel.cast_string(:a) + 'a', "(CAST(a AS varchar(255)) || 'a')")
832
+ end
833
+
834
+ it "Sequel.lit should return a literal string" do
835
+ l(Sequel.lit('a'), "a")
836
+ end
837
+
838
+ it "Sequel.lit should return the argument if given a single literal string" do
839
+ o = Sequel.lit('a')
840
+ Sequel.lit(o).should equal(o)
841
+ end
842
+
843
+ it "Sequel.lit should accept multiple arguments for a placeholder literal string" do
844
+ l(Sequel.lit('a = ?', 1), "a = 1")
845
+ l(Sequel.lit('? = ?', :a, 1), "a = 1")
846
+ l(Sequel.lit('a = :a', :a=>1), "a = 1")
847
+ end
848
+
849
+ it "Sequel.lit should work with an array for the placeholder string" do
850
+ l(Sequel.lit(['a = '], 1), "a = 1")
851
+ l(Sequel.lit(['', ' = '], :a, 1), "a = 1")
852
+ end
853
+
854
+ it "Sequel.blob should return an SQL::Blob" do
855
+ l(Sequel.blob('a'), "'a'")
856
+ Sequel.blob('a').should be_a_kind_of(Sequel::SQL::Blob)
857
+ end
858
+
859
+ it "Sequel.blob should return the given argument if given a blob" do
860
+ o = Sequel.blob('a')
861
+ Sequel.blob(o).should equal(o)
862
+ end
863
+
864
+ it "Sequel.qualify should return a qualified identifier" do
865
+ l(Sequel.qualify(:t, :c), "t.c")
866
+ end
867
+
868
+ it "Sequel.identifier should return an identifier" do
869
+ l(Sequel.identifier(:t__c), "t__c")
870
+ end
871
+
872
+ it "Sequel.asc should return an ASC ordered expression" do
873
+ l(Sequel.asc(:a), "a ASC")
874
+ l(Sequel.asc(:a, :nulls=>:first), "a ASC NULLS FIRST")
875
+ end
876
+
877
+ it "Sequel.desc should return a DESC ordered expression " do
878
+ l(Sequel.desc(:a), "a DESC")
879
+ l(Sequel.desc(:a, :nulls=>:last), "a DESC NULLS LAST")
880
+ end
881
+
882
+ it "Sequel.{+,-,*,/} should accept arguments and use the appropriate operator" do
883
+ %w'+ - * /'.each do |op|
884
+ l(Sequel.send(op, 1), '(1)')
885
+ l(Sequel.send(op, 1, 2), "(1 #{op} 2)")
886
+ l(Sequel.send(op, 1, 2, 3), "(1 #{op} 2 #{op} 3)")
887
+ end
888
+ end
889
+
890
+ it "Sequel.{+,-,*,/} should raise if given no arguments" do
891
+ %w'+ - * /'.each do |op|
892
+ proc{Sequel.send(op)}.should raise_error(Sequel::Error)
893
+ end
894
+ end
895
+
896
+ it "Sequel.like should use a LIKE expression" do
897
+ l(Sequel.like('a', 'b'), "('a' LIKE 'b')")
898
+ l(Sequel.like(:a, :b), "(a LIKE b)")
899
+ l(Sequel.like(:a, /b/), "(a ~ 'b')")
900
+ l(Sequel.like(:a, 'c', /b/), "((a LIKE 'c') OR (a ~ 'b'))")
901
+ end
902
+
903
+ it "Sequel.ilike should use an ILIKE expression" do
904
+ l(Sequel.ilike('a', 'b'), "('a' ILIKE 'b')")
905
+ l(Sequel.ilike(:a, :b), "(a ILIKE b)")
906
+ l(Sequel.ilike(:a, /b/), "(a ~* 'b')")
907
+ l(Sequel.ilike(:a, 'c', /b/), "((a ILIKE 'c') OR (a ~* 'b'))")
908
+ end
909
+
910
+ it "Sequel.subscript should use an SQL subscript" do
911
+ l(Sequel.subscript(:a, 1), 'a[1]')
912
+ l(Sequel.subscript(:a, 1, 2), 'a[1, 2]')
913
+ l(Sequel.subscript(:a, [1, 2]), 'a[1, 2]')
914
+ end
915
+
916
+ it "Sequel.function should return an SQL function" do
917
+ l(Sequel.function(:a), 'a()')
918
+ l(Sequel.function(:a, 1), 'a(1)')
919
+ l(Sequel.function(:a, :b, 2), 'a(b, 2)')
920
+ end
921
+
922
+ it "Sequel.extract should use a date/time extraction" do
923
+ l(Sequel.extract(:year, :a), 'extract(year FROM a)')
924
+ end
925
+ end
926
+
927
+ describe "Sequel::SQL::Function#==" do
928
+ specify "should be true for functions with the same name and arguments, false otherwise" do
929
+ a = :date.sql_function(:t)
930
+ b = :date.sql_function(:t)
931
+ a.should == b
932
+ (a == b).should == true
933
+ c = :date.sql_function(:c)
934
+ a.should_not == c
935
+ (a == c).should == false
936
+ d = :time.sql_function(:c)
937
+ a.should_not == d
938
+ c.should_not == d
939
+ (a == d).should == false
940
+ (c == d).should == false
941
+ end
942
+ end
943
+
944
+ describe "Sequel::SQL::OrderedExpression" do
945
+ specify "should #desc" do
946
+ @oe = Sequel.asc(:column)
947
+ @oe.descending.should == false
948
+ @oe.desc.descending.should == true
949
+ end
950
+
951
+ specify "should #asc" do
952
+ @oe = Sequel.desc(:column)
953
+ @oe.descending.should == true
954
+ @oe.asc.descending.should == false
955
+ end
956
+
957
+ specify "should #invert" do
958
+ @oe = Sequel.desc(:column)
959
+ @oe.invert.descending.should == false
960
+ @oe.invert.invert.descending.should == true
961
+ end
962
+ end
963
+
964
+ describe "Expression" do
965
+ specify "should consider objects == only if they have the same attributes" do
966
+ :column.qualify(:table).cast(:type).*(:numeric_column).asc.should == :column.qualify(:table).cast(:type).*(:numeric_column).asc
967
+ :other_column.qualify(:table).cast(:type).*(:numeric_column).asc.should_not == :column.qualify(:table).cast(:type).*(:numeric_column).asc
968
+
969
+ :column.qualify(:table).cast(:type).*(:numeric_column).asc.should eql(:column.qualify(:table).cast(:type).*(:numeric_column).asc)
970
+ :other_column.qualify(:table).cast(:type).*(:numeric_column).asc.should_not eql(:column.qualify(:table).cast(:type).*(:numeric_column).asc)
971
+ end
972
+
973
+ specify "should use the same hash value for objects that have the same attributes" do
974
+ :column.qualify(:table).cast(:type).*(:numeric_column).asc.hash.should == :column.qualify(:table).cast(:type).*(:numeric_column).asc.hash
975
+ :other_column.qualify(:table).cast(:type).*(:numeric_column).asc.hash.should_not == :column.qualify(:table).cast(:type).*(:numeric_column).asc.hash
976
+
977
+ h = {}
978
+ a = :column.qualify(:table).cast(:type).*(:numeric_column).asc
979
+ b = :column.qualify(:table).cast(:type).*(:numeric_column).asc
980
+ h[a] = 1
981
+ h[b] = 2
982
+ h[a].should == 2
983
+ h[b].should == 2
984
+ end
985
+ end
986
+
987
+ describe "Sequel::SQLTime" do
988
+ before do
989
+ @db = Sequel.mock
990
+ end
991
+
992
+ specify ".create should create from hour, minutes, seconds and optional microseconds" do
993
+ @db.literal(Sequel::SQLTime.create(1, 2, 3)).should == "'01:02:03.000000'"
994
+ @db.literal(Sequel::SQLTime.create(1, 2, 3, 500000)).should == "'01:02:03.500000'"
995
+ end
996
+ end
997
+
998
+ describe "Sequel::SQL::Wrapper" do
999
+ before do
1000
+ @ds = Sequel.mock.dataset
1001
+ end
1002
+
1003
+ specify "should wrap objects so they can be used by the Sequel DSL" do
1004
+ o = Object.new
1005
+ def o.sql_literal(ds) 'foo' end
1006
+ s = Sequel::SQL::Wrapper.new(o)
1007
+ @ds.literal(s).should == "foo"
1008
+ @ds.literal(s+1).should == "(foo + 1)"
1009
+ @ds.literal(s & true).should == "(foo AND 't')"
1010
+ @ds.literal(s < 1).should == "(foo < 1)"
1011
+ @ds.literal(s.sql_subscript(1)).should == "foo[1]"
1012
+ @ds.literal(s.like('a')).should == "(foo LIKE 'a')"
1013
+ @ds.literal(s.as(:a)).should == "foo AS a"
1014
+ @ds.literal(s.cast(Integer)).should == "CAST(foo AS integer)"
1015
+ @ds.literal(s.desc).should == "foo DESC"
1016
+ @ds.literal(s.sql_string + '1').should == "(foo || '1')"
1017
+ end
1018
+ end
1019
+
1020
+ describe "Sequel::SQL::Blob#to_sequel_blob" do
1021
+ specify "should return self" do
1022
+ c = Sequel::SQL::Blob.new('a')
1023
+ c.to_sequel_blob.should equal(c)
1024
+ end
1025
+ end
1026
+
1027
+ describe Sequel::SQL::Subscript do
1028
+ before do
1029
+ @s = Sequel::SQL::Subscript.new(:a, [1])
1030
+ @ds = Sequel::Dataset.new(nil)
1031
+ end
1032
+
1033
+ specify "should have | return a new non-nested subscript" do
1034
+ s = (@s | 2)
1035
+ s.should_not equal(@s)
1036
+ @ds.literal(s).should == 'a[1, 2]'
1037
+ end
1038
+
1039
+ specify "should have [] return a new nested subscript" do
1040
+ s = @s[2]
1041
+ s.should_not equal(@s)
1042
+ @ds.literal(s).should == 'a[1][2]'
1043
+ end
599
1044
  end