sequel 3.21.0 → 3.39.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 (352) hide show
  1. data/CHANGELOG +1061 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +103 -63
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +67 -51
  7. data/doc/advanced_associations.rdoc +189 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/cheat_sheet.rdoc +21 -21
  10. data/doc/core_extensions.rdoc +374 -0
  11. data/doc/dataset_basics.rdoc +10 -10
  12. data/doc/dataset_filtering.rdoc +54 -42
  13. data/doc/mass_assignment.rdoc +56 -0
  14. data/doc/migration.rdoc +89 -557
  15. data/doc/model_hooks.rdoc +80 -29
  16. data/doc/object_model.rdoc +547 -0
  17. data/doc/opening_databases.rdoc +114 -53
  18. data/doc/prepared_statements.rdoc +41 -15
  19. data/doc/querying.rdoc +137 -56
  20. data/doc/reflection.rdoc +36 -10
  21. data/doc/release_notes/3.22.0.txt +39 -0
  22. data/doc/release_notes/3.23.0.txt +172 -0
  23. data/doc/release_notes/3.24.0.txt +420 -0
  24. data/doc/release_notes/3.25.0.txt +88 -0
  25. data/doc/release_notes/3.26.0.txt +88 -0
  26. data/doc/release_notes/3.27.0.txt +82 -0
  27. data/doc/release_notes/3.28.0.txt +304 -0
  28. data/doc/release_notes/3.29.0.txt +459 -0
  29. data/doc/release_notes/3.30.0.txt +135 -0
  30. data/doc/release_notes/3.31.0.txt +146 -0
  31. data/doc/release_notes/3.32.0.txt +202 -0
  32. data/doc/release_notes/3.33.0.txt +157 -0
  33. data/doc/release_notes/3.34.0.txt +671 -0
  34. data/doc/release_notes/3.35.0.txt +144 -0
  35. data/doc/release_notes/3.36.0.txt +245 -0
  36. data/doc/release_notes/3.37.0.txt +338 -0
  37. data/doc/release_notes/3.38.0.txt +234 -0
  38. data/doc/release_notes/3.39.0.txt +237 -0
  39. data/doc/schema_modification.rdoc +585 -0
  40. data/doc/sharding.rdoc +99 -8
  41. data/doc/sql.rdoc +154 -112
  42. data/doc/testing.rdoc +169 -0
  43. data/doc/thread_safety.rdoc +17 -0
  44. data/doc/transactions.rdoc +137 -0
  45. data/doc/validations.rdoc +1 -1
  46. data/doc/virtual_rows.rdoc +78 -43
  47. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  48. data/lib/sequel/adapters/ado.rb +40 -18
  49. data/lib/sequel/adapters/amalgalite.rb +17 -9
  50. data/lib/sequel/adapters/db2.rb +175 -86
  51. data/lib/sequel/adapters/dbi.rb +15 -15
  52. data/lib/sequel/adapters/do/mysql.rb +17 -11
  53. data/lib/sequel/adapters/do/postgres.rb +2 -61
  54. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  55. data/lib/sequel/adapters/do.rb +18 -36
  56. data/lib/sequel/adapters/firebird.rb +27 -208
  57. data/lib/sequel/adapters/ibmdb.rb +453 -0
  58. data/lib/sequel/adapters/informix.rb +6 -23
  59. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  60. data/lib/sequel/adapters/jdbc/db2.rb +56 -0
  61. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  62. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  63. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  64. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  65. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  66. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  67. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  68. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  69. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  70. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  71. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  73. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  74. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  75. data/lib/sequel/adapters/jdbc.rb +279 -97
  76. data/lib/sequel/adapters/mock.rb +372 -0
  77. data/lib/sequel/adapters/mysql.rb +125 -222
  78. data/lib/sequel/adapters/mysql2.rb +65 -56
  79. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  80. data/lib/sequel/adapters/odbc.rb +28 -9
  81. data/lib/sequel/adapters/openbase.rb +3 -5
  82. data/lib/sequel/adapters/oracle.rb +349 -53
  83. data/lib/sequel/adapters/postgres.rb +368 -138
  84. data/lib/sequel/adapters/shared/access.rb +32 -9
  85. data/lib/sequel/adapters/shared/db2.rb +343 -0
  86. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  87. data/lib/sequel/adapters/shared/informix.rb +53 -0
  88. data/lib/sequel/adapters/shared/mssql.rb +401 -116
  89. data/lib/sequel/adapters/shared/mysql.rb +424 -89
  90. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  91. data/lib/sequel/adapters/shared/oracle.rb +257 -45
  92. data/lib/sequel/adapters/shared/postgres.rb +675 -279
  93. data/lib/sequel/adapters/shared/progress.rb +5 -8
  94. data/lib/sequel/adapters/shared/sqlite.rb +285 -96
  95. data/lib/sequel/adapters/sqlite.rb +101 -39
  96. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  97. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  98. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  99. data/lib/sequel/adapters/swift.rb +28 -19
  100. data/lib/sequel/adapters/tinytds.rb +174 -22
  101. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  102. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  103. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  104. data/lib/sequel/ast_transformer.rb +194 -0
  105. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  106. data/lib/sequel/connection_pool/sharded_threaded.rb +33 -2
  107. data/lib/sequel/connection_pool/single.rb +5 -0
  108. data/lib/sequel/connection_pool/threaded.rb +43 -14
  109. data/lib/sequel/connection_pool.rb +10 -2
  110. data/lib/sequel/core.rb +143 -11
  111. data/lib/sequel/database/connecting.rb +38 -28
  112. data/lib/sequel/database/dataset.rb +4 -4
  113. data/lib/sequel/database/dataset_defaults.rb +63 -2
  114. data/lib/sequel/database/logging.rb +7 -2
  115. data/lib/sequel/database/misc.rb +190 -46
  116. data/lib/sequel/database/query.rb +272 -110
  117. data/lib/sequel/database/schema_generator.rb +103 -36
  118. data/lib/sequel/database/schema_methods.rb +300 -67
  119. data/lib/sequel/dataset/actions.rb +344 -90
  120. data/lib/sequel/dataset/features.rb +101 -8
  121. data/lib/sequel/dataset/graph.rb +42 -15
  122. data/lib/sequel/dataset/misc.rb +52 -25
  123. data/lib/sequel/dataset/mutation.rb +12 -7
  124. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  125. data/lib/sequel/dataset/query.rb +305 -54
  126. data/lib/sequel/dataset/sql.rb +720 -292
  127. data/lib/sequel/dataset.rb +8 -0
  128. data/lib/sequel/exceptions.rb +4 -0
  129. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  130. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  131. data/lib/sequel/extensions/blank.rb +4 -0
  132. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  133. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  134. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  135. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  136. data/lib/sequel/extensions/inflector.rb +4 -0
  137. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  138. data/lib/sequel/extensions/migration.rb +100 -11
  139. data/lib/sequel/extensions/named_timezones.rb +9 -0
  140. data/lib/sequel/extensions/null_dataset.rb +94 -0
  141. data/lib/sequel/extensions/pagination.rb +4 -0
  142. data/lib/sequel/extensions/pg_array.rb +537 -0
  143. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  144. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  145. data/lib/sequel/extensions/pg_hstore.rb +313 -0
  146. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  147. data/lib/sequel/extensions/pg_inet.rb +113 -0
  148. data/lib/sequel/extensions/pg_interval.rb +191 -0
  149. data/lib/sequel/extensions/pg_json.rb +208 -0
  150. data/lib/sequel/extensions/pg_range.rb +512 -0
  151. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  152. data/lib/sequel/extensions/pg_row.rb +572 -0
  153. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  154. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  155. data/lib/sequel/extensions/pretty_table.rb +17 -72
  156. data/lib/sequel/extensions/query.rb +8 -4
  157. data/lib/sequel/extensions/query_literals.rb +79 -0
  158. data/lib/sequel/extensions/schema_caching.rb +76 -0
  159. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  160. data/lib/sequel/extensions/select_remove.rb +39 -0
  161. data/lib/sequel/extensions/server_block.rb +140 -0
  162. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  163. data/lib/sequel/extensions/sql_expr.rb +8 -110
  164. data/lib/sequel/extensions/string_date_time.rb +4 -0
  165. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  166. data/lib/sequel/extensions/to_dot.rb +99 -83
  167. data/lib/sequel/model/associations.rb +1263 -409
  168. data/lib/sequel/model/base.rb +624 -171
  169. data/lib/sequel/model/errors.rb +1 -1
  170. data/lib/sequel/model/exceptions.rb +24 -2
  171. data/lib/sequel/model/inflections.rb +1 -1
  172. data/lib/sequel/model.rb +30 -11
  173. data/lib/sequel/no_core_ext.rb +2 -0
  174. data/lib/sequel/plugins/active_model.rb +13 -1
  175. data/lib/sequel/plugins/association_pks.rb +22 -4
  176. data/lib/sequel/plugins/caching.rb +25 -18
  177. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  178. data/lib/sequel/plugins/composition.rb +44 -12
  179. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  180. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  181. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  182. data/lib/sequel/plugins/dirty.rb +214 -0
  183. data/lib/sequel/plugins/eager_each.rb +59 -0
  184. data/lib/sequel/plugins/force_encoding.rb +6 -6
  185. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  186. data/lib/sequel/plugins/identity_map.rb +134 -15
  187. data/lib/sequel/plugins/instance_filters.rb +10 -0
  188. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  189. data/lib/sequel/plugins/json_serializer.rb +58 -6
  190. data/lib/sequel/plugins/list.rb +13 -2
  191. data/lib/sequel/plugins/many_through_many.rb +103 -51
  192. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  193. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  194. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  195. data/lib/sequel/plugins/pg_row.rb +121 -0
  196. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  197. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  198. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  199. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  200. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  201. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  202. data/lib/sequel/plugins/schema.rb +8 -3
  203. data/lib/sequel/plugins/serialization.rb +98 -49
  204. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  205. data/lib/sequel/plugins/sharding.rb +21 -54
  206. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  207. data/lib/sequel/plugins/static_cache.rb +99 -0
  208. data/lib/sequel/plugins/subclasses.rb +29 -3
  209. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  210. data/lib/sequel/plugins/timestamps.rb +1 -1
  211. data/lib/sequel/plugins/tree.rb +3 -3
  212. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  213. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  214. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  215. data/lib/sequel/plugins/validation_helpers.rb +55 -4
  216. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  217. data/lib/sequel/sql.rb +649 -122
  218. data/lib/sequel/timezones.rb +67 -40
  219. data/lib/sequel/version.rb +1 -1
  220. data/spec/adapters/db2_spec.rb +146 -0
  221. data/spec/adapters/firebird_spec.rb +1 -1
  222. data/spec/adapters/mssql_spec.rb +194 -66
  223. data/spec/adapters/mysql_spec.rb +475 -306
  224. data/spec/adapters/oracle_spec.rb +92 -117
  225. data/spec/adapters/postgres_spec.rb +1982 -325
  226. data/spec/adapters/spec_helper.rb +9 -6
  227. data/spec/adapters/sqlite_spec.rb +203 -75
  228. data/spec/core/connection_pool_spec.rb +218 -93
  229. data/spec/core/database_spec.rb +956 -522
  230. data/spec/core/dataset_spec.rb +1631 -1242
  231. data/spec/core/expression_filters_spec.rb +690 -334
  232. data/spec/core/mock_adapter_spec.rb +453 -0
  233. data/spec/core/object_graph_spec.rb +88 -129
  234. data/spec/core/schema_generator_spec.rb +4 -4
  235. data/spec/core/schema_spec.rb +556 -53
  236. data/spec/core/spec_helper.rb +7 -48
  237. data/spec/core_extensions_spec.rb +626 -0
  238. data/spec/extensions/active_model_spec.rb +13 -0
  239. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  240. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  241. data/spec/extensions/association_dependencies_spec.rb +15 -25
  242. data/spec/extensions/association_pks_spec.rb +66 -32
  243. data/spec/extensions/association_proxies_spec.rb +4 -4
  244. data/spec/extensions/boolean_readers_spec.rb +25 -25
  245. data/spec/extensions/caching_spec.rb +47 -51
  246. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  247. data/spec/extensions/columns_introspection_spec.rb +91 -0
  248. data/spec/extensions/composition_spec.rb +18 -13
  249. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  250. data/spec/extensions/constraint_validations_spec.rb +316 -0
  251. data/spec/extensions/dataset_associations_spec.rb +199 -0
  252. data/spec/extensions/defaults_setter_spec.rb +88 -0
  253. data/spec/extensions/dirty_spec.rb +155 -0
  254. data/spec/extensions/eager_each_spec.rb +34 -0
  255. data/spec/extensions/eval_inspect_spec.rb +67 -0
  256. data/spec/extensions/force_encoding_spec.rb +4 -2
  257. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  258. data/spec/extensions/identity_map_spec.rb +142 -24
  259. data/spec/extensions/inflector_spec.rb +0 -4
  260. data/spec/extensions/instance_filters_spec.rb +11 -21
  261. data/spec/extensions/instance_hooks_spec.rb +72 -0
  262. data/spec/extensions/json_serializer_spec.rb +49 -12
  263. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  264. data/spec/extensions/list_spec.rb +49 -40
  265. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  266. data/spec/extensions/many_through_many_spec.rb +368 -254
  267. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  268. data/spec/extensions/migration_spec.rb +140 -35
  269. data/spec/extensions/named_timezones_spec.rb +29 -11
  270. data/spec/extensions/nested_attributes_spec.rb +268 -89
  271. data/spec/extensions/null_dataset_spec.rb +85 -0
  272. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  273. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  274. data/spec/extensions/pg_array_spec.rb +320 -0
  275. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  276. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  277. data/spec/extensions/pg_hstore_spec.rb +194 -0
  278. data/spec/extensions/pg_inet_spec.rb +47 -0
  279. data/spec/extensions/pg_interval_spec.rb +72 -0
  280. data/spec/extensions/pg_json_spec.rb +99 -0
  281. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  282. data/spec/extensions/pg_range_spec.rb +395 -0
  283. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  284. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  285. data/spec/extensions/pg_row_spec.rb +323 -0
  286. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  287. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  288. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  289. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  290. data/spec/extensions/prepared_statements_spec.rb +87 -0
  291. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  292. data/spec/extensions/pretty_table_spec.rb +7 -6
  293. data/spec/extensions/query_literals_spec.rb +167 -0
  294. data/spec/extensions/query_spec.rb +9 -3
  295. data/spec/extensions/rcte_tree_spec.rb +50 -43
  296. data/spec/extensions/schema_caching_spec.rb +41 -0
  297. data/spec/extensions/schema_dumper_spec.rb +434 -49
  298. data/spec/extensions/schema_spec.rb +30 -49
  299. data/spec/extensions/select_remove_spec.rb +38 -0
  300. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  301. data/spec/extensions/serialization_spec.rb +86 -48
  302. data/spec/extensions/server_block_spec.rb +90 -0
  303. data/spec/extensions/sharding_spec.rb +69 -143
  304. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  305. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  306. data/spec/extensions/spec_helper.rb +34 -67
  307. data/spec/extensions/split_array_nil_spec.rb +24 -0
  308. data/spec/extensions/sql_expr_spec.rb +29 -60
  309. data/spec/extensions/static_cache_spec.rb +145 -0
  310. data/spec/extensions/subclasses_spec.rb +14 -0
  311. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  312. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  313. data/spec/extensions/timestamps_spec.rb +6 -6
  314. data/spec/extensions/to_dot_spec.rb +8 -11
  315. data/spec/extensions/touch_spec.rb +13 -14
  316. data/spec/extensions/tree_spec.rb +11 -26
  317. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  318. data/spec/extensions/update_primary_key_spec.rb +30 -24
  319. data/spec/extensions/validation_class_methods_spec.rb +58 -67
  320. data/spec/extensions/validation_helpers_spec.rb +31 -37
  321. data/spec/extensions/xml_serializer_spec.rb +29 -4
  322. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  323. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  324. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  325. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  326. data/spec/integration/associations_test.rb +659 -21
  327. data/spec/integration/database_test.rb +11 -5
  328. data/spec/integration/dataset_test.rb +717 -129
  329. data/spec/integration/eager_loader_test.rb +38 -54
  330. data/spec/integration/migrator_test.rb +2 -3
  331. data/spec/integration/model_test.rb +81 -2
  332. data/spec/integration/plugin_test.rb +604 -147
  333. data/spec/integration/prepared_statement_test.rb +272 -128
  334. data/spec/integration/schema_test.rb +367 -57
  335. data/spec/integration/spec_helper.rb +42 -32
  336. data/spec/integration/timezone_test.rb +38 -12
  337. data/spec/integration/transaction_test.rb +183 -8
  338. data/spec/integration/type_test.rb +31 -8
  339. data/spec/model/association_reflection_spec.rb +207 -8
  340. data/spec/model/associations_spec.rb +1206 -693
  341. data/spec/model/base_spec.rb +334 -100
  342. data/spec/model/dataset_methods_spec.rb +45 -27
  343. data/spec/model/eager_loading_spec.rb +828 -692
  344. data/spec/model/hooks_spec.rb +355 -67
  345. data/spec/model/model_spec.rb +251 -179
  346. data/spec/model/plugins_spec.rb +24 -13
  347. data/spec/model/record_spec.rb +506 -232
  348. data/spec/model/spec_helper.rb +16 -77
  349. data/spec/model/validations_spec.rb +38 -2
  350. data/spec/rcov.opts +2 -0
  351. metadata +209 -80
  352. data/spec/core/core_sql_spec.rb +0 -451
@@ -5,97 +5,70 @@ rescue LoadError
5
5
  end
6
6
  raise(LoadError, "require 'mysql' did not define Mysql::CLIENT_MULTI_RESULTS!\n You are probably using the pure ruby mysql.rb driver,\n which Sequel does not support. You need to install\n the C based adapter, and make sure that the mysql.so\n file is loaded instead of the mysql.rb file.\n") unless defined?(Mysql::CLIENT_MULTI_RESULTS)
7
7
 
8
- Sequel.require %w'shared/mysql utils/stored_procedures', 'adapters'
8
+ Sequel.require %w'shared/mysql_prepared_statements', 'adapters'
9
9
 
10
10
  module Sequel
11
11
  # Module for holding all MySQL-related classes and modules for Sequel.
12
12
  module MySQL
13
13
  TYPE_TRANSLATOR = tt = Class.new do
14
14
  def boolean(s) s.to_i != 0 end
15
- def blob(s) ::Sequel::SQL::Blob.new(s) end
16
15
  def integer(s) s.to_i end
17
16
  def float(s) s.to_f end
18
- def decimal(s) ::BigDecimal.new(s) end
19
- def date(s) ::Sequel.string_to_date(s) end
20
- def time(s) ::Sequel.string_to_time(s) end
21
- def timestamp(s) ::Sequel.database_to_application_timestamp(s) end
22
- def date_conv(s) ::Sequel::MySQL.convert_date_time(:string_to_date, s) end
23
- def time_conv(s) ::Sequel::MySQL.convert_date_time(:string_to_time, s) end
24
- def timestamp_conv(s) ::Sequel::MySQL.convert_date_time(:database_to_application_timestamp, s) end
25
17
  end.new
26
18
 
27
19
  # Hash with integer keys and callable values for converting MySQL types.
28
20
  MYSQL_TYPES = {}
29
21
  {
30
- [0, 246] => tt.method(:decimal),
31
- [2, 3, 8, 9, 13, 247, 248] => tt.method(:integer),
32
- [4, 5] => tt.method(:float),
33
- [249, 250, 251, 252] => tt.method(:blob)
22
+ [0, 246] => ::BigDecimal.method(:new),
23
+ [2, 3, 8, 9, 13, 247, 248] => tt.method(:integer),
24
+ [4, 5] => tt.method(:float),
25
+ [249, 250, 251, 252] => ::Sequel::SQL::Blob.method(:new)
34
26
  }.each do |k,v|
35
27
  k.each{|n| MYSQL_TYPES[n] = v}
36
28
  end
37
29
 
38
- # Modify the type translator used for the tinyint type based
39
- # on the value given.
40
- def self.convert_tinyint_to_bool=(v)
41
- MYSQL_TYPES[1] = TYPE_TRANSLATOR.method(v ? :boolean : :integer)
42
- @convert_tinyint_to_bool = v
43
- end
44
- self.convert_tinyint_to_bool = convert_tinyint_to_bool
45
-
46
30
  class << self
47
- # By default, Sequel raises an exception if in invalid date or time is used.
48
- # However, if this is set to nil or :nil, the adapter treats dates
49
- # like 0000-00-00 and times like 838:00:00 as nil values. If set to :string,
50
- # it returns the strings as is.
51
- attr_reader :convert_invalid_date_time
52
- end
53
-
54
- # Modify the type translators for the date, time, and timestamp types
55
- # depending on the value given.
56
- def self.convert_invalid_date_time=(v)
57
- MYSQL_TYPES[11] = TYPE_TRANSLATOR.method(v == false ? :time : :time_conv)
58
- m = TYPE_TRANSLATOR.method(v == false ? :date : :date_conv)
59
- [10, 14].each{|i| MYSQL_TYPES[i] = m}
60
- m = TYPE_TRANSLATOR.method(v == false ? :timestamp : :timestamp_conv)
61
- [7, 12].each{|i| MYSQL_TYPES[i] = m}
62
- @convert_invalid_date_time = v
31
+ # Whether to convert invalid date time values by default.
32
+ #
33
+ # Only applies to Sequel::Database instances created after this
34
+ # has been set.
35
+ attr_accessor :convert_invalid_date_time
63
36
  end
64
37
  self.convert_invalid_date_time = false
65
38
 
66
- # If convert_invalid_date_time is nil, :nil, or :string and
67
- # the conversion raises an InvalidValue exception, return v
68
- # if :string and nil otherwise.
69
- def self.convert_date_time(meth, v)
70
- begin
71
- Sequel.send(meth, v)
72
- rescue InvalidValue
73
- case @convert_invalid_date_time
74
- when nil, :nil
75
- nil
76
- when :string
77
- v
78
- else
79
- raise
80
- end
81
- end
82
- end
83
-
84
39
  # Database class for MySQL databases used with Sequel.
85
40
  class Database < Sequel::Database
86
41
  include Sequel::MySQL::DatabaseMethods
42
+ include Sequel::MySQL::PreparedStatements::DatabaseMethods
87
43
 
88
44
  # Mysql::Error messages that indicate the current connection should be disconnected
89
45
  MYSQL_DATABASE_DISCONNECT_ERRORS = /\A(Commands out of sync; you can't run this command now|Can't connect to local MySQL server through socket|MySQL server has gone away|Lost connection to MySQL server during query)/
46
+
47
+ # Regular expression used for getting accurate number of rows
48
+ # matched by an update statement.
49
+ AFFECTED_ROWS_RE = /Rows matched:\s+(\d+)\s+Changed:\s+\d+\s+Warnings:\s+\d+/.freeze
90
50
 
91
51
  set_adapter_scheme :mysql
52
+
53
+ # Hash of conversion procs for the current database
54
+ attr_reader :conversion_procs
55
+ #
56
+ # Whether to convert tinyint columns to bool for the current database
57
+ attr_reader :convert_tinyint_to_bool
58
+
59
+ # By default, Sequel raises an exception if in invalid date or time is used.
60
+ # However, if this is set to nil or :nil, the adapter treats dates
61
+ # like 0000-00-00 and times like 838:00:00 as nil values. If set to :string,
62
+ # it returns the strings as is.
63
+ attr_reader :convert_invalid_date_time
92
64
 
93
- # Support stored procedures on MySQL
94
- def call_sproc(name, opts={}, &block)
95
- args = opts[:args] || []
96
- execute("CALL #{name}#{args.empty? ? '()' : literal(args)}", opts.merge(:sproc=>false), &block)
65
+ def initialize(opts={})
66
+ super
67
+ @conversion_procs = MYSQL_TYPES.dup
68
+ self.convert_tinyint_to_bool = Sequel::MySQL.convert_tinyint_to_bool
69
+ self.convert_invalid_date_time = Sequel::MySQL.convert_invalid_date_time
97
70
  end
98
-
71
+
99
72
  # Connect to the database. In addition to the usual database options,
100
73
  # the following options have effect:
101
74
  #
@@ -139,52 +112,57 @@ module Sequel
139
112
  opts[:user],
140
113
  opts[:password],
141
114
  opts[:database],
142
- opts[:port],
115
+ (opts[:port].to_i if opts[:port]),
143
116
  opts[:socket],
144
117
  Mysql::CLIENT_MULTI_RESULTS +
145
118
  Mysql::CLIENT_MULTI_STATEMENTS +
146
119
  (opts[:compress] == false ? 0 : Mysql::CLIENT_COMPRESS)
147
120
  )
148
- sqls = []
121
+ sqls = mysql_connection_setting_sqls
122
+
149
123
  # Set encoding a slightly different way after connecting,
150
124
  # in case the READ_DEFAULT_GROUP overrode the provided encoding.
151
125
  # Doesn't work across implicit reconnects, but Sequel doesn't turn on
152
126
  # that feature.
153
- sqls << "SET NAMES #{literal(encoding.to_s)}" if encoding
154
-
155
- # Increase timeout so mysql server doesn't disconnect us
156
- # Value used by default is maximum allowed value on Windows.
157
- sqls << "SET @@wait_timeout = #{opts[:timeout] || 2147483}"
158
-
159
- # By default, MySQL 'where id is null' selects the last inserted id
160
- sqls << "SET SQL_AUTO_IS_NULL=0" unless opts[:auto_is_null]
127
+ sqls.unshift("SET NAMES #{literal(encoding.to_s)}") if encoding
161
128
 
162
129
  sqls.each{|sql| log_yield(sql){conn.query(sql)}}
163
130
 
164
- class << conn
165
- attr_accessor :prepared_statements
166
- end
167
- conn.prepared_statements = {}
131
+ add_prepared_statements_cache(conn)
168
132
  conn
169
133
  end
170
134
 
171
- # Returns instance of Sequel::MySQL::Dataset with the given options.
172
- def dataset(opts = nil)
173
- MySQL::Dataset.new(self, opts)
135
+ # Modify the type translators for the date, time, and timestamp types
136
+ # depending on the value given.
137
+ def convert_invalid_date_time=(v)
138
+ m0 = ::Sequel.method(:string_to_time)
139
+ @conversion_procs[11] = (v != false) ? lambda{|v| convert_date_time(v, &m0)} : m0
140
+ m1 = ::Sequel.method(:string_to_date)
141
+ m = (v != false) ? lambda{|v| convert_date_time(v, &m1)} : m1
142
+ [10, 14].each{|i| @conversion_procs[i] = m}
143
+ m2 = method(:to_application_timestamp)
144
+ m = (v != false) ? lambda{|v| convert_date_time(v, &m2)} : m2
145
+ [7, 12].each{|i| @conversion_procs[i] = m}
146
+ @convert_invalid_date_time = v
174
147
  end
175
-
176
- # Executes the given SQL using an available connection, yielding the
177
- # connection if the block is given.
178
- def execute(sql, opts={}, &block)
179
- if opts[:sproc]
180
- call_sproc(sql, opts, &block)
181
- elsif sql.is_a?(Symbol)
182
- execute_prepared_statement(sql, opts, &block)
183
- else
184
- synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
185
- end
148
+
149
+ # Modify the type translator used for the tinyint type based
150
+ # on the value given.
151
+ def convert_tinyint_to_bool=(v)
152
+ @conversion_procs[1] = TYPE_TRANSLATOR.method(v ? :boolean : :integer)
153
+ @convert_tinyint_to_bool = v
186
154
  end
187
-
155
+
156
+ # Return the number of matched rows when executing a delete/update statement.
157
+ def execute_dui(sql, opts={})
158
+ execute(sql, opts){|c| return affected_rows(c)}
159
+ end
160
+
161
+ # Return the last inserted id when executing an insert statement.
162
+ def execute_insert(sql, opts={})
163
+ execute(sql, opts){|c| return c.insert_id}
164
+ end
165
+
188
166
  # Return the version of the MySQL server two which we are connecting.
189
167
  def server_version(server=nil)
190
168
  @server_version ||= (synchronize(server){|conn| conn.server_version if conn.respond_to?(:server_version)} || super)
@@ -197,7 +175,7 @@ module Sequel
197
175
  # yield the connection if a block is given.
198
176
  def _execute(conn, sql, opts)
199
177
  begin
200
- r = log_yield(sql){conn.query(sql)}
178
+ r = log_yield((log_sql = opts[:log_sql]) ? sql + log_sql : sql){conn.query(sql)}
201
179
  if opts[:type] == :select
202
180
  yield r if r
203
181
  elsif block_given?
@@ -220,7 +198,7 @@ module Sequel
220
198
  end
221
199
  end
222
200
  rescue Mysql::Error => e
223
- raise_error(e, :disconnect=>MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
201
+ raise_error(e)
224
202
  ensure
225
203
  r.free if r
226
204
  # Use up all results to avoid a commands out of sync message.
@@ -239,15 +217,51 @@ module Sequel
239
217
  end
240
218
  end
241
219
 
220
+ # Try to get an accurate number of rows matched using the query
221
+ # info. Fall back to affected_rows if there was no match, but
222
+ # that may be inaccurate.
223
+ def affected_rows(conn)
224
+ s = conn.info
225
+ if s && s =~ AFFECTED_ROWS_RE
226
+ $1.to_i
227
+ else
228
+ conn.affected_rows
229
+ end
230
+ end
231
+
242
232
  # MySQL connections use the query method to execute SQL without a result
243
233
  def connection_execute_method
244
234
  :query
245
235
  end
246
236
 
237
+ # If convert_invalid_date_time is nil, :nil, or :string and
238
+ # the conversion raises an InvalidValue exception, return v
239
+ # if :string and nil otherwise.
240
+ def convert_date_time(v)
241
+ begin
242
+ yield v
243
+ rescue InvalidValue
244
+ case @convert_invalid_date_time
245
+ when nil, :nil
246
+ nil
247
+ when :string
248
+ v
249
+ else
250
+ raise
251
+ end
252
+ end
253
+ end
254
+
247
255
  # The MySQL adapter main error class is Mysql::Error
248
256
  def database_error_classes
249
257
  [Mysql::Error]
250
258
  end
259
+
260
+ # Raise a disconnect error if the exception message matches the list
261
+ # of recognized exceptions.
262
+ def disconnect_error?(e, opts)
263
+ super || (e.is_a?(::Mysql::Error) && MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
264
+ end
251
265
 
252
266
  # The database name when using the native adapter is always stored in
253
267
  # the :database option.
@@ -262,114 +276,31 @@ module Sequel
262
276
  nil
263
277
  end
264
278
 
265
- # Executes a prepared statement on an available connection. If the
266
- # prepared statement already exists for the connection and has the same
267
- # SQL, reuse it, otherwise, prepare the new statement. Because of the
268
- # usual MySQL stupidity, we are forced to name arguments via separate
269
- # SET queries. Use @sequel_arg_N (for N starting at 1) for these
270
- # arguments.
271
- def execute_prepared_statement(ps_name, opts, &block)
272
- args = opts[:arguments]
273
- ps = prepared_statements[ps_name]
274
- sql = ps.prepared_sql
275
- synchronize(opts[:server]) do |conn|
276
- unless conn.prepared_statements[ps_name] == sql
277
- conn.prepared_statements[ps_name] = sql
278
- _execute(conn, "PREPARE #{ps_name} FROM '#{::Mysql.quote(sql)}'", opts)
279
- end
280
- i = 0
281
- _execute(conn, "SET " + args.map {|arg| "@sequel_arg_#{i+=1} = #{literal(arg)}"}.join(", "), opts) unless args.empty?
282
- _execute(conn, "EXECUTE #{ps_name}#{" USING #{(1..i).map{|j| "@sequel_arg_#{j}"}.join(', ')}" unless i == 0}", opts, &block)
283
- end
284
- end
285
-
286
279
  # Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
287
280
  def schema_column_type(db_type)
288
- Sequel::MySQL.convert_tinyint_to_bool && db_type == 'tinyint(1)' ? :boolean : super
281
+ convert_tinyint_to_bool && db_type == 'tinyint(1)' ? :boolean : super
289
282
  end
290
283
  end
291
284
 
292
285
  # Dataset class for MySQL datasets accessed via the native driver.
293
286
  class Dataset < Sequel::Dataset
294
287
  include Sequel::MySQL::DatasetMethods
295
- include StoredProcedures
296
-
297
- # Methods to add to MySQL prepared statement calls without using a
298
- # real database prepared statement and bound variables.
299
- module CallableStatementMethods
300
- # Extend given dataset with this module so subselects inside subselects in
301
- # prepared statements work.
302
- def subselect_sql(ds)
303
- ps = ds.to_prepared_statement(:select)
304
- ps.extend(CallableStatementMethods)
305
- ps = ps.bind(@opts[:bind_vars]) if @opts[:bind_vars]
306
- ps.prepared_args = prepared_args
307
- ps.prepared_sql
308
- end
309
- end
310
-
311
- # Methods for MySQL prepared statements using the native driver.
312
- module PreparedStatementMethods
313
- include Sequel::Dataset::UnnumberedArgumentMapper
314
-
315
- private
316
-
317
- # Execute the prepared statement with the bind arguments instead of
318
- # the given SQL.
319
- def execute(sql, opts={}, &block)
320
- super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
321
- end
322
-
323
- # Same as execute, explicit due to intricacies of alias and super.
324
- def execute_dui(sql, opts={}, &block)
325
- super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
326
- end
327
- end
328
-
329
- # Methods for MySQL stored procedures using the native driver.
330
- module StoredProcedureMethods
331
- include Sequel::Dataset::StoredProcedureMethods
332
-
333
- private
334
-
335
- # Execute the database stored procedure with the stored arguments.
336
- def execute(sql, opts={}, &block)
337
- super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
338
- end
339
-
340
- # Same as execute, explicit due to intricacies of alias and super.
341
- def execute_dui(sql, opts={}, &block)
342
- super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
343
- end
344
- end
345
-
346
- # MySQL is different in that it supports prepared statements but not bound
347
- # variables outside of prepared statements. The default implementation
348
- # breaks the use of subselects in prepared statements, so extend the
349
- # temporary prepared statement that this creates with a module that
350
- # fixes it.
351
- def call(type, bind_arguments={}, *values, &block)
352
- ps = to_prepared_statement(type, values)
353
- ps.extend(CallableStatementMethods)
354
- ps.call(bind_arguments, &block)
355
- end
356
-
357
- # Delete rows matching this dataset
358
- def delete
359
- execute_dui(delete_sql){|c| return c.affected_rows}
360
- end
361
-
288
+ include Sequel::MySQL::PreparedStatements::DatasetMethods
289
+
290
+ Database::DatasetClass = self
291
+
362
292
  # Yield all rows matching this dataset. If the dataset is set to
363
293
  # split multiple statements, yield arrays of hashes one per statement
364
294
  # instead of yielding results for all statements as hashes.
365
- def fetch_rows(sql, &block)
295
+ def fetch_rows(sql)
366
296
  execute(sql) do |r|
367
297
  i = -1
298
+ cps = db.conversion_procs
368
299
  cols = r.fetch_fields.map do |f|
369
300
  # Pretend tinyint is another integer type if its length is not 1, to
370
301
  # avoid casting to boolean if Sequel::MySQL.convert_tinyint_to_bool
371
302
  # is set.
372
- type_proc = f.type == 1 && f.length != 1 ? MYSQL_TYPES[2] : MYSQL_TYPES[f.type]
303
+ type_proc = f.type == 1 && cast_tinyint_integer?(f) ? cps[2] : cps[f.type]
373
304
  [output_identifier(f.name), type_proc, i+=1]
374
305
  end
375
306
  @columns = cols.map{|c| c.first}
@@ -378,7 +309,7 @@ module Sequel
378
309
  yield_rows(r, cols){|h| s << h}
379
310
  yield s
380
311
  else
381
- yield_rows(r, cols, &block)
312
+ yield_rows(r, cols){|h| yield h}
382
313
  end
383
314
  end
384
315
  self
@@ -390,28 +321,6 @@ module Sequel
390
321
  super
391
322
  end
392
323
 
393
- # Insert a new value into this dataset
394
- def insert(*values)
395
- execute_dui(insert_sql(*values)){|c| return c.insert_id}
396
- end
397
-
398
- # Store the given type of prepared statement in the associated database
399
- # with the given name.
400
- def prepare(type, name=nil, *values)
401
- ps = to_prepared_statement(type, values)
402
- ps.extend(PreparedStatementMethods)
403
- if name
404
- ps.prepared_statement_name = name
405
- db.prepared_statements[name] = ps
406
- end
407
- ps
408
- end
409
-
410
- # Replace (update or insert) the matching row.
411
- def replace(*args)
412
- execute_dui(replace_sql(*args)){|c| return c.insert_id}
413
- end
414
-
415
324
  # Makes each yield arrays of rows, with each array containing the rows
416
325
  # for a given result set. Does not work with graphing. So you can submit
417
326
  # SQL with multiple statements and easily determine which statement
@@ -428,31 +337,25 @@ module Sequel
428
337
  ds
429
338
  end
430
339
 
431
- # Update the matching rows.
432
- def update(values={})
433
- execute_dui(update_sql(values)){|c| return c.affected_rows}
434
- end
435
-
436
340
  private
341
+
342
+ # Whether a tinyint field should be casted as an integer. By default,
343
+ # casts to integer if the field length is not 1. Can be overwritten
344
+ # to make tinyint casting dataset dependent.
345
+ def cast_tinyint_integer?(field)
346
+ field.length != 1
347
+ end
437
348
 
438
349
  # Set the :type option to :select if it hasn't been set.
439
350
  def execute(sql, opts={}, &block)
440
351
  super(sql, {:type=>:select}.merge(opts), &block)
441
352
  end
442
353
 
443
- # Set the :type option to :dui if it hasn't been set.
444
- def execute_dui(sql, opts={}, &block)
445
- super(sql, {:type=>:dui}.merge(opts), &block)
446
- end
447
-
448
354
  # Handle correct quoting of strings using ::MySQL.quote.
449
- def literal_string(v)
450
- "'#{::Mysql.quote(v)}'"
451
- end
452
-
453
- # Extend the dataset with the MySQL stored procedure methods.
454
- def prepare_extend_sproc(ds)
455
- ds.extend(StoredProcedureMethods)
355
+ def literal_string_append(sql, v)
356
+ sql << "'"
357
+ sql << ::Mysql.quote(v)
358
+ sql << "'"
456
359
  end
457
360
 
458
361
  # Yield each row of the given result set r with columns cols