sequel 3.21.0 → 3.41.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 (363) hide show
  1. data/CHANGELOG +1131 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +106 -65
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +68 -52
  7. data/doc/advanced_associations.rdoc +201 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/bin_sequel.rdoc +144 -0
  10. data/doc/cheat_sheet.rdoc +21 -21
  11. data/doc/core_extensions.rdoc +374 -0
  12. data/doc/dataset_basics.rdoc +10 -10
  13. data/doc/dataset_filtering.rdoc +54 -42
  14. data/doc/mass_assignment.rdoc +56 -0
  15. data/doc/migration.rdoc +89 -557
  16. data/doc/model_hooks.rdoc +80 -29
  17. data/doc/object_model.rdoc +576 -0
  18. data/doc/opening_databases.rdoc +121 -53
  19. data/doc/prepared_statements.rdoc +41 -15
  20. data/doc/querying.rdoc +137 -56
  21. data/doc/reflection.rdoc +36 -10
  22. data/doc/release_notes/3.22.0.txt +39 -0
  23. data/doc/release_notes/3.23.0.txt +172 -0
  24. data/doc/release_notes/3.24.0.txt +420 -0
  25. data/doc/release_notes/3.25.0.txt +88 -0
  26. data/doc/release_notes/3.26.0.txt +88 -0
  27. data/doc/release_notes/3.27.0.txt +82 -0
  28. data/doc/release_notes/3.28.0.txt +304 -0
  29. data/doc/release_notes/3.29.0.txt +459 -0
  30. data/doc/release_notes/3.30.0.txt +135 -0
  31. data/doc/release_notes/3.31.0.txt +146 -0
  32. data/doc/release_notes/3.32.0.txt +202 -0
  33. data/doc/release_notes/3.33.0.txt +157 -0
  34. data/doc/release_notes/3.34.0.txt +671 -0
  35. data/doc/release_notes/3.35.0.txt +144 -0
  36. data/doc/release_notes/3.36.0.txt +245 -0
  37. data/doc/release_notes/3.37.0.txt +338 -0
  38. data/doc/release_notes/3.38.0.txt +234 -0
  39. data/doc/release_notes/3.39.0.txt +237 -0
  40. data/doc/release_notes/3.40.0.txt +73 -0
  41. data/doc/release_notes/3.41.0.txt +155 -0
  42. data/doc/schema_modification.rdoc +585 -0
  43. data/doc/sharding.rdoc +99 -8
  44. data/doc/sql.rdoc +154 -112
  45. data/doc/testing.rdoc +169 -0
  46. data/doc/thread_safety.rdoc +17 -0
  47. data/doc/transactions.rdoc +137 -0
  48. data/doc/validations.rdoc +1 -1
  49. data/doc/virtual_rows.rdoc +78 -43
  50. data/lib/sequel/adapters/ado/access.rb +334 -0
  51. data/lib/sequel/adapters/ado/mssql.rb +18 -12
  52. data/lib/sequel/adapters/ado.rb +69 -21
  53. data/lib/sequel/adapters/amalgalite.rb +17 -14
  54. data/lib/sequel/adapters/cubrid.rb +143 -0
  55. data/lib/sequel/adapters/db2.rb +178 -85
  56. data/lib/sequel/adapters/dbi.rb +19 -21
  57. data/lib/sequel/adapters/do/mysql.rb +17 -11
  58. data/lib/sequel/adapters/do/postgres.rb +2 -61
  59. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  60. data/lib/sequel/adapters/do.rb +20 -39
  61. data/lib/sequel/adapters/firebird.rb +33 -210
  62. data/lib/sequel/adapters/ibmdb.rb +452 -0
  63. data/lib/sequel/adapters/informix.rb +6 -29
  64. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  65. data/lib/sequel/adapters/jdbc/cubrid.rb +52 -0
  66. data/lib/sequel/adapters/jdbc/db2.rb +78 -0
  67. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  68. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  69. data/lib/sequel/adapters/jdbc/h2.rb +68 -31
  70. data/lib/sequel/adapters/jdbc/hsqldb.rb +189 -0
  71. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  73. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  74. data/lib/sequel/adapters/jdbc/mysql.rb +16 -28
  75. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  76. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  77. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  78. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  79. data/lib/sequel/adapters/jdbc/sqlserver.rb +72 -0
  80. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  81. data/lib/sequel/adapters/jdbc.rb +308 -114
  82. data/lib/sequel/adapters/mock.rb +372 -0
  83. data/lib/sequel/adapters/mysql.rb +130 -230
  84. data/lib/sequel/adapters/mysql2.rb +64 -63
  85. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  86. data/lib/sequel/adapters/odbc.rb +29 -10
  87. data/lib/sequel/adapters/openbase.rb +7 -11
  88. data/lib/sequel/adapters/oracle.rb +364 -57
  89. data/lib/sequel/adapters/postgres.rb +379 -145
  90. data/lib/sequel/adapters/shared/access.rb +236 -11
  91. data/lib/sequel/adapters/shared/cubrid.rb +216 -0
  92. data/lib/sequel/adapters/shared/db2.rb +353 -0
  93. data/lib/sequel/adapters/shared/firebird.rb +231 -0
  94. data/lib/sequel/adapters/shared/informix.rb +53 -0
  95. data/lib/sequel/adapters/shared/mssql.rb +412 -116
  96. data/lib/sequel/adapters/shared/mysql.rb +396 -89
  97. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +166 -0
  98. data/lib/sequel/adapters/shared/oracle.rb +262 -45
  99. data/lib/sequel/adapters/shared/postgres.rb +688 -279
  100. data/lib/sequel/adapters/shared/progress.rb +5 -8
  101. data/lib/sequel/adapters/shared/sqlite.rb +317 -96
  102. data/lib/sequel/adapters/sqlite.rb +109 -46
  103. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  104. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  105. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  106. data/lib/sequel/adapters/swift.rb +31 -27
  107. data/lib/sequel/adapters/tinytds.rb +177 -25
  108. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  109. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  110. data/lib/sequel/adapters/utils/split_alter_table.rb +36 -0
  111. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  112. data/lib/sequel/ast_transformer.rb +194 -0
  113. data/lib/sequel/connection_pool/sharded_single.rb +23 -13
  114. data/lib/sequel/connection_pool/sharded_threaded.rb +64 -13
  115. data/lib/sequel/connection_pool/single.rb +11 -3
  116. data/lib/sequel/connection_pool/threaded.rb +69 -20
  117. data/lib/sequel/connection_pool.rb +20 -18
  118. data/lib/sequel/core.rb +143 -11
  119. data/lib/sequel/database/connecting.rb +66 -29
  120. data/lib/sequel/database/dataset.rb +4 -4
  121. data/lib/sequel/database/dataset_defaults.rb +63 -2
  122. data/lib/sequel/database/logging.rb +7 -2
  123. data/lib/sequel/database/misc.rb +192 -51
  124. data/lib/sequel/database/query.rb +299 -114
  125. data/lib/sequel/database/schema_generator.rb +103 -36
  126. data/lib/sequel/database/schema_methods.rb +310 -69
  127. data/lib/sequel/dataset/actions.rb +344 -90
  128. data/lib/sequel/dataset/features.rb +101 -8
  129. data/lib/sequel/dataset/graph.rb +42 -15
  130. data/lib/sequel/dataset/misc.rb +52 -25
  131. data/lib/sequel/dataset/mutation.rb +12 -7
  132. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  133. data/lib/sequel/dataset/query.rb +322 -68
  134. data/lib/sequel/dataset/sql.rb +716 -293
  135. data/lib/sequel/dataset.rb +8 -0
  136. data/lib/sequel/exceptions.rb +4 -0
  137. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  138. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  139. data/lib/sequel/extensions/blank.rb +4 -0
  140. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  141. data/lib/sequel/extensions/connection_validator.rb +109 -0
  142. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  143. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  144. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  145. data/lib/sequel/extensions/inflector.rb +4 -0
  146. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  147. data/lib/sequel/extensions/migration.rb +100 -11
  148. data/lib/sequel/extensions/named_timezones.rb +9 -0
  149. data/lib/sequel/extensions/null_dataset.rb +94 -0
  150. data/lib/sequel/extensions/pagination.rb +4 -0
  151. data/lib/sequel/extensions/pg_array.rb +539 -0
  152. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  153. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  154. data/lib/sequel/extensions/pg_hstore.rb +334 -0
  155. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  156. data/lib/sequel/extensions/pg_inet.rb +113 -0
  157. data/lib/sequel/extensions/pg_interval.rb +191 -0
  158. data/lib/sequel/extensions/pg_json.rb +212 -0
  159. data/lib/sequel/extensions/pg_range.rb +513 -0
  160. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  161. data/lib/sequel/extensions/pg_row.rb +576 -0
  162. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  163. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  164. data/lib/sequel/extensions/pretty_table.rb +17 -72
  165. data/lib/sequel/extensions/query.rb +8 -4
  166. data/lib/sequel/extensions/query_literals.rb +79 -0
  167. data/lib/sequel/extensions/schema_caching.rb +76 -0
  168. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  169. data/lib/sequel/extensions/select_remove.rb +39 -0
  170. data/lib/sequel/extensions/server_block.rb +140 -0
  171. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  172. data/lib/sequel/extensions/sql_expr.rb +8 -110
  173. data/lib/sequel/extensions/string_date_time.rb +4 -0
  174. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  175. data/lib/sequel/extensions/to_dot.rb +99 -83
  176. data/lib/sequel/model/associations.rb +1263 -409
  177. data/lib/sequel/model/base.rb +624 -171
  178. data/lib/sequel/model/errors.rb +1 -1
  179. data/lib/sequel/model/exceptions.rb +24 -2
  180. data/lib/sequel/model/inflections.rb +1 -1
  181. data/lib/sequel/model.rb +30 -11
  182. data/lib/sequel/no_core_ext.rb +2 -0
  183. data/lib/sequel/plugins/active_model.rb +13 -1
  184. data/lib/sequel/plugins/association_pks.rb +137 -16
  185. data/lib/sequel/plugins/caching.rb +25 -18
  186. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  187. data/lib/sequel/plugins/composition.rb +44 -12
  188. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  189. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  190. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  191. data/lib/sequel/plugins/dirty.rb +214 -0
  192. data/lib/sequel/plugins/eager_each.rb +59 -0
  193. data/lib/sequel/plugins/force_encoding.rb +6 -6
  194. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  195. data/lib/sequel/plugins/identity_map.rb +134 -15
  196. data/lib/sequel/plugins/instance_filters.rb +10 -0
  197. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  198. data/lib/sequel/plugins/json_serializer.rb +77 -6
  199. data/lib/sequel/plugins/list.rb +13 -2
  200. data/lib/sequel/plugins/many_through_many.rb +103 -51
  201. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  202. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  203. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  204. data/lib/sequel/plugins/pg_row.rb +121 -0
  205. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  206. data/lib/sequel/plugins/prepared_statements.rb +168 -0
  207. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  208. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  209. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  210. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  211. data/lib/sequel/plugins/schema.rb +8 -3
  212. data/lib/sequel/plugins/serialization.rb +98 -49
  213. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  214. data/lib/sequel/plugins/sharding.rb +21 -54
  215. data/lib/sequel/plugins/single_table_inheritance.rb +57 -12
  216. data/lib/sequel/plugins/static_cache.rb +99 -0
  217. data/lib/sequel/plugins/subclasses.rb +29 -3
  218. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  219. data/lib/sequel/plugins/timestamps.rb +1 -1
  220. data/lib/sequel/plugins/touch.rb +18 -6
  221. data/lib/sequel/plugins/tree.rb +3 -3
  222. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  223. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  224. data/lib/sequel/plugins/validation_class_methods.rb +2 -1
  225. data/lib/sequel/plugins/validation_helpers.rb +57 -4
  226. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  227. data/lib/sequel/sql.rb +721 -141
  228. data/lib/sequel/timezones.rb +67 -40
  229. data/lib/sequel/version.rb +1 -1
  230. data/spec/adapters/db2_spec.rb +146 -0
  231. data/spec/adapters/firebird_spec.rb +53 -39
  232. data/spec/adapters/mssql_spec.rb +261 -66
  233. data/spec/adapters/mysql_spec.rb +597 -352
  234. data/spec/adapters/oracle_spec.rb +92 -117
  235. data/spec/adapters/postgres_spec.rb +2103 -341
  236. data/spec/adapters/spec_helper.rb +16 -6
  237. data/spec/adapters/sqlite_spec.rb +237 -76
  238. data/spec/core/connection_pool_spec.rb +270 -141
  239. data/spec/core/database_spec.rb +980 -524
  240. data/spec/core/dataset_spec.rb +1658 -1243
  241. data/spec/core/expression_filters_spec.rb +741 -334
  242. data/spec/core/mock_adapter_spec.rb +453 -0
  243. data/spec/core/object_graph_spec.rb +88 -129
  244. data/spec/core/schema_generator_spec.rb +4 -4
  245. data/spec/core/schema_spec.rb +556 -53
  246. data/spec/core/spec_helper.rb +7 -48
  247. data/spec/core_extensions_spec.rb +626 -0
  248. data/spec/extensions/active_model_spec.rb +13 -0
  249. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  250. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  251. data/spec/extensions/association_dependencies_spec.rb +15 -25
  252. data/spec/extensions/association_pks_spec.rb +227 -33
  253. data/spec/extensions/association_proxies_spec.rb +4 -4
  254. data/spec/extensions/boolean_readers_spec.rb +25 -25
  255. data/spec/extensions/caching_spec.rb +47 -51
  256. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  257. data/spec/extensions/columns_introspection_spec.rb +91 -0
  258. data/spec/extensions/composition_spec.rb +18 -13
  259. data/spec/extensions/connection_validator_spec.rb +118 -0
  260. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  261. data/spec/extensions/constraint_validations_spec.rb +316 -0
  262. data/spec/extensions/dataset_associations_spec.rb +199 -0
  263. data/spec/extensions/defaults_setter_spec.rb +88 -0
  264. data/spec/extensions/dirty_spec.rb +155 -0
  265. data/spec/extensions/eager_each_spec.rb +34 -0
  266. data/spec/extensions/eval_inspect_spec.rb +67 -0
  267. data/spec/extensions/force_encoding_spec.rb +4 -2
  268. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  269. data/spec/extensions/identity_map_spec.rb +142 -24
  270. data/spec/extensions/inflector_spec.rb +0 -4
  271. data/spec/extensions/instance_filters_spec.rb +11 -21
  272. data/spec/extensions/instance_hooks_spec.rb +72 -0
  273. data/spec/extensions/json_serializer_spec.rb +49 -12
  274. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  275. data/spec/extensions/list_spec.rb +49 -40
  276. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  277. data/spec/extensions/many_through_many_spec.rb +368 -254
  278. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  279. data/spec/extensions/migration_spec.rb +140 -35
  280. data/spec/extensions/named_timezones_spec.rb +29 -11
  281. data/spec/extensions/nested_attributes_spec.rb +268 -89
  282. data/spec/extensions/null_dataset_spec.rb +85 -0
  283. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  284. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  285. data/spec/extensions/pg_array_spec.rb +324 -0
  286. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  287. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  288. data/spec/extensions/pg_hstore_spec.rb +200 -0
  289. data/spec/extensions/pg_inet_spec.rb +47 -0
  290. data/spec/extensions/pg_interval_spec.rb +72 -0
  291. data/spec/extensions/pg_json_spec.rb +99 -0
  292. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  293. data/spec/extensions/pg_range_spec.rb +395 -0
  294. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  295. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  296. data/spec/extensions/pg_row_spec.rb +340 -0
  297. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  298. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  299. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  300. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  301. data/spec/extensions/prepared_statements_spec.rb +87 -0
  302. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  303. data/spec/extensions/pretty_table_spec.rb +7 -6
  304. data/spec/extensions/query_literals_spec.rb +167 -0
  305. data/spec/extensions/query_spec.rb +9 -3
  306. data/spec/extensions/rcte_tree_spec.rb +50 -43
  307. data/spec/extensions/schema_caching_spec.rb +41 -0
  308. data/spec/extensions/schema_dumper_spec.rb +434 -49
  309. data/spec/extensions/schema_spec.rb +30 -49
  310. data/spec/extensions/select_remove_spec.rb +38 -0
  311. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  312. data/spec/extensions/serialization_spec.rb +86 -48
  313. data/spec/extensions/server_block_spec.rb +90 -0
  314. data/spec/extensions/sharding_spec.rb +69 -143
  315. data/spec/extensions/single_table_inheritance_spec.rb +65 -29
  316. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  317. data/spec/extensions/spec_helper.rb +34 -67
  318. data/spec/extensions/split_array_nil_spec.rb +24 -0
  319. data/spec/extensions/sql_expr_spec.rb +29 -60
  320. data/spec/extensions/static_cache_spec.rb +145 -0
  321. data/spec/extensions/subclasses_spec.rb +14 -0
  322. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  323. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  324. data/spec/extensions/timestamps_spec.rb +6 -6
  325. data/spec/extensions/to_dot_spec.rb +8 -11
  326. data/spec/extensions/touch_spec.rb +53 -14
  327. data/spec/extensions/tree_spec.rb +11 -26
  328. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  329. data/spec/extensions/update_primary_key_spec.rb +30 -24
  330. data/spec/extensions/validation_class_methods_spec.rb +77 -68
  331. data/spec/extensions/validation_helpers_spec.rb +48 -37
  332. data/spec/extensions/xml_serializer_spec.rb +29 -4
  333. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  334. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  335. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  336. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  337. data/spec/integration/associations_test.rb +659 -21
  338. data/spec/integration/database_test.rb +25 -5
  339. data/spec/integration/dataset_test.rb +729 -141
  340. data/spec/integration/eager_loader_test.rb +38 -54
  341. data/spec/integration/migrator_test.rb +2 -3
  342. data/spec/integration/model_test.rb +81 -2
  343. data/spec/integration/plugin_test.rb +871 -160
  344. data/spec/integration/prepared_statement_test.rb +272 -128
  345. data/spec/integration/schema_test.rb +377 -57
  346. data/spec/integration/spec_helper.rb +49 -32
  347. data/spec/integration/timezone_test.rb +38 -12
  348. data/spec/integration/transaction_test.rb +183 -8
  349. data/spec/integration/type_test.rb +40 -8
  350. data/spec/model/association_reflection_spec.rb +207 -8
  351. data/spec/model/associations_spec.rb +1206 -693
  352. data/spec/model/base_spec.rb +334 -100
  353. data/spec/model/dataset_methods_spec.rb +45 -27
  354. data/spec/model/eager_loading_spec.rb +828 -692
  355. data/spec/model/hooks_spec.rb +355 -67
  356. data/spec/model/model_spec.rb +251 -179
  357. data/spec/model/plugins_spec.rb +24 -13
  358. data/spec/model/record_spec.rb +506 -232
  359. data/spec/model/spec_helper.rb +16 -77
  360. data/spec/model/validations_spec.rb +38 -2
  361. data/spec/rcov.opts +2 -0
  362. metadata +223 -81
  363. data/spec/core/core_sql_spec.rb +0 -451
@@ -14,10 +14,6 @@ INTEGRATION_DB = MYSQL_DB unless defined?(INTEGRATION_DB)
14
14
 
15
15
  MYSQL_URI = URI.parse(MYSQL_DB.uri)
16
16
 
17
- MYSQL_DB.create_table! :test2 do
18
- text :name
19
- integer :value
20
- end
21
17
  def MYSQL_DB.sqls
22
18
  (@sqls ||= [])
23
19
  end
@@ -26,9 +22,7 @@ def logger.method_missing(m, msg)
26
22
  MYSQL_DB.sqls << msg
27
23
  end
28
24
  MYSQL_DB.loggers = [logger]
29
- MYSQL_DB.drop_table(:items) rescue nil
30
- MYSQL_DB.drop_table(:dolls) rescue nil
31
- MYSQL_DB.drop_table(:booltest) rescue nil
25
+ MYSQL_DB.drop_table?(:items, :dolls, :booltest)
32
26
 
33
27
  SQL_BEGIN = 'BEGIN'
34
28
  SQL_ROLLBACK = 'ROLLBACK'
@@ -40,29 +34,37 @@ describe "MySQL", '#create_table' do
40
34
  MYSQL_DB.sqls.clear
41
35
  end
42
36
  after do
43
- @db.drop_table(:dolls) rescue nil
37
+ @db.drop_table?(:dolls)
44
38
  end
45
-
39
+
46
40
  specify "should allow to specify options for MySQL" do
47
41
  @db.create_table(:dolls, :engine => 'MyISAM', :charset => 'latin2'){text :name}
48
- @db.sqls.should == ["CREATE TABLE dolls (name text) ENGINE=MyISAM DEFAULT CHARSET=latin2"]
42
+ check_sqls do
43
+ @db.sqls.should == ["CREATE TABLE `dolls` (`name` text) ENGINE=MyISAM DEFAULT CHARSET=latin2"]
44
+ end
49
45
  end
50
-
46
+
51
47
  specify "should create a temporary table" do
52
48
  @db.create_table(:tmp_dolls, :temp => true, :engine => 'MyISAM', :charset => 'latin2'){text :name}
53
- @db.sqls.should == ["CREATE TEMPORARY TABLE tmp_dolls (name text) ENGINE=MyISAM DEFAULT CHARSET=latin2"]
49
+ check_sqls do
50
+ @db.sqls.should == ["CREATE TEMPORARY TABLE `tmp_dolls` (`name` text) ENGINE=MyISAM DEFAULT CHARSET=latin2"]
51
+ end
54
52
  end
55
-
53
+
56
54
  specify "should not use a default for a String :text=>true type" do
57
55
  @db.create_table(:dolls){String :name, :text=>true, :default=>'blah'}
58
- @db.sqls.should == ["CREATE TABLE dolls (name text)"]
56
+ check_sqls do
57
+ @db.sqls.should == ["CREATE TABLE `dolls` (`name` text)"]
58
+ end
59
59
  end
60
-
60
+
61
61
  specify "should not use a default for a File type" do
62
62
  @db.create_table(:dolls){File :name, :default=>'blah'}
63
- @db.sqls.should == ["CREATE TABLE dolls (name blob)"]
63
+ check_sqls do
64
+ @db.sqls.should == ["CREATE TABLE `dolls` (`name` blob)"]
65
+ end
64
66
  end
65
-
67
+
66
68
  specify "should respect the size option for File type" do
67
69
  @db.create_table(:dolls) do
68
70
  File :n1
@@ -82,42 +84,41 @@ describe "MySQL", '#create_table' do
82
84
  end
83
85
  @db.schema(:dolls).map{|k, v| v[:auto_increment]}.should == [nil, nil, true]
84
86
  end
85
- end
86
87
 
87
- describe "A MySQL database" do
88
- specify "should provide the server version" do
89
- MYSQL_DB.server_version.should >= 40000
88
+ specify "should support collate with various other column options" do
89
+ @db.create_table!(:dolls){ String :name, :size=>128, :collate=>:utf8_bin, :default=>'foo', :null=>false, :unique=>true}
90
+ @db[:dolls].insert
91
+ @db[:dolls].select_map(:name).should == ["foo"]
90
92
  end
91
-
92
- specify "should handle the creation and dropping of an InnoDB table with foreign keys" do
93
- proc{MYSQL_DB.create_table!(:test_innodb, :engine=>:InnoDB){primary_key :id; foreign_key :fk, :test_innodb, :key=>:id}}.should_not raise_error
94
- end
95
-
96
- specify "should support for_share" do
97
- MYSQL_DB.transaction{MYSQL_DB[:test2].for_share.all.should == []}
93
+
94
+ specify "should be able to parse the default value for set and enum types" do
95
+ @db.create_table!(:dolls){column :t, "set('a', 'b', 'c', 'd')", :default=>'a,b'}
96
+ @db.schema(:dolls).first.last[:ruby_default].should == 'a,b'
97
+ @db.create_table!(:dolls){column :t, "enum('a', 'b', 'c', 'd')", :default=>'b'}
98
+ @db.schema(:dolls).first.last[:ruby_default].should == 'b'
98
99
  end
99
100
  end
100
101
 
101
- if MYSQL_DB.adapter_scheme == :mysql
102
- describe "Sequel::MySQL.convert_tinyint_to_bool" do
102
+ if [:mysql, :mysql2].include?(MYSQL_DB.adapter_scheme)
103
+ describe "Sequel::MySQL::Database#convert_tinyint_to_bool" do
103
104
  before do
104
105
  @db = MYSQL_DB
105
106
  @db.create_table(:booltest){column :b, 'tinyint(1)'; column :i, 'tinyint(4)'}
106
107
  @ds = @db[:booltest]
107
108
  end
108
109
  after do
109
- Sequel::MySQL.convert_tinyint_to_bool = true
110
- @db.drop_table(:booltest)
110
+ @db.convert_tinyint_to_bool = true
111
+ @db.drop_table?(:booltest)
111
112
  end
112
-
113
+
113
114
  specify "should consider tinyint(1) datatypes as boolean if set, but not larger tinyints" do
114
115
  @db.schema(:booltest, :reload=>true).should == [[:b, {:type=>:boolean, :allow_null=>true, :primary_key=>false, :default=>nil, :ruby_default=>nil, :db_type=>"tinyint(1)"}, ], [:i, {:type=>:integer, :allow_null=>true, :primary_key=>false, :default=>nil, :ruby_default=>nil, :db_type=>"tinyint(4)"}, ]]
115
- Sequel::MySQL.convert_tinyint_to_bool = false
116
+ @db.convert_tinyint_to_bool = false
116
117
  @db.schema(:booltest, :reload=>true).should == [[:b, {:type=>:integer, :allow_null=>true, :primary_key=>false, :default=>nil, :ruby_default=>nil, :db_type=>"tinyint(1)"}, ], [:i, {:type=>:integer, :allow_null=>true, :primary_key=>false, :default=>nil, :ruby_default=>nil, :db_type=>"tinyint(4)"}, ]]
117
118
  end
118
-
119
+
119
120
  specify "should return tinyint(1)s as bools and tinyint(4)s as integers when set" do
120
- Sequel::MySQL.convert_tinyint_to_bool = true
121
+ @db.convert_tinyint_to_bool = true
121
122
  @ds.delete
122
123
  @ds << {:b=>true, :i=>10}
123
124
  @ds.all.should == [{:b=>true, :i=>10}]
@@ -130,14 +131,14 @@ if MYSQL_DB.adapter_scheme == :mysql
130
131
  end
131
132
 
132
133
  specify "should return all tinyints as integers when unset" do
133
- Sequel::MySQL.convert_tinyint_to_bool = false
134
+ @db.convert_tinyint_to_bool = false
134
135
  @ds.delete
135
136
  @ds << {:b=>true, :i=>10}
136
137
  @ds.all.should == [{:b=>1, :i=>10}]
137
138
  @ds.delete
138
139
  @ds << {:b=>false, :i=>0}
139
140
  @ds.all.should == [{:b=>0, :i=>0}]
140
-
141
+
141
142
  @ds.delete
142
143
  @ds << {:b=>1, :i=>10}
143
144
  @ds.all.should == [{:b=>1, :i=>10}]
@@ -145,6 +146,17 @@ if MYSQL_DB.adapter_scheme == :mysql
145
146
  @ds << {:b=>0, :i=>0}
146
147
  @ds.all.should == [{:b=>0, :i=>0}]
147
148
  end
149
+
150
+ specify "should allow disabling the conversion on a per-dataset basis" do
151
+ @db.convert_tinyint_to_bool = true
152
+ ds = @ds.clone
153
+ ds.meta_def(:cast_tinyint_integer?){|f| true} # mysql
154
+ ds.meta_def(:convert_tinyint_to_bool?){false} # mysql2
155
+ ds.delete
156
+ ds << {:b=>true, :i=>10}
157
+ ds.all.should == [{:b=>1, :i=>10}]
158
+ @ds.all.should == [{:b=>true, :i=>10}]
159
+ end
148
160
  end
149
161
  end
150
162
 
@@ -155,106 +167,86 @@ describe "A MySQL dataset" do
155
167
  MYSQL_DB.sqls.clear
156
168
  end
157
169
  after do
158
- MYSQL_DB.drop_table(:items)
170
+ MYSQL_DB.drop_table?(:items)
159
171
  end
160
-
172
+
161
173
  specify "should quote columns and tables using back-ticks if quoting identifiers" do
162
174
  @d.quote_identifiers = true
163
- @d.select(:name).sql.should == \
164
- 'SELECT `name` FROM `items`'
165
-
166
- @d.select('COUNT(*)'.lit).sql.should == \
167
- 'SELECT COUNT(*) FROM `items`'
168
-
169
- @d.select(:max.sql_function(:value)).sql.should == \
170
- 'SELECT max(`value`) FROM `items`'
171
-
172
- @d.select(:NOW.sql_function).sql.should == \
173
- 'SELECT NOW() FROM `items`'
174
-
175
- @d.select(:max.sql_function(:items__value)).sql.should == \
176
- 'SELECT max(`items`.`value`) FROM `items`'
177
-
178
- @d.order(:name.desc).sql.should == \
179
- 'SELECT * FROM `items` ORDER BY `name` DESC'
180
-
181
- @d.select('items.name AS item_name'.lit).sql.should == \
182
- 'SELECT items.name AS item_name FROM `items`'
183
-
184
- @d.select('`name`'.lit).sql.should == \
185
- 'SELECT `name` FROM `items`'
186
-
187
- @d.select('max(items.`name`) AS `max_name`'.lit).sql.should == \
188
- 'SELECT max(items.`name`) AS `max_name` FROM `items`'
189
-
190
- @d.select(:test.sql_function(:abc, 'hello')).sql.should == \
191
- "SELECT test(`abc`, 'hello') FROM `items`"
192
-
193
- @d.select(:test.sql_function(:abc__def, 'hello')).sql.should == \
194
- "SELECT test(`abc`.`def`, 'hello') FROM `items`"
195
-
196
- @d.select(:test.sql_function(:abc__def, 'hello').as(:x2)).sql.should == \
197
- "SELECT test(`abc`.`def`, 'hello') AS `x2` FROM `items`"
198
-
199
- @d.insert_sql(:value => 333).should == \
200
- 'INSERT INTO `items` (`value`) VALUES (333)'
201
-
202
- @d.insert_sql(:x => :y).should == \
203
- 'INSERT INTO `items` (`x`) VALUES (`y`)'
175
+ @d.select(:name).sql.should == 'SELECT `name` FROM `items`'
176
+ @d.select(Sequel.lit('COUNT(*)')).sql.should == 'SELECT COUNT(*) FROM `items`'
177
+ @d.select(Sequel.function(:max, :value)).sql.should == 'SELECT max(`value`) FROM `items`'
178
+ @d.select(Sequel.function(:NOW)).sql.should == 'SELECT NOW() FROM `items`'
179
+ @d.select(Sequel.function(:max, :items__value)).sql.should == 'SELECT max(`items`.`value`) FROM `items`'
180
+ @d.order(Sequel.expr(:name).desc).sql.should == 'SELECT * FROM `items` ORDER BY `name` DESC'
181
+ @d.select(Sequel.lit('items.name AS item_name')).sql.should == 'SELECT items.name AS item_name FROM `items`'
182
+ @d.select(Sequel.lit('`name`')).sql.should == 'SELECT `name` FROM `items`'
183
+ @d.select(Sequel.lit('max(items.`name`) AS `max_name`')).sql.should == 'SELECT max(items.`name`) AS `max_name` FROM `items`'
184
+ @d.select(Sequel.function(:test, :abc, 'hello')).sql.should == "SELECT test(`abc`, 'hello') FROM `items`"
185
+ @d.select(Sequel.function(:test, :abc__def, 'hello')).sql.should == "SELECT test(`abc`.`def`, 'hello') FROM `items`"
186
+ @d.select(Sequel.function(:test, :abc__def, 'hello').as(:x2)).sql.should == "SELECT test(`abc`.`def`, 'hello') AS `x2` FROM `items`"
187
+ @d.insert_sql(:value => 333).should == 'INSERT INTO `items` (`value`) VALUES (333)'
188
+ @d.insert_sql(:x => :y).should == 'INSERT INTO `items` (`x`) VALUES (`y`)'
204
189
  end
205
-
190
+
206
191
  specify "should quote fields correctly when reversing the order" do
207
192
  @d.quote_identifiers = true
208
- @d.reverse_order(:name).sql.should == \
209
- 'SELECT * FROM `items` ORDER BY `name` DESC'
210
-
211
- @d.reverse_order(:name.desc).sql.should == \
212
- 'SELECT * FROM `items` ORDER BY `name` ASC'
213
-
214
- @d.reverse_order(:name, :test.desc).sql.should == \
215
- 'SELECT * FROM `items` ORDER BY `name` DESC, `test` ASC'
216
-
217
- @d.reverse_order(:name.desc, :test).sql.should == \
218
- 'SELECT * FROM `items` ORDER BY `name` ASC, `test` DESC'
193
+ @d.reverse_order(:name).sql.should == 'SELECT * FROM `items` ORDER BY `name` DESC'
194
+ @d.reverse_order(Sequel.desc(:name)).sql.should == 'SELECT * FROM `items` ORDER BY `name` ASC'
195
+ @d.reverse_order(:name, Sequel.desc(:test)).sql.should == 'SELECT * FROM `items` ORDER BY `name` DESC, `test` ASC'
196
+ @d.reverse_order(Sequel.desc(:name), :test).sql.should == 'SELECT * FROM `items` ORDER BY `name` ASC, `test` DESC'
219
197
  end
220
-
198
+
221
199
  specify "should support ORDER clause in UPDATE statements" do
222
- @d.order(:name).update_sql(:value => 1).should == \
223
- 'UPDATE items SET value = 1 ORDER BY name'
200
+ @d.order(:name).update_sql(:value => 1).should == 'UPDATE `items` SET `value` = 1 ORDER BY `name`'
224
201
  end
225
-
202
+
226
203
  specify "should support LIMIT clause in UPDATE statements" do
227
- @d.limit(10).update_sql(:value => 1).should == \
228
- 'UPDATE items SET value = 1 LIMIT 10'
204
+ @d.limit(10).update_sql(:value => 1).should == 'UPDATE `items` SET `value` = 1 LIMIT 10'
229
205
  end
230
-
206
+
231
207
  specify "should support regexps" do
232
208
  @d << {:name => 'abc', :value => 1}
233
209
  @d << {:name => 'bcd', :value => 2}
234
210
  @d.filter(:name => /bc/).count.should == 2
235
211
  @d.filter(:name => /^bc/).count.should == 1
236
212
  end
237
-
213
+
214
+ specify "should have explain output" do
215
+ @d.explain.should be_a_kind_of(String)
216
+ @d.explain(:extended=>true).should be_a_kind_of(String)
217
+ @d.explain.should_not == @d.explain(:extended=>true)
218
+ end
219
+
238
220
  specify "should correctly literalize strings with comment backslashes in them" do
239
221
  @d.delete
240
222
  proc {@d << {:name => ':\\'}}.should_not raise_error
241
-
223
+
242
224
  @d.first[:name].should == ':\\'
243
225
  end
226
+
227
+ specify "should handle prepared statements with on_duplicate_key_update" do
228
+ @d.db.add_index :items, :value, :unique=>true
229
+ ds = @d.on_duplicate_key_update
230
+ ps = ds.prepare(:insert, :insert_user_id_feature_name, :value => :$v, :name => :$n)
231
+ ps.call(:v => 1, :n => 'a')
232
+ ds.all.should == [{:value=>1, :name=>'a'}]
233
+ ps.call(:v => 1, :n => 'b')
234
+ ds.all.should == [{:value=>1, :name=>'b'}]
235
+ end
244
236
  end
245
237
 
246
238
  describe "MySQL datasets" do
247
239
  before do
248
240
  @d = MYSQL_DB[:orders]
249
241
  end
250
-
242
+
251
243
  specify "should correctly quote column references" do
252
244
  @d.quote_identifiers = true
253
245
  market = 'ICE'
254
246
  ack_stamp = Time.now - 15 * 60 # 15 minutes ago
255
- @d.select(:market, :minute.sql_function(:from_unixtime.sql_function(:ack)).as(:minute)).
256
- where{|o|(:ack.sql_number > ack_stamp) & {:market => market}}.
257
- group_by(:minute.sql_function(:from_unixtime.sql_function(:ack))).sql.should == \
247
+ @d.select(:market, Sequel.function(:minute, Sequel.function(:from_unixtime, :ack)).as(:minute)).
248
+ where{(ack > ack_stamp) & {:market => market}}.
249
+ group_by(Sequel.function(:minute, Sequel.function(:from_unixtime, :ack))).sql.should == \
258
250
  "SELECT `market`, minute(from_unixtime(`ack`)) AS `minute` FROM `orders` WHERE ((`ack` > #{@d.literal(ack_stamp)}) AND (`market` = 'ICE')) GROUP BY minute(from_unixtime(`ack`))"
259
251
  end
260
252
  end
@@ -269,14 +261,14 @@ describe "Dataset#distinct" do
269
261
  @ds = @db[:a]
270
262
  end
271
263
  after do
272
- @db.drop_table(:a)
264
+ @db.drop_table?(:a)
273
265
  end
274
-
266
+
275
267
  it "#distinct with arguments should return results distinct on those arguments" do
276
268
  @ds.insert(20, 10)
277
269
  @ds.insert(30, 10)
278
270
  @ds.order(:b, :a).distinct.map(:a).should == [20, 30]
279
- @ds.order(:b, :a.desc).distinct.map(:a).should == [30, 20]
271
+ @ds.order(:b, Sequel.desc(:a)).distinct.map(:a).should == [30, 20]
280
272
  # MySQL doesn't respect orders when using the nonstandard GROUP BY
281
273
  [[20], [30]].should include(@ds.order(:b, :a).distinct(:b).map(:a))
282
274
  end
@@ -285,7 +277,6 @@ end
285
277
  describe "MySQL join expressions" do
286
278
  before do
287
279
  @ds = MYSQL_DB[:nodes]
288
- @ds.db.meta_def(:server_version) {50014}
289
280
  end
290
281
 
291
282
  specify "should raise error for :full_outer join requests." do
@@ -293,43 +284,43 @@ describe "MySQL join expressions" do
293
284
  end
294
285
  specify "should support natural left joins" do
295
286
  @ds.join_table(:natural_left, :nodes).sql.should == \
296
- 'SELECT * FROM nodes NATURAL LEFT JOIN nodes'
287
+ 'SELECT * FROM `nodes` NATURAL LEFT JOIN `nodes`'
297
288
  end
298
289
  specify "should support natural right joins" do
299
290
  @ds.join_table(:natural_right, :nodes).sql.should == \
300
- 'SELECT * FROM nodes NATURAL RIGHT JOIN nodes'
291
+ 'SELECT * FROM `nodes` NATURAL RIGHT JOIN `nodes`'
301
292
  end
302
293
  specify "should support natural left outer joins" do
303
294
  @ds.join_table(:natural_left_outer, :nodes).sql.should == \
304
- 'SELECT * FROM nodes NATURAL LEFT OUTER JOIN nodes'
295
+ 'SELECT * FROM `nodes` NATURAL LEFT OUTER JOIN `nodes`'
305
296
  end
306
297
  specify "should support natural right outer joins" do
307
298
  @ds.join_table(:natural_right_outer, :nodes).sql.should == \
308
- 'SELECT * FROM nodes NATURAL RIGHT OUTER JOIN nodes'
299
+ 'SELECT * FROM `nodes` NATURAL RIGHT OUTER JOIN `nodes`'
309
300
  end
310
301
  specify "should support natural inner joins" do
311
302
  @ds.join_table(:natural_inner, :nodes).sql.should == \
312
- 'SELECT * FROM nodes NATURAL LEFT JOIN nodes'
303
+ 'SELECT * FROM `nodes` NATURAL LEFT JOIN `nodes`'
313
304
  end
314
305
  specify "should support cross joins" do
315
306
  @ds.join_table(:cross, :nodes).sql.should == \
316
- 'SELECT * FROM nodes CROSS JOIN nodes'
307
+ 'SELECT * FROM `nodes` CROSS JOIN `nodes`'
317
308
  end
318
309
  specify "should support cross joins as inner joins if conditions are used" do
319
310
  @ds.join_table(:cross, :nodes, :id=>:id).sql.should == \
320
- 'SELECT * FROM nodes INNER JOIN nodes ON (nodes.id = nodes.id)'
311
+ 'SELECT * FROM `nodes` INNER JOIN `nodes` ON (`nodes`.`id` = `nodes`.`id`)'
321
312
  end
322
313
  specify "should support straight joins (force left table to be read before right)" do
323
314
  @ds.join_table(:straight, :nodes).sql.should == \
324
- 'SELECT * FROM nodes STRAIGHT_JOIN nodes'
315
+ 'SELECT * FROM `nodes` STRAIGHT_JOIN `nodes`'
325
316
  end
326
317
  specify "should support natural joins on multiple tables." do
327
318
  @ds.join_table(:natural_left_outer, [:nodes, :branches]).sql.should == \
328
- 'SELECT * FROM nodes NATURAL LEFT OUTER JOIN (nodes, branches)'
319
+ 'SELECT * FROM `nodes` NATURAL LEFT OUTER JOIN (`nodes`, `branches`)'
329
320
  end
330
321
  specify "should support straight joins on multiple tables." do
331
322
  @ds.join_table(:straight, [:nodes,:branches]).sql.should == \
332
- 'SELECT * FROM nodes STRAIGHT_JOIN (nodes, branches)'
323
+ 'SELECT * FROM `nodes` STRAIGHT_JOIN (`nodes`, `branches`)'
333
324
  end
334
325
  end
335
326
 
@@ -337,46 +328,79 @@ describe "Joined MySQL dataset" do
337
328
  before do
338
329
  @ds = MYSQL_DB[:nodes]
339
330
  end
340
-
331
+
341
332
  specify "should quote fields correctly" do
342
333
  @ds.quote_identifiers = true
343
334
  @ds.join(:attributes, :node_id => :id).sql.should == \
344
335
  "SELECT * FROM `nodes` INNER JOIN `attributes` ON (`attributes`.`node_id` = `nodes`.`id`)"
345
336
  end
346
-
337
+
347
338
  specify "should allow a having clause on ungrouped datasets" do
348
339
  proc {@ds.having('blah')}.should_not raise_error
349
340
 
350
341
  @ds.having('blah').sql.should == \
351
- "SELECT * FROM nodes HAVING (blah)"
342
+ "SELECT * FROM `nodes` HAVING (blah)"
352
343
  end
353
-
344
+
354
345
  specify "should put a having clause before an order by clause" do
355
346
  @ds.order(:aaa).having(:bbb => :ccc).sql.should == \
356
- "SELECT * FROM nodes HAVING (bbb = ccc) ORDER BY aaa"
347
+ "SELECT * FROM `nodes` HAVING (`bbb` = `ccc`) ORDER BY `aaa`"
357
348
  end
358
349
  end
359
350
 
360
351
  describe "A MySQL database" do
361
- before do
352
+ after do
353
+ MYSQL_DB.drop_table?(:test_innodb)
354
+ end
355
+
356
+ specify "should handle the creation and dropping of an InnoDB table with foreign keys" do
357
+ proc{MYSQL_DB.create_table!(:test_innodb, :engine=>:InnoDB){primary_key :id; foreign_key :fk, :test_innodb, :key=>:id}}.should_not raise_error
358
+ end
359
+ end
360
+
361
+ describe "A MySQL database" do
362
+ before(:all) do
362
363
  @db = MYSQL_DB
364
+ @db.create_table! :test2 do
365
+ text :name
366
+ integer :value
367
+ end
368
+ end
369
+ after(:all) do
370
+ @db.drop_table?(:test2)
371
+ end
372
+
373
+ specify "should provide the server version" do
374
+ @db.server_version.should >= 40000
375
+ end
376
+
377
+ specify "should cache the server version" do
378
+ # warm cache:
379
+ @db.server_version
380
+ @db.sqls.clear
381
+ 3.times{@db.server_version}
382
+ @db.sqls.should be_empty
383
+ end
384
+
385
+ specify "should support for_share" do
386
+ @db.transaction{@db[:test2].for_share.all.should == []}
363
387
  end
364
388
 
365
389
  specify "should support add_column operations" do
366
390
  @db.add_column :test2, :xyz, :text
367
-
391
+
368
392
  @db[:test2].columns.should == [:name, :value, :xyz]
369
393
  @db[:test2] << {:name => 'mmm', :value => 111, :xyz => '000'}
370
394
  @db[:test2].first[:xyz].should == '000'
371
395
  end
372
-
396
+
373
397
  specify "should support drop_column operations" do
374
398
  @db[:test2].columns.should == [:name, :value, :xyz]
375
399
  @db.drop_column :test2, :xyz
376
-
400
+
377
401
  @db[:test2].columns.should == [:name, :value]
378
402
  end
379
-
403
+
380
404
  specify "should support rename_column operations" do
381
405
  @db[:test2].delete
382
406
  @db.add_column :test2, :xyz, :text
@@ -387,7 +411,7 @@ describe "A MySQL database" do
387
411
  @db[:test2].columns.should == [:name, :value, :zyx]
388
412
  @db[:test2].first[:zyx].should == 'qqqq'
389
413
  end
390
-
414
+
391
415
  specify "should support rename_column operations with types like varchar(255)" do
392
416
  @db[:test2].delete
393
417
  @db.add_column :test2, :tre, :text
@@ -398,26 +422,27 @@ describe "A MySQL database" do
398
422
  @db[:test2].columns.should == [:name, :value, :zyx, :ert]
399
423
  @db[:test2].first[:ert].should == 'qqqq'
400
424
  end
401
-
425
+
402
426
  specify "should support set_column_type operations" do
403
427
  @db.add_column :test2, :xyz, :float
404
428
  @db[:test2].delete
405
429
  @db[:test2] << {:name => 'mmm', :value => 111, :xyz => 56.78}
406
430
  @db.set_column_type :test2, :xyz, :integer
407
-
431
+
408
432
  @db[:test2].first[:xyz].should == 57
409
433
  end
410
-
434
+
411
435
  specify "should support add_index" do
412
436
  @db.add_index :test2, :value
413
437
  end
414
-
438
+
415
439
  specify "should support drop_index" do
416
440
  @db.drop_index :test2, :value
417
441
  end
418
-
442
+
419
443
  specify "should support add_foreign_key" do
420
444
  @db.alter_table :test2 do
445
+ add_index :value, :unique=>true
421
446
  add_foreign_key :value2, :test2, :key=>:value
422
447
  end
423
448
  @db[:test2].columns.should == [:name, :value, :zyx, :ert, :xyz, :value2]
@@ -427,79 +452,98 @@ end
427
452
  describe "A MySQL database with table options" do
428
453
  before do
429
454
  @options = {:engine=>'MyISAM', :charset=>'latin1', :collate => 'latin1_swedish_ci'}
430
-
455
+
431
456
  Sequel::MySQL.default_engine = 'InnoDB'
432
457
  Sequel::MySQL.default_charset = 'utf8'
433
458
  Sequel::MySQL.default_collate = 'utf8_general_ci'
434
-
459
+
435
460
  @db = MYSQL_DB
436
- @db.drop_table(:items) rescue nil
437
-
461
+ @db.drop_table?(:items)
462
+
438
463
  MYSQL_DB.sqls.clear
439
464
  end
440
465
  after do
441
- @db.drop_table(:items) rescue nil
466
+ @db.drop_table?(:items)
442
467
 
443
468
  Sequel::MySQL.default_engine = nil
444
469
  Sequel::MySQL.default_charset = nil
445
470
  Sequel::MySQL.default_collate = nil
446
471
  end
447
-
472
+
448
473
  specify "should allow to pass custom options (engine, charset, collate) for table creation" do
449
474
  @db.create_table(:items, @options){Integer :size; text :name}
450
- @db.sqls.should == ["CREATE TABLE items (size integer, name text) ENGINE=MyISAM DEFAULT CHARSET=latin1 DEFAULT COLLATE=latin1_swedish_ci"]
475
+ check_sqls do
476
+ @db.sqls.should == ["CREATE TABLE `items` (`size` integer, `name` text) ENGINE=MyISAM DEFAULT CHARSET=latin1 DEFAULT COLLATE=latin1_swedish_ci"]
477
+ end
451
478
  end
452
-
479
+
453
480
  specify "should use default options if specified (engine, charset, collate) for table creation" do
454
481
  @db.create_table(:items){Integer :size; text :name}
455
- @db.sqls.should == ["CREATE TABLE items (size integer, name text) ENGINE=InnoDB DEFAULT CHARSET=utf8 DEFAULT COLLATE=utf8_general_ci"]
482
+ check_sqls do
483
+ @db.sqls.should == ["CREATE TABLE `items` (`size` integer, `name` text) ENGINE=InnoDB DEFAULT CHARSET=utf8 DEFAULT COLLATE=utf8_general_ci"]
484
+ end
456
485
  end
457
-
486
+
458
487
  specify "should not use default if option has a nil value" do
459
488
  @db.create_table(:items, :engine=>nil, :charset=>nil, :collate=>nil){Integer :size; text :name}
460
- @db.sqls.should == ["CREATE TABLE items (size integer, name text)"]
489
+ check_sqls do
490
+ @db.sqls.should == ["CREATE TABLE `items` (`size` integer, `name` text)"]
491
+ end
461
492
  end
462
493
  end
463
494
 
464
495
  describe "A MySQL database" do
465
496
  before do
466
497
  @db = MYSQL_DB
467
- @db.drop_table(:items) rescue nil
498
+ @db.drop_table?(:items)
468
499
  MYSQL_DB.sqls.clear
469
500
  end
470
501
  after do
471
- @db.drop_table(:items) rescue nil
502
+ @db.drop_table?(:items, :users)
472
503
  end
473
-
504
+
474
505
  specify "should support defaults for boolean columns" do
475
506
  @db.create_table(:items){TrueClass :active1, :default=>true; FalseClass :active2, :default => false}
476
- @db.sqls.should == ["CREATE TABLE items (active1 tinyint(1) DEFAULT 1, active2 tinyint(1) DEFAULT 0)"]
507
+ check_sqls do
508
+ @db.sqls.should == ["CREATE TABLE `items` (`active1` tinyint(1) DEFAULT 1, `active2` tinyint(1) DEFAULT 0)"]
509
+ end
477
510
  end
478
-
511
+
479
512
  specify "should correctly format CREATE TABLE statements with foreign keys" do
480
- @db.create_table(:items){Integer :id; foreign_key :p_id, :items, :key => :id, :null => false, :on_delete => :cascade}
481
- @db.sqls.should == ["CREATE TABLE items (id integer, p_id integer NOT NULL, FOREIGN KEY (p_id) REFERENCES items(id) ON DELETE CASCADE)"]
513
+ @db.create_table(:items){primary_key :id; foreign_key :p_id, :items, :key => :id, :null => false, :on_delete => :cascade}
514
+ check_sqls do
515
+ @db.sqls.should == ["CREATE TABLE `items` (`id` integer PRIMARY KEY AUTO_INCREMENT, `p_id` integer NOT NULL, UNIQUE (`id`), FOREIGN KEY (`p_id`) REFERENCES `items`(`id`) ON DELETE CASCADE)"]
516
+ end
482
517
  end
483
-
518
+
484
519
  specify "should correctly format ALTER TABLE statements with foreign keys" do
485
520
  @db.create_table(:items){Integer :id}
521
+ @db.create_table(:users){primary_key :id}
486
522
  @db.alter_table(:items){add_foreign_key :p_id, :users, :key => :id, :null => false, :on_delete => :cascade}
487
- @db.sqls.should == ["CREATE TABLE items (id integer)", "ALTER TABLE items ADD COLUMN p_id integer NOT NULL", "ALTER TABLE items ADD FOREIGN KEY (p_id) REFERENCES users(id) ON DELETE CASCADE"]
523
+ check_sqls do
524
+ @db.sqls.should == ["CREATE TABLE `items` (`id` integer)",
525
+ "CREATE TABLE `users` (`id` integer PRIMARY KEY AUTO_INCREMENT)",
526
+ "ALTER TABLE `items` ADD COLUMN `p_id` integer NOT NULL, ADD FOREIGN KEY (`p_id`) REFERENCES `users`(`id`) ON DELETE CASCADE"]
527
+ end
488
528
  end
489
-
529
+
490
530
  specify "should have rename_column support keep existing options" do
491
531
  @db.create_table(:items){String :id, :null=>false, :default=>'blah'}
492
532
  @db.alter_table(:items){rename_column :id, :nid}
493
- @db.sqls.should == ["CREATE TABLE items (id varchar(255) NOT NULL DEFAULT 'blah')", "DESCRIBE items", "ALTER TABLE items CHANGE COLUMN id nid varchar(255) NOT NULL DEFAULT 'blah'"]
533
+ check_sqls do
534
+ @db.sqls.should == ["CREATE TABLE `items` (`id` varchar(255) NOT NULL DEFAULT 'blah')", "DESCRIBE `items`", "ALTER TABLE `items` CHANGE COLUMN `id` `nid` varchar(255) NOT NULL DEFAULT 'blah'"]
535
+ end
494
536
  @db[:items].insert
495
537
  @db[:items].all.should == [{:nid=>'blah'}]
496
538
  proc{@db[:items].insert(:nid=>nil)}.should raise_error(Sequel::DatabaseError)
497
539
  end
498
-
540
+
499
541
  specify "should have set_column_type support keep existing options" do
500
542
  @db.create_table(:items){Integer :id, :null=>false, :default=>5}
501
543
  @db.alter_table(:items){set_column_type :id, Bignum}
502
- @db.sqls.should == ["CREATE TABLE items (id integer NOT NULL DEFAULT 5)", "DESCRIBE items", "ALTER TABLE items CHANGE COLUMN id id bigint NOT NULL DEFAULT 5"]
544
+ check_sqls do
545
+ @db.sqls.should == ["CREATE TABLE `items` (`id` integer NOT NULL DEFAULT 5)", "DESCRIBE `items`", "ALTER TABLE `items` CHANGE COLUMN `id` `id` bigint NOT NULL DEFAULT 5"]
546
+ end
503
547
  @db[:items].insert
504
548
  @db[:items].all.should == [{:id=>5}]
505
549
  proc{@db[:items].insert(:id=>nil)}.should raise_error(Sequel::DatabaseError)
@@ -511,35 +555,41 @@ describe "A MySQL database" do
511
555
  specify "should have set_column_type pass through options" do
512
556
  @db.create_table(:items){integer :id; enum :list, :elements=>%w[one]}
513
557
  @db.alter_table(:items){set_column_type :id, :int, :unsigned=>true, :size=>8; set_column_type :list, :enum, :elements=>%w[two]}
514
- @db.sqls.should == ["CREATE TABLE items (id integer, list enum('one'))", "DESCRIBE items", "ALTER TABLE items CHANGE COLUMN id id int(8) UNSIGNED NULL", "ALTER TABLE items CHANGE COLUMN list list enum('two') NULL"]
558
+ check_sqls do
559
+ @db.sqls.should == ["CREATE TABLE `items` (`id` integer, `list` enum('one'))", "DESCRIBE `items`", "ALTER TABLE `items` CHANGE COLUMN `id` `id` int(8) UNSIGNED NULL, CHANGE COLUMN `list` `list` enum('two') NULL"]
560
+ end
515
561
  end
516
-
562
+
517
563
  specify "should have set_column_default support keep existing options" do
518
564
  @db.create_table(:items){Integer :id, :null=>false, :default=>5}
519
565
  @db.alter_table(:items){set_column_default :id, 6}
520
- @db.sqls.should == ["CREATE TABLE items (id integer NOT NULL DEFAULT 5)", "DESCRIBE items", "ALTER TABLE items CHANGE COLUMN id id int(11) NOT NULL DEFAULT 6"]
566
+ check_sqls do
567
+ @db.sqls.should == ["CREATE TABLE `items` (`id` integer NOT NULL DEFAULT 5)", "DESCRIBE `items`", "ALTER TABLE `items` CHANGE COLUMN `id` `id` int(11) NOT NULL DEFAULT 6"]
568
+ end
521
569
  @db[:items].insert
522
570
  @db[:items].all.should == [{:id=>6}]
523
571
  proc{@db[:items].insert(:id=>nil)}.should raise_error(Sequel::DatabaseError)
524
572
  end
525
-
573
+
526
574
  specify "should have set_column_allow_null support keep existing options" do
527
575
  @db.create_table(:items){Integer :id, :null=>false, :default=>5}
528
576
  @db.alter_table(:items){set_column_allow_null :id, true}
529
- @db.sqls.should == ["CREATE TABLE items (id integer NOT NULL DEFAULT 5)", "DESCRIBE items", "ALTER TABLE items CHANGE COLUMN id id int(11) NULL DEFAULT 5"]
577
+ check_sqls do
578
+ @db.sqls.should == ["CREATE TABLE `items` (`id` integer NOT NULL DEFAULT 5)", "DESCRIBE `items`", "ALTER TABLE `items` CHANGE COLUMN `id` `id` int(11) NULL DEFAULT 5"]
579
+ end
530
580
  @db[:items].insert
531
581
  @db[:items].all.should == [{:id=>5}]
532
582
  proc{@db[:items].insert(:id=>nil)}.should_not
533
583
  end
534
-
584
+
535
585
  specify "should accept repeated raw sql statements using Database#<<" do
536
586
  @db.create_table(:items){String :name; Integer :value}
537
587
  @db << 'DELETE FROM items'
538
588
  @db[:items].count.should == 0
539
-
589
+
540
590
  @db << "INSERT INTO items (name, value) VALUES ('tutu', 1234)"
541
591
  @db[:items].first.should == {:name => 'tutu', :value => 1234}
542
-
592
+
543
593
  @db << 'DELETE FROM items'
544
594
  @db[:items].first.should == nil
545
595
  end
@@ -552,12 +602,12 @@ if %w'localhost 127.0.0.1 ::1'.include?(MYSQL_URI.host) and MYSQL_DB.adapter_sch
552
602
  db = Sequel.mysql(MYSQL_DB.opts[:database], :host => 'localhost', :user => MYSQL_DB.opts[:user], :password => MYSQL_DB.opts[:password], :socket => MYSQL_SOCKET_FILE)
553
603
  proc {db.test_connection}.should_not raise_error
554
604
  end
555
-
605
+
556
606
  specify "should accept a socket option without host option" do
557
607
  db = Sequel.mysql(MYSQL_DB.opts[:database], :user => MYSQL_DB.opts[:user], :password => MYSQL_DB.opts[:password], :socket => MYSQL_SOCKET_FILE)
558
608
  proc {db.test_connection}.should_not raise_error
559
609
  end
560
-
610
+
561
611
  specify "should fail to connect with invalid socket" do
562
612
  db = Sequel.mysql(MYSQL_DB.opts[:database], :user => MYSQL_DB.opts[:user], :password => MYSQL_DB.opts[:password], :socket =>'blah')
563
613
  proc {db.test_connection}.should raise_error
@@ -577,9 +627,58 @@ describe "A MySQL database" do
577
627
  end
578
628
  end
579
629
 
630
+ describe "MySQL foreign key support" do
631
+ after do
632
+ MYSQL_DB.drop_table?(:testfk, :testpk)
633
+ end
634
+
635
+ specify "should create table without :key" do
636
+ MYSQL_DB.create_table!(:testpk){primary_key :id}
637
+ MYSQL_DB.create_table!(:testfk){foreign_key :fk, :testpk}
638
+ end
639
+
640
+ specify "should create table with composite keys without :key" do
641
+ MYSQL_DB.create_table!(:testpk){Integer :id; Integer :id2; primary_key([:id, :id2])}
642
+ MYSQL_DB.create_table!(:testfk){Integer :fk; Integer :fk2; foreign_key([:fk, :fk2], :testpk)}
643
+ end
644
+
645
+ specify "should create table with self referential without :key" do
646
+ MYSQL_DB.create_table!(:testfk){primary_key :id; foreign_key :fk, :testfk}
647
+ end
648
+
649
+ specify "should create table with self referential with composite keys without :key" do
650
+ MYSQL_DB.create_table!(:testfk){Integer :id; Integer :id2; Integer :fk; Integer :fk2; primary_key([:id, :id2]); foreign_key([:fk, :fk2], :testfk)}
651
+ end
652
+
653
+ specify "should alter table without :key" do
654
+ MYSQL_DB.create_table!(:testpk){primary_key :id}
655
+ MYSQL_DB.create_table!(:testfk){Integer :id}
656
+ MYSQL_DB.alter_table(:testfk){add_foreign_key :fk, :testpk}
657
+ end
658
+
659
+ specify "should alter table with composite keys without :key" do
660
+ MYSQL_DB.create_table!(:testpk){Integer :id; Integer :id2; primary_key([:id, :id2])}
661
+ MYSQL_DB.create_table!(:testfk){Integer :fk; Integer :fk2}
662
+ MYSQL_DB.alter_table(:testfk){add_foreign_key([:fk, :fk2], :testpk)}
663
+ end
664
+
665
+ specify "should alter table with self referential without :key" do
666
+ MYSQL_DB.create_table!(:testfk){primary_key :id}
667
+ MYSQL_DB.alter_table(:testfk){add_foreign_key :fk, :testfk}
668
+ end
669
+
670
+ specify "should alter table with self referential with composite keys without :key" do
671
+ MYSQL_DB.create_table!(:testfk){Integer :id; Integer :id2; Integer :fk; Integer :fk2; primary_key([:id, :id2])}
672
+ MYSQL_DB.alter_table(:testfk){add_foreign_key [:fk, :fk2], :testfk}
673
+ end
674
+ end
675
+
580
676
  describe "A grouped MySQL dataset" do
581
677
  before do
582
- MYSQL_DB[:test2].delete
678
+ MYSQL_DB.create_table! :test2 do
679
+ text :name
680
+ integer :value
681
+ end
583
682
  MYSQL_DB[:test2] << {:name => '11', :value => 10}
584
683
  MYSQL_DB[:test2] << {:name => '11', :value => 20}
585
684
  MYSQL_DB[:test2] << {:name => '11', :value => 30}
@@ -587,12 +686,15 @@ describe "A grouped MySQL dataset" do
587
686
  MYSQL_DB[:test2] << {:name => '12', :value => 20}
588
687
  MYSQL_DB[:test2] << {:name => '13', :value => 10}
589
688
  end
590
-
689
+ after do
690
+ MYSQL_DB.drop_table?(:test2)
691
+ end
692
+
591
693
  specify "should return the correct count for raw sql query" do
592
694
  ds = MYSQL_DB["select name FROM test2 WHERE name = '11' GROUP BY name"]
593
695
  ds.count.should == 1
594
696
  end
595
-
697
+
596
698
  specify "should return the correct count for a normal dataset" do
597
699
  ds = MYSQL_DB[:test2].select(:name).where(:name => '11').group(:name)
598
700
  ds.count.should == 1
@@ -602,21 +704,23 @@ end
602
704
  describe "A MySQL database" do
603
705
  before do
604
706
  @db = MYSQL_DB
605
- @db.drop_table(:posts) rescue nil
707
+ @db.drop_table?(:posts)
606
708
  @db.sqls.clear
607
709
  end
608
710
  after do
609
- @db.drop_table(:posts) rescue nil
711
+ @db.drop_table?(:posts)
610
712
  end
611
-
713
+
612
714
  specify "should support fulltext indexes and full_text_search" do
613
- @db.create_table(:posts){text :title; text :body; full_text_index :title; full_text_index [:title, :body]}
614
- @db.sqls.should == [
615
- "CREATE TABLE posts (title text, body text)",
616
- "CREATE FULLTEXT INDEX posts_title_index ON posts (title)",
617
- "CREATE FULLTEXT INDEX posts_title_body_index ON posts (title, body)"
618
- ]
619
-
715
+ @db.create_table(:posts, :engine=>:MyISAM){text :title; text :body; full_text_index :title; full_text_index [:title, :body]}
716
+ check_sqls do
717
+ @db.sqls.should == [
718
+ "CREATE TABLE `posts` (`title` text, `body` text) ENGINE=MyISAM",
719
+ "CREATE FULLTEXT INDEX `posts_title_index` ON `posts` (`title`)",
720
+ "CREATE FULLTEXT INDEX `posts_title_body_index` ON `posts` (`title`, `body`)"
721
+ ]
722
+ end
723
+
620
724
  @db[:posts].insert(:title=>'ruby rails', :body=>'y')
621
725
  @db[:posts].insert(:title=>'sequel', :body=>'ruby')
622
726
  @db[:posts].insert(:title=>'ruby scooby', :body=>'x')
@@ -625,34 +729,45 @@ describe "A MySQL database" do
625
729
  @db[:posts].full_text_search(:title, 'rails').all.should == [{:title=>'ruby rails', :body=>'y'}]
626
730
  @db[:posts].full_text_search([:title, :body], ['sequel', 'ruby']).all.should == [{:title=>'sequel', :body=>'ruby'}]
627
731
  @db[:posts].full_text_search(:title, '+ruby -rails', :boolean => true).all.should == [{:title=>'ruby scooby', :body=>'x'}]
628
- @db.sqls.should == [
629
- "SELECT * FROM posts WHERE (MATCH (title) AGAINST ('rails'))",
630
- "SELECT * FROM posts WHERE (MATCH (title, body) AGAINST ('sequel ruby'))",
631
- "SELECT * FROM posts WHERE (MATCH (title) AGAINST ('+ruby -rails' IN BOOLEAN MODE))"]
732
+ check_sqls do
733
+ @db.sqls.should == [
734
+ "SELECT * FROM `posts` WHERE (MATCH (`title`) AGAINST ('rails'))",
735
+ "SELECT * FROM `posts` WHERE (MATCH (`title`, `body`) AGAINST ('sequel ruby'))",
736
+ "SELECT * FROM `posts` WHERE (MATCH (`title`) AGAINST ('+ruby -rails' IN BOOLEAN MODE))"]
737
+ end
738
+
739
+ @db[:posts].full_text_search(:title, :$n).call(:select, :n=>'rails').should == [{:title=>'ruby rails', :body=>'y'}]
740
+ @db[:posts].full_text_search(:title, :$n).prepare(:select, :fts_select).call(:n=>'rails').should == [{:title=>'ruby rails', :body=>'y'}]
632
741
  end
633
742
 
634
743
  specify "should support spatial indexes" do
635
- @db.create_table(:posts){point :geom, :null=>false; spatial_index [:geom]}
636
- @db.sqls.should == [
637
- "CREATE TABLE posts (geom point NOT NULL)",
638
- "CREATE SPATIAL INDEX posts_geom_index ON posts (geom)"
639
- ]
744
+ @db.create_table(:posts, :engine=>:MyISAM){point :geom, :null=>false; spatial_index [:geom]}
745
+ check_sqls do
746
+ @db.sqls.should == [
747
+ "CREATE TABLE `posts` (`geom` point NOT NULL) ENGINE=MyISAM",
748
+ "CREATE SPATIAL INDEX `posts_geom_index` ON `posts` (`geom`)"
749
+ ]
750
+ end
640
751
  end
641
752
 
642
753
  specify "should support indexes with index type" do
643
754
  @db.create_table(:posts){Integer :id; index :id, :type => :btree}
644
- @db.sqls.should == [
645
- "CREATE TABLE posts (id integer)",
646
- "CREATE INDEX posts_id_index USING btree ON posts (id)"
647
- ]
755
+ check_sqls do
756
+ @db.sqls.should == [
757
+ "CREATE TABLE `posts` (`id` integer)",
758
+ "CREATE INDEX `posts_id_index` USING btree ON `posts` (`id`)"
759
+ ]
760
+ end
648
761
  end
649
762
 
650
763
  specify "should support unique indexes with index type" do
651
764
  @db.create_table(:posts){Integer :id; index :id, :type => :btree, :unique => true}
652
- @db.sqls.should == [
653
- "CREATE TABLE posts (id integer)",
654
- "CREATE UNIQUE INDEX posts_id_index USING btree ON posts (id)"
655
- ]
765
+ check_sqls do
766
+ @db.sqls.should == [
767
+ "CREATE TABLE `posts` (`id` integer)",
768
+ "CREATE UNIQUE INDEX `posts_id_index` USING btree ON `posts` (`id`)"
769
+ ]
770
+ end
656
771
  end
657
772
 
658
773
  specify "should not dump partial indexes" do
@@ -675,43 +790,31 @@ describe "MySQL::Dataset#insert and related methods" do
675
790
  MYSQL_DB.sqls.clear
676
791
  end
677
792
  after do
678
- MYSQL_DB.drop_table(:items)
793
+ MYSQL_DB.drop_table?(:items)
679
794
  end
680
795
 
681
796
  specify "#insert should insert record with default values when no arguments given" do
682
797
  @d.insert
683
-
684
- MYSQL_DB.sqls.should == [
685
- "INSERT INTO items () VALUES ()"
686
- ]
687
-
688
- @d.all.should == [
689
- {:name => nil, :value => nil}
690
- ]
798
+ check_sqls do
799
+ MYSQL_DB.sqls.should == ["INSERT INTO `items` () VALUES ()"]
800
+ end
801
+ @d.all.should == [{:name => nil, :value => nil}]
691
802
  end
692
803
 
693
804
  specify "#insert should insert record with default values when empty hash given" do
694
805
  @d.insert({})
695
-
696
- MYSQL_DB.sqls.should == [
697
- "INSERT INTO items () VALUES ()"
698
- ]
699
-
700
- @d.all.should == [
701
- {:name => nil, :value => nil}
702
- ]
806
+ check_sqls do
807
+ MYSQL_DB.sqls.should == ["INSERT INTO `items` () VALUES ()"]
808
+ end
809
+ @d.all.should == [{:name => nil, :value => nil}]
703
810
  end
704
811
 
705
812
  specify "#insert should insert record with default values when empty array given" do
706
813
  @d.insert []
707
-
708
- MYSQL_DB.sqls.should == [
709
- "INSERT INTO items () VALUES ()"
710
- ]
711
-
712
- @d.all.should == [
713
- {:name => nil, :value => nil}
714
- ]
814
+ check_sqls do
815
+ MYSQL_DB.sqls.should == ["INSERT INTO `items` () VALUES ()"]
816
+ end
817
+ @d.all.should == [{:name => nil, :value => nil}]
715
818
  end
716
819
 
717
820
  specify "#on_duplicate_key_update should work with regular inserts" do
@@ -721,22 +824,88 @@ describe "MySQL::Dataset#insert and related methods" do
721
824
  @d.on_duplicate_key_update(:name, :value => 6).insert(:name => 'abc', :value => 1)
722
825
  @d.on_duplicate_key_update(:name, :value => 6).insert(:name => 'def', :value => 2)
723
826
 
724
- MYSQL_DB.sqls.length.should == 3
725
- MYSQL_DB.sqls[0].should =~ /\AINSERT INTO items \((name|value), (name|value)\) VALUES \(('abc'|1), (1|'abc')\)\z/
726
- MYSQL_DB.sqls[1].should =~ /\AINSERT INTO items \((name|value), (name|value)\) VALUES \(('abc'|1), (1|'abc')\) ON DUPLICATE KEY UPDATE name=VALUES\(name\), value=6\z/
727
- MYSQL_DB.sqls[2].should =~ /\AINSERT INTO items \((name|value), (name|value)\) VALUES \(('def'|2), (2|'def')\) ON DUPLICATE KEY UPDATE name=VALUES\(name\), value=6\z/
827
+ check_sqls do
828
+ MYSQL_DB.sqls.length.should == 3
829
+ MYSQL_DB.sqls[0].should =~ /\AINSERT INTO `items` \(`(name|value)`, `(name|value)`\) VALUES \(('abc'|1), (1|'abc')\)\z/
830
+ MYSQL_DB.sqls[1].should =~ /\AINSERT INTO `items` \(`(name|value)`, `(name|value)`\) VALUES \(('abc'|1), (1|'abc')\) ON DUPLICATE KEY UPDATE `name`=VALUES\(`name`\), `value`=6\z/
831
+ MYSQL_DB.sqls[2].should =~ /\AINSERT INTO `items` \(`(name|value)`, `(name|value)`\) VALUES \(('def'|2), (2|'def')\) ON DUPLICATE KEY UPDATE `name`=VALUES\(`name`\), `value`=6\z/
832
+ end
728
833
 
729
834
  @d.all.should == [{:name => 'abc', :value => 6}, {:name => 'def', :value => 2}]
730
835
  end
731
836
 
837
+ specify "#multi_replace should insert multiple records in a single statement" do
838
+ @d.multi_replace([{:name => 'abc'}, {:name => 'def'}])
839
+
840
+ check_sqls do
841
+ MYSQL_DB.sqls.should == [
842
+ SQL_BEGIN,
843
+ "REPLACE INTO `items` (`name`) VALUES ('abc'), ('def')",
844
+ SQL_COMMIT
845
+ ]
846
+ end
847
+
848
+ @d.all.should == [
849
+ {:name => 'abc', :value => nil}, {:name => 'def', :value => nil}
850
+ ]
851
+ end
852
+
853
+ specify "#multi_replace should split the list of records into batches if :commit_every option is given" do
854
+ @d.multi_replace([{:value => 1}, {:value => 2}, {:value => 3}, {:value => 4}],
855
+ :commit_every => 2)
856
+
857
+ check_sqls do
858
+ MYSQL_DB.sqls.should == [
859
+ SQL_BEGIN,
860
+ "REPLACE INTO `items` (`value`) VALUES (1), (2)",
861
+ SQL_COMMIT,
862
+ SQL_BEGIN,
863
+ "REPLACE INTO `items` (`value`) VALUES (3), (4)",
864
+ SQL_COMMIT
865
+ ]
866
+ end
867
+
868
+ @d.all.should == [
869
+ {:name => nil, :value => 1},
870
+ {:name => nil, :value => 2},
871
+ {:name => nil, :value => 3},
872
+ {:name => nil, :value => 4}
873
+ ]
874
+ end
875
+
876
+ specify "#multi_replace should split the list of records into batches if :slice option is given" do
877
+ @d.multi_replace([{:value => 1}, {:value => 2}, {:value => 3}, {:value => 4}],
878
+ :slice => 2)
879
+
880
+ check_sqls do
881
+ MYSQL_DB.sqls.should == [
882
+ SQL_BEGIN,
883
+ "REPLACE INTO `items` (`value`) VALUES (1), (2)",
884
+ SQL_COMMIT,
885
+ SQL_BEGIN,
886
+ "REPLACE INTO `items` (`value`) VALUES (3), (4)",
887
+ SQL_COMMIT
888
+ ]
889
+ end
890
+
891
+ @d.all.should == [
892
+ {:name => nil, :value => 1},
893
+ {:name => nil, :value => 2},
894
+ {:name => nil, :value => 3},
895
+ {:name => nil, :value => 4}
896
+ ]
897
+ end
898
+
732
899
  specify "#multi_insert should insert multiple records in a single statement" do
733
900
  @d.multi_insert([{:name => 'abc'}, {:name => 'def'}])
734
-
735
- MYSQL_DB.sqls.should == [
736
- SQL_BEGIN,
737
- "INSERT INTO items (name) VALUES ('abc'), ('def')",
738
- SQL_COMMIT
739
- ]
901
+
902
+ check_sqls do
903
+ MYSQL_DB.sqls.should == [
904
+ SQL_BEGIN,
905
+ "INSERT INTO `items` (`name`) VALUES ('abc'), ('def')",
906
+ SQL_COMMIT
907
+ ]
908
+ end
740
909
 
741
910
  @d.all.should == [
742
911
  {:name => 'abc', :value => nil}, {:name => 'def', :value => nil}
@@ -747,15 +916,17 @@ describe "MySQL::Dataset#insert and related methods" do
747
916
  @d.multi_insert([{:value => 1}, {:value => 2}, {:value => 3}, {:value => 4}],
748
917
  :commit_every => 2)
749
918
 
750
- MYSQL_DB.sqls.should == [
751
- SQL_BEGIN,
752
- "INSERT INTO items (value) VALUES (1), (2)",
753
- SQL_COMMIT,
754
- SQL_BEGIN,
755
- "INSERT INTO items (value) VALUES (3), (4)",
756
- SQL_COMMIT
757
- ]
758
-
919
+ check_sqls do
920
+ MYSQL_DB.sqls.should == [
921
+ SQL_BEGIN,
922
+ "INSERT INTO `items` (`value`) VALUES (1), (2)",
923
+ SQL_COMMIT,
924
+ SQL_BEGIN,
925
+ "INSERT INTO `items` (`value`) VALUES (3), (4)",
926
+ SQL_COMMIT
927
+ ]
928
+ end
929
+
759
930
  @d.all.should == [
760
931
  {:name => nil, :value => 1},
761
932
  {:name => nil, :value => 2},
@@ -768,15 +939,17 @@ describe "MySQL::Dataset#insert and related methods" do
768
939
  @d.multi_insert([{:value => 1}, {:value => 2}, {:value => 3}, {:value => 4}],
769
940
  :slice => 2)
770
941
 
771
- MYSQL_DB.sqls.should == [
772
- SQL_BEGIN,
773
- "INSERT INTO items (value) VALUES (1), (2)",
774
- SQL_COMMIT,
775
- SQL_BEGIN,
776
- "INSERT INTO items (value) VALUES (3), (4)",
777
- SQL_COMMIT
778
- ]
779
-
942
+ check_sqls do
943
+ MYSQL_DB.sqls.should == [
944
+ SQL_BEGIN,
945
+ "INSERT INTO `items` (`value`) VALUES (1), (2)",
946
+ SQL_COMMIT,
947
+ SQL_BEGIN,
948
+ "INSERT INTO `items` (`value`) VALUES (3), (4)",
949
+ SQL_COMMIT
950
+ ]
951
+ end
952
+
780
953
  @d.all.should == [
781
954
  {:name => nil, :value => 1},
782
955
  {:name => nil, :value => 2},
@@ -784,73 +957,105 @@ describe "MySQL::Dataset#insert and related methods" do
784
957
  {:name => nil, :value => 4}
785
958
  ]
786
959
  end
787
-
960
+
788
961
  specify "#import should support inserting using columns and values arrays" do
789
962
  @d.import([:name, :value], [['abc', 1], ['def', 2]])
790
963
 
791
- MYSQL_DB.sqls.should == [
792
- SQL_BEGIN,
793
- "INSERT INTO items (name, value) VALUES ('abc', 1), ('def', 2)",
794
- SQL_COMMIT
795
- ]
796
-
964
+ check_sqls do
965
+ MYSQL_DB.sqls.should == [
966
+ SQL_BEGIN,
967
+ "INSERT INTO `items` (`name`, `value`) VALUES ('abc', 1), ('def', 2)",
968
+ SQL_COMMIT
969
+ ]
970
+ end
971
+
797
972
  @d.all.should == [
798
973
  {:name => 'abc', :value => 1},
799
974
  {:name => 'def', :value => 2}
800
975
  ]
801
976
  end
802
-
977
+
803
978
  specify "#insert_ignore should add the IGNORE keyword when inserting" do
804
979
  @d.insert_ignore.multi_insert([{:name => 'abc'}, {:name => 'def'}])
805
-
806
- MYSQL_DB.sqls.should == [
807
- SQL_BEGIN,
808
- "INSERT IGNORE INTO items (name) VALUES ('abc'), ('def')",
809
- SQL_COMMIT
810
- ]
980
+
981
+ check_sqls do
982
+ MYSQL_DB.sqls.should == [
983
+ SQL_BEGIN,
984
+ "INSERT IGNORE INTO `items` (`name`) VALUES ('abc'), ('def')",
985
+ SQL_COMMIT
986
+ ]
987
+ end
811
988
 
812
989
  @d.all.should == [
813
990
  {:name => 'abc', :value => nil}, {:name => 'def', :value => nil}
814
991
  ]
815
992
  end
816
-
993
+
817
994
  specify "#insert_ignore should add the IGNORE keyword for single inserts" do
818
995
  @d.insert_ignore.insert(:name => 'ghi')
819
- MYSQL_DB.sqls.should == ["INSERT IGNORE INTO items (name) VALUES ('ghi')"]
996
+ check_sqls do
997
+ MYSQL_DB.sqls.should == ["INSERT IGNORE INTO `items` (`name`) VALUES ('ghi')"]
998
+ end
820
999
  @d.all.should == [{:name => 'ghi', :value => nil}]
821
1000
  end
822
-
1001
+
823
1002
  specify "#on_duplicate_key_update should add the ON DUPLICATE KEY UPDATE and ALL columns when no args given" do
824
1003
  @d.on_duplicate_key_update.import([:name,:value], [['abc', 1], ['def',2]])
825
-
826
- MYSQL_DB.sqls.should == [
827
- "SELECT * FROM items LIMIT 1",
828
- SQL_BEGIN,
829
- "INSERT INTO items (name, value) VALUES ('abc', 1), ('def', 2) ON DUPLICATE KEY UPDATE name=VALUES(name), value=VALUES(value)",
830
- SQL_COMMIT
831
- ]
1004
+
1005
+ check_sqls do
1006
+ MYSQL_DB.sqls.should == [
1007
+ "SELECT * FROM `items` LIMIT 1",
1008
+ SQL_BEGIN,
1009
+ "INSERT INTO `items` (`name`, `value`) VALUES ('abc', 1), ('def', 2) ON DUPLICATE KEY UPDATE `name`=VALUES(`name`), `value`=VALUES(`value`)",
1010
+ SQL_COMMIT
1011
+ ]
1012
+ end
832
1013
 
833
1014
  @d.all.should == [
834
1015
  {:name => 'abc', :value => 1}, {:name => 'def', :value => 2}
835
1016
  ]
836
1017
  end
837
-
1018
+
838
1019
  specify "#on_duplicate_key_update should add the ON DUPLICATE KEY UPDATE and columns specified when args are given" do
839
1020
  @d.on_duplicate_key_update(:value).import([:name,:value],
840
1021
  [['abc', 1], ['def',2]]
841
1022
  )
842
-
843
- MYSQL_DB.sqls.should == [
844
- SQL_BEGIN,
845
- "INSERT INTO items (name, value) VALUES ('abc', 1), ('def', 2) ON DUPLICATE KEY UPDATE value=VALUES(value)",
846
- SQL_COMMIT
847
- ]
1023
+
1024
+ check_sqls do
1025
+ MYSQL_DB.sqls.should == [
1026
+ SQL_BEGIN,
1027
+ "INSERT INTO `items` (`name`, `value`) VALUES ('abc', 1), ('def', 2) ON DUPLICATE KEY UPDATE `value`=VALUES(`value`)",
1028
+ SQL_COMMIT
1029
+ ]
1030
+ end
848
1031
 
849
1032
  @d.all.should == [
850
1033
  {:name => 'abc', :value => 1}, {:name => 'def', :value => 2}
851
1034
  ]
852
1035
  end
853
-
1036
+
1037
+ end
1038
+
1039
+ describe "MySQL::Dataset#update and related methods" do
1040
+ before do
1041
+ MYSQL_DB.create_table(:items){String :name; Integer :value; index :name, :unique=>true}
1042
+ @d = MYSQL_DB[:items]
1043
+ end
1044
+ after do
1045
+ MYSQL_DB.drop_table?(:items)
1046
+ end
1047
+
1048
+ specify "#update_ignore should not raise error where normal update would fail" do
1049
+ @d.insert(:name => 'cow', :value => 0)
1050
+ @d.insert(:name => 'cat', :value => 1)
1051
+ proc{@d.where(:value => 1).update(:name => 'cow')}.should raise_error(Sequel::DatabaseError)
1052
+ MYSQL_DB.sqls.clear
1053
+ @d.update_ignore.where(:value => 1).update(:name => 'cow')
1054
+ check_sqls do
1055
+ MYSQL_DB.sqls.should == ["UPDATE IGNORE `items` SET `name` = 'cow' WHERE (`value` = 1)"]
1056
+ end
1057
+ @d.order(:name).all.should == [{:name => 'cat', :value => 1}, {:name => 'cow', :value => 0}]
1058
+ end
854
1059
  end
855
1060
 
856
1061
  describe "MySQL::Dataset#replace" do
@@ -860,9 +1065,9 @@ describe "MySQL::Dataset#replace" do
860
1065
  MYSQL_DB.sqls.clear
861
1066
  end
862
1067
  after do
863
- MYSQL_DB.drop_table(:items)
1068
+ MYSQL_DB.drop_table?(:items)
864
1069
  end
865
-
1070
+
866
1071
  specify "should use default values if they exist" do
867
1072
  MYSQL_DB.alter_table(:items){set_column_default :id, 1; set_column_default :value, 2}
868
1073
  @d.replace
@@ -872,7 +1077,7 @@ describe "MySQL::Dataset#replace" do
872
1077
  @d.replace({})
873
1078
  @d.all.should == [{:id=>1, :value=>2}]
874
1079
  end
875
-
1080
+
876
1081
  specify "should use support arrays, datasets, and multiple values" do
877
1082
  @d.replace([1, 2])
878
1083
  @d.all.should == [{:id=>1, :value=>2}]
@@ -881,12 +1086,12 @@ describe "MySQL::Dataset#replace" do
881
1086
  @d.replace(@d)
882
1087
  @d.all.should == [{:id=>1, :value=>2}]
883
1088
  end
884
-
1089
+
885
1090
  specify "should create a record if the condition is not met" do
886
1091
  @d.replace(:id => 111, :value => 333)
887
1092
  @d.all.should == [{:id => 111, :value => 333}]
888
1093
  end
889
-
1094
+
890
1095
  specify "should update a record if the condition is met" do
891
1096
  @d << {:id => 111}
892
1097
  @d.all.should == [{:id => 111, :value => nil}]
@@ -901,25 +1106,25 @@ describe "MySQL::Dataset#complex_expression_sql" do
901
1106
  end
902
1107
 
903
1108
  specify "should handle pattern matches correctly" do
904
- @d.literal(:x.like('a')).should == "(x LIKE BINARY 'a')"
905
- @d.literal(~:x.like('a')).should == "(x NOT LIKE BINARY 'a')"
906
- @d.literal(:x.ilike('a')).should == "(x LIKE 'a')"
907
- @d.literal(~:x.ilike('a')).should == "(x NOT LIKE 'a')"
908
- @d.literal(:x.like(/a/)).should == "(x REGEXP BINARY 'a')"
909
- @d.literal(~:x.like(/a/)).should == "(x NOT REGEXP BINARY 'a')"
910
- @d.literal(:x.like(/a/i)).should == "(x REGEXP 'a')"
911
- @d.literal(~:x.like(/a/i)).should == "(x NOT REGEXP 'a')"
1109
+ @d.literal(Sequel.expr(:x).like('a')).should == "(`x` LIKE BINARY 'a')"
1110
+ @d.literal(~Sequel.expr(:x).like('a')).should == "(`x` NOT LIKE BINARY 'a')"
1111
+ @d.literal(Sequel.expr(:x).ilike('a')).should == "(`x` LIKE 'a')"
1112
+ @d.literal(~Sequel.expr(:x).ilike('a')).should == "(`x` NOT LIKE 'a')"
1113
+ @d.literal(Sequel.expr(:x).like(/a/)).should == "(`x` REGEXP BINARY 'a')"
1114
+ @d.literal(~Sequel.expr(:x).like(/a/)).should == "(`x` NOT REGEXP BINARY 'a')"
1115
+ @d.literal(Sequel.expr(:x).like(/a/i)).should == "(`x` REGEXP 'a')"
1116
+ @d.literal(~Sequel.expr(:x).like(/a/i)).should == "(`x` NOT REGEXP 'a')"
912
1117
  end
913
1118
 
914
1119
  specify "should handle string concatenation with CONCAT if more than one record" do
915
- @d.literal([:x, :y].sql_string_join).should == "CONCAT(x, y)"
916
- @d.literal([:x, :y].sql_string_join(' ')).should == "CONCAT(x, ' ', y)"
917
- @d.literal([:x.sql_function(:y), 1, 'z'.lit].sql_string_join(:y.sql_subscript(1))).should == "CONCAT(x(y), y[1], '1', y[1], z)"
1120
+ @d.literal(Sequel.join([:x, :y])).should == "CONCAT(`x`, `y`)"
1121
+ @d.literal(Sequel.join([:x, :y], ' ')).should == "CONCAT(`x`, ' ', `y`)"
1122
+ @d.literal(Sequel.join([Sequel.function(:x, :y), 1, Sequel.lit('z')], Sequel.subscript(:y, 1))).should == "CONCAT(x(`y`), `y`[1], '1', `y`[1], z)"
918
1123
  end
919
1124
 
920
1125
  specify "should handle string concatenation as simple string if just one record" do
921
- @d.literal([:x].sql_string_join).should == "x"
922
- @d.literal([:x].sql_string_join(' ')).should == "x"
1126
+ @d.literal(Sequel.join([:x])).should == "`x`"
1127
+ @d.literal(Sequel.join([:x], ' ')).should == "`x`"
923
1128
  end
924
1129
  end
925
1130
 
@@ -928,29 +1133,33 @@ describe "MySQL::Dataset#calc_found_rows" do
928
1133
  MYSQL_DB.create_table!(:items){Integer :a}
929
1134
  end
930
1135
  after do
931
- MYSQL_DB.drop_table(:items)
1136
+ MYSQL_DB.drop_table?(:items)
932
1137
  end
933
1138
 
934
1139
  specify "should add the SQL_CALC_FOUND_ROWS keyword when selecting" do
935
1140
  MYSQL_DB[:items].select(:a).calc_found_rows.limit(1).sql.should == \
936
- 'SELECT SQL_CALC_FOUND_ROWS a FROM items LIMIT 1'
1141
+ 'SELECT SQL_CALC_FOUND_ROWS `a` FROM `items` LIMIT 1'
937
1142
  end
938
1143
 
939
1144
  specify "should count matching rows disregarding LIMIT clause" do
940
1145
  MYSQL_DB[:items].multi_insert([{:a => 1}, {:a => 1}, {:a => 2}])
941
1146
  MYSQL_DB.sqls.clear
942
1147
 
943
- MYSQL_DB[:items].calc_found_rows.filter(:a => 1).limit(1).all.should == [{:a => 1}]
944
- MYSQL_DB.dataset.select(:FOUND_ROWS.sql_function.as(:rows)).all.should == [{:rows => 2 }]
1148
+ MYSQL_DB.synchronize do
1149
+ MYSQL_DB[:items].calc_found_rows.filter(:a => 1).limit(1).all.should == [{:a => 1}]
1150
+ MYSQL_DB.dataset.select(Sequel.function(:FOUND_ROWS).as(:rows)).all.should == [{:rows => 2 }]
1151
+ end
945
1152
 
946
- MYSQL_DB.sqls.should == [
947
- 'SELECT SQL_CALC_FOUND_ROWS * FROM items WHERE (a = 1) LIMIT 1',
948
- 'SELECT FOUND_ROWS() AS rows',
949
- ]
1153
+ check_sqls do
1154
+ MYSQL_DB.sqls.should == [
1155
+ 'SELECT SQL_CALC_FOUND_ROWS * FROM `items` WHERE (`a` = 1) LIMIT 1',
1156
+ 'SELECT FOUND_ROWS() AS `rows`',
1157
+ ]
1158
+ end
950
1159
  end
951
1160
  end
952
1161
 
953
- if MYSQL_DB.adapter_scheme == :mysql or MYSQL_DB.adapter_scheme == :jdbc
1162
+ if MYSQL_DB.adapter_scheme == :mysql or MYSQL_DB.adapter_scheme == :jdbc or MYSQL_DB.adapter_scheme == :mysql2
954
1163
  describe "MySQL Stored Procedures" do
955
1164
  before do
956
1165
  MYSQL_DB.create_table(:items){Integer :id; Integer :value}
@@ -958,10 +1167,10 @@ if MYSQL_DB.adapter_scheme == :mysql or MYSQL_DB.adapter_scheme == :jdbc
958
1167
  MYSQL_DB.sqls.clear
959
1168
  end
960
1169
  after do
961
- MYSQL_DB.drop_table(:items)
1170
+ MYSQL_DB.drop_table?(:items)
962
1171
  MYSQL_DB.execute('DROP PROCEDURE test_sproc')
963
1172
  end
964
-
1173
+
965
1174
  specify "should be callable on the database object" do
966
1175
  MYSQL_DB.execute_ddl('CREATE PROCEDURE test_sproc() BEGIN DELETE FROM items; END')
967
1176
  MYSQL_DB[:items].delete
@@ -970,27 +1179,38 @@ if MYSQL_DB.adapter_scheme == :mysql or MYSQL_DB.adapter_scheme == :jdbc
970
1179
  MYSQL_DB.call_sproc(:test_sproc)
971
1180
  MYSQL_DB[:items].count.should == 0
972
1181
  end
973
-
974
- specify "should be callable on the dataset object" do
975
- MYSQL_DB.execute_ddl('CREATE PROCEDURE test_sproc(a INTEGER) BEGIN SELECT *, a AS b FROM items; END')
976
- MYSQL_DB[:items].delete
977
- @d = MYSQL_DB[:items]
978
- @d.call_sproc(:select, :test_sproc, 3).should == []
979
- @d.insert(:value=>1)
980
- @d.call_sproc(:select, :test_sproc, 4).should == [{:id=>nil, :value=>1, :b=>4}]
981
- @d.row_proc = proc{|r| r.keys.each{|k| r[k] *= 2 if r[k].is_a?(Integer)}; r}
982
- @d.call_sproc(:select, :test_sproc, 3).should == [{:id=>nil, :value=>2, :b=>6}]
983
- end
984
-
985
- specify "should be callable on the dataset object with multiple arguments" do
986
- MYSQL_DB.execute_ddl('CREATE PROCEDURE test_sproc(a INTEGER, c INTEGER) BEGIN SELECT *, a AS b, c AS d FROM items; END')
1182
+
1183
+ # Mysql2 doesn't support stored procedures that return result sets, probably because
1184
+ # CLIENT_MULTI_RESULTS is not set.
1185
+ unless MYSQL_DB.adapter_scheme == :mysql2
1186
+ specify "should be callable on the dataset object" do
1187
+ MYSQL_DB.execute_ddl('CREATE PROCEDURE test_sproc(a INTEGER) BEGIN SELECT *, a AS b FROM items; END')
1188
+ MYSQL_DB[:items].delete
1189
+ @d = MYSQL_DB[:items]
1190
+ @d.call_sproc(:select, :test_sproc, 3).should == []
1191
+ @d.insert(:value=>1)
1192
+ @d.call_sproc(:select, :test_sproc, 4).should == [{:id=>nil, :value=>1, :b=>4}]
1193
+ @d.row_proc = proc{|r| r.keys.each{|k| r[k] *= 2 if r[k].is_a?(Integer)}; r}
1194
+ @d.call_sproc(:select, :test_sproc, 3).should == [{:id=>nil, :value=>2, :b=>6}]
1195
+ end
1196
+
1197
+ specify "should be callable on the dataset object with multiple arguments" do
1198
+ MYSQL_DB.execute_ddl('CREATE PROCEDURE test_sproc(a INTEGER, c INTEGER) BEGIN SELECT *, a AS b, c AS d FROM items; END')
1199
+ MYSQL_DB[:items].delete
1200
+ @d = MYSQL_DB[:items]
1201
+ @d.call_sproc(:select, :test_sproc, 3, 4).should == []
1202
+ @d.insert(:value=>1)
1203
+ @d.call_sproc(:select, :test_sproc, 4, 5).should == [{:id=>nil, :value=>1, :b=>4, :d=>5}]
1204
+ @d.row_proc = proc{|r| r.keys.each{|k| r[k] *= 2 if r[k].is_a?(Integer)}; r}
1205
+ @d.call_sproc(:select, :test_sproc, 3, 4).should == [{:id=>nil, :value=>2, :b=>6, :d => 8}]
1206
+ end
1207
+ end
1208
+
1209
+ specify "should deal with nil values" do
1210
+ MYSQL_DB.execute_ddl('CREATE PROCEDURE test_sproc(i INTEGER, v INTEGER) BEGIN INSERT INTO items VALUES (i, v); END')
987
1211
  MYSQL_DB[:items].delete
988
- @d = MYSQL_DB[:items]
989
- @d.call_sproc(:select, :test_sproc, 3, 4).should == []
990
- @d.insert(:value=>1)
991
- @d.call_sproc(:select, :test_sproc, 4, 5).should == [{:id=>nil, :value=>1, :b=>4, :d=>5}]
992
- @d.row_proc = proc{|r| r.keys.each{|k| r[k] *= 2 if r[k].is_a?(Integer)}; r}
993
- @d.call_sproc(:select, :test_sproc, 3, 4).should == [{:id=>nil, :value=>2, :b=>6, :d => 8}]
1212
+ MYSQL_DB.call_sproc(:test_sproc, :args=>[1, nil])
1213
+ MYSQL_DB[:items].all.should == [{:id=>1, :value=>nil}]
994
1214
  end
995
1215
  end
996
1216
  end
@@ -998,35 +1218,35 @@ end
998
1218
  if MYSQL_DB.adapter_scheme == :mysql
999
1219
  describe "MySQL bad date/time conversions" do
1000
1220
  after do
1001
- Sequel::MySQL.convert_invalid_date_time = false
1221
+ MYSQL_DB.convert_invalid_date_time = false
1002
1222
  end
1003
-
1223
+
1004
1224
  specify "should raise an exception when a bad date/time is used and convert_invalid_date_time is false" do
1005
- Sequel::MySQL.convert_invalid_date_time = false
1225
+ MYSQL_DB.convert_invalid_date_time = false
1006
1226
  proc{MYSQL_DB["SELECT CAST('0000-00-00' AS date)"].single_value}.should raise_error(Sequel::InvalidValue)
1007
1227
  proc{MYSQL_DB["SELECT CAST('0000-00-00 00:00:00' AS datetime)"].single_value}.should raise_error(Sequel::InvalidValue)
1008
1228
  proc{MYSQL_DB["SELECT CAST('25:00:00' AS time)"].single_value}.should raise_error(Sequel::InvalidValue)
1009
1229
  end
1010
-
1230
+
1011
1231
  specify "should not use a nil value bad date/time is used and convert_invalid_date_time is nil or :nil" do
1012
- Sequel::MySQL.convert_invalid_date_time = nil
1232
+ MYSQL_DB.convert_invalid_date_time = nil
1013
1233
  MYSQL_DB["SELECT CAST('0000-00-00' AS date)"].single_value.should == nil
1014
1234
  MYSQL_DB["SELECT CAST('0000-00-00 00:00:00' AS datetime)"].single_value.should == nil
1015
1235
  MYSQL_DB["SELECT CAST('25:00:00' AS time)"].single_value.should == nil
1016
- Sequel::MySQL.convert_invalid_date_time = :nil
1236
+ MYSQL_DB.convert_invalid_date_time = :nil
1017
1237
  MYSQL_DB["SELECT CAST('0000-00-00' AS date)"].single_value.should == nil
1018
1238
  MYSQL_DB["SELECT CAST('0000-00-00 00:00:00' AS datetime)"].single_value.should == nil
1019
1239
  MYSQL_DB["SELECT CAST('25:00:00' AS time)"].single_value.should == nil
1020
1240
  end
1021
-
1241
+
1022
1242
  specify "should not use a nil value bad date/time is used and convert_invalid_date_time is :string" do
1023
- Sequel::MySQL.convert_invalid_date_time = :string
1243
+ MYSQL_DB.convert_invalid_date_time = :string
1024
1244
  MYSQL_DB["SELECT CAST('0000-00-00' AS date)"].single_value.should == '0000-00-00'
1025
1245
  MYSQL_DB["SELECT CAST('0000-00-00 00:00:00' AS datetime)"].single_value.should == '0000-00-00 00:00:00'
1026
1246
  MYSQL_DB["SELECT CAST('25:00:00' AS time)"].single_value.should == '25:00:00'
1027
1247
  end
1028
1248
  end
1029
-
1249
+
1030
1250
  describe "MySQL multiple result sets" do
1031
1251
  before do
1032
1252
  MYSQL_DB.create_table!(:a){Integer :a}
@@ -1038,43 +1258,68 @@ if MYSQL_DB.adapter_scheme == :mysql
1038
1258
  MYSQL_DB[:b].insert(25)
1039
1259
  end
1040
1260
  after do
1041
- MYSQL_DB.drop_table(:a, :b)
1261
+ MYSQL_DB.drop_table?(:a, :b)
1042
1262
  end
1043
-
1263
+
1044
1264
  specify "should combine all results by default" do
1045
1265
  @ds.all.should == [{:a=>10}, {:a=>15}, {:b=>20}, {:b=>25}]
1046
1266
  end
1047
-
1267
+
1048
1268
  specify "should work with Database#run" do
1049
1269
  proc{MYSQL_DB.run('SELECT * FROM a; SELECT * FROM b')}.should_not raise_error
1050
1270
  proc{MYSQL_DB.run('SELECT * FROM a; SELECT * FROM b')}.should_not raise_error
1051
1271
  end
1052
-
1272
+
1053
1273
  specify "should work with Database#run and other statements" do
1054
1274
  proc{MYSQL_DB.run('UPDATE a SET a = 1; SELECT * FROM a; DELETE FROM b')}.should_not raise_error
1055
1275
  MYSQL_DB[:a].select_order_map(:a).should == [1, 1]
1056
1276
  MYSQL_DB[:b].all.should == []
1057
1277
  end
1058
-
1278
+
1059
1279
  specify "should split results returned into arrays if split_multiple_result_sets is used" do
1060
1280
  @ds.split_multiple_result_sets.all.should == [[{:a=>10}, {:a=>15}], [{:b=>20}, {:b=>25}]]
1061
1281
  end
1062
-
1282
+
1063
1283
  specify "should have regular row_procs work when splitting multiple result sets" do
1064
1284
  @ds.row_proc = proc{|x| x[x.keys.first] *= 2; x}
1065
1285
  @ds.split_multiple_result_sets.all.should == [[{:a=>20}, {:a=>30}], [{:b=>40}, {:b=>50}]]
1066
1286
  end
1067
-
1287
+
1068
1288
  specify "should use the columns from the first result set when splitting result sets" do
1069
1289
  @ds.split_multiple_result_sets.columns.should == [:a]
1070
1290
  end
1071
-
1291
+
1072
1292
  specify "should not allow graphing a dataset that splits multiple statements" do
1073
1293
  proc{@ds.split_multiple_result_sets.graph(:b, :b=>:a)}.should raise_error(Sequel::Error)
1074
1294
  end
1075
-
1295
+
1076
1296
  specify "should not allow splitting a graphed dataset" do
1077
1297
  proc{MYSQL_DB[:a].graph(:b, :b=>:a).split_multiple_result_sets}.should raise_error(Sequel::Error)
1078
1298
  end
1079
1299
  end
1080
1300
  end
1301
+
1302
+ if MYSQL_DB.adapter_scheme == :mysql2
1303
+ describe "Mysql2 streaming" do
1304
+ before(:all) do
1305
+ MYSQL_DB.create_table!(:a){Integer :a}
1306
+ MYSQL_DB.transaction do
1307
+ 1000.times do |i|
1308
+ MYSQL_DB[:a].insert(i)
1309
+ end
1310
+ end
1311
+ @ds = MYSQL_DB[:a].stream.order(:a)
1312
+ end
1313
+ after(:all) do
1314
+ MYSQL_DB.drop_table?(:a)
1315
+ end
1316
+
1317
+ specify "should correctly stream results" do
1318
+ @ds.map(:a).should == (0...1000).to_a
1319
+ end
1320
+
1321
+ specify "should correctly handle early returning when streaming results" do
1322
+ 3.times{@ds.each{|r| break r[:a]}.should == 0}
1323
+ end
1324
+ end if false
1325
+ end