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
@@ -32,47 +32,102 @@ module Sequel
32
32
  DATABASE_SETUP = {:postgresql=>proc do |db|
33
33
  Sequel.ts_require 'adapters/jdbc/postgresql'
34
34
  db.extend(Sequel::JDBC::Postgres::DatabaseMethods)
35
+ db.dataset_class = Sequel::JDBC::Postgres::Dataset
35
36
  JDBC.load_gem('postgres')
36
37
  org.postgresql.Driver
37
38
  end,
38
39
  :mysql=>proc do |db|
39
40
  Sequel.ts_require 'adapters/jdbc/mysql'
40
41
  db.extend(Sequel::JDBC::MySQL::DatabaseMethods)
42
+ db.extend_datasets Sequel::MySQL::DatasetMethods
41
43
  JDBC.load_gem('mysql')
42
44
  com.mysql.jdbc.Driver
43
45
  end,
44
46
  :sqlite=>proc do |db|
45
47
  Sequel.ts_require 'adapters/jdbc/sqlite'
46
48
  db.extend(Sequel::JDBC::SQLite::DatabaseMethods)
49
+ db.extend_datasets Sequel::SQLite::DatasetMethods
50
+ db.set_integer_booleans
47
51
  JDBC.load_gem('sqlite3')
48
52
  org.sqlite.JDBC
49
53
  end,
50
54
  :oracle=>proc do |db|
51
55
  Sequel.ts_require 'adapters/jdbc/oracle'
52
56
  db.extend(Sequel::JDBC::Oracle::DatabaseMethods)
57
+ db.dataset_class = Sequel::JDBC::Oracle::Dataset
53
58
  Java::oracle.jdbc.driver.OracleDriver
54
59
  end,
55
60
  :sqlserver=>proc do |db|
56
- Sequel.ts_require 'adapters/jdbc/mssql'
57
- db.extend(Sequel::JDBC::MSSQL::DatabaseMethods)
61
+ Sequel.ts_require 'adapters/jdbc/sqlserver'
62
+ db.extend(Sequel::JDBC::SQLServer::DatabaseMethods)
63
+ db.extend_datasets Sequel::MSSQL::DatasetMethods
64
+ db.send(:set_mssql_unicode_strings)
58
65
  com.microsoft.sqlserver.jdbc.SQLServerDriver
59
66
  end,
60
67
  :jtds=>proc do |db|
61
- Sequel.ts_require 'adapters/jdbc/mssql'
62
- db.extend(Sequel::JDBC::MSSQL::DatabaseMethods)
68
+ Sequel.ts_require 'adapters/jdbc/jtds'
69
+ db.extend(Sequel::JDBC::JTDS::DatabaseMethods)
70
+ db.dataset_class = Sequel::JDBC::JTDS::Dataset
71
+ db.send(:set_mssql_unicode_strings)
63
72
  JDBC.load_gem('jtds')
64
73
  Java::net.sourceforge.jtds.jdbc.Driver
65
74
  end,
66
75
  :h2=>proc do |db|
67
76
  Sequel.ts_require 'adapters/jdbc/h2'
68
77
  db.extend(Sequel::JDBC::H2::DatabaseMethods)
78
+ db.dataset_class = Sequel::JDBC::H2::Dataset
69
79
  JDBC.load_gem('h2')
70
80
  org.h2.Driver
71
81
  end,
82
+ :hsqldb=>proc do |db|
83
+ Sequel.ts_require 'adapters/jdbc/hsqldb'
84
+ db.extend(Sequel::JDBC::HSQLDB::DatabaseMethods)
85
+ db.dataset_class = Sequel::JDBC::HSQLDB::Dataset
86
+ # Current gem is 1.8.1.3, but Sequel supports 2.2.5
87
+ org.hsqldb.jdbcDriver
88
+ end,
89
+ :derby=>proc do |db|
90
+ Sequel.ts_require 'adapters/jdbc/derby'
91
+ db.extend(Sequel::JDBC::Derby::DatabaseMethods)
92
+ db.dataset_class = Sequel::JDBC::Derby::Dataset
93
+ JDBC.load_gem('derby')
94
+ org.apache.derby.jdbc.EmbeddedDriver
95
+ end,
72
96
  :as400=>proc do |db|
73
97
  Sequel.ts_require 'adapters/jdbc/as400'
74
98
  db.extend(Sequel::JDBC::AS400::DatabaseMethods)
99
+ db.dataset_class = Sequel::JDBC::AS400::Dataset
75
100
  com.ibm.as400.access.AS400JDBCDriver
101
+ end,
102
+ :"informix-sqli"=>proc do |db|
103
+ Sequel.ts_require 'adapters/jdbc/informix'
104
+ db.extend(Sequel::JDBC::Informix::DatabaseMethods)
105
+ db.extend_datasets Sequel::Informix::DatasetMethods
106
+ com.informix.jdbc.IfxDriver
107
+ end,
108
+ :db2=>proc do |db|
109
+ Sequel.ts_require 'adapters/jdbc/db2'
110
+ db.extend(Sequel::JDBC::DB2::DatabaseMethods)
111
+ db.dataset_class = Sequel::JDBC::DB2::Dataset
112
+ com.ibm.db2.jcc.DB2Driver
113
+ end,
114
+ :firebirdsql=>proc do |db|
115
+ Sequel.ts_require 'adapters/jdbc/firebird'
116
+ db.extend(Sequel::JDBC::Firebird::DatabaseMethods)
117
+ db.extend_datasets Sequel::Firebird::DatasetMethods
118
+ org.firebirdsql.jdbc.FBDriver
119
+ end,
120
+ :jdbcprogress=>proc do |db|
121
+ Sequel.ts_require 'adapters/jdbc/progress'
122
+ db.extend(Sequel::JDBC::Progress::DatabaseMethods)
123
+ db.extend_datasets Sequel::Progress::DatasetMethods
124
+ com.progress.sql.jdbc.JdbcProgressDriver
125
+ end,
126
+ :cubrid=>proc do |db|
127
+ Sequel.ts_require 'adapters/jdbc/cubrid'
128
+ db.extend(Sequel::JDBC::Cubrid::DatabaseMethods)
129
+ db.extend_datasets Sequel::Cubrid::DatasetMethods
130
+ Java::cubrid.jdbc.driver.CUBRIDDriver
76
131
  end
77
132
  }
78
133
 
@@ -101,13 +156,15 @@ module Sequel
101
156
  # True by default, can be set to false to roughly double performance when
102
157
  # fetching rows.
103
158
  attr_accessor :convert_types
104
-
159
+
105
160
  # Call the DATABASE_SETUP proc directly after initialization,
106
161
  # so the object always uses sub adapter specific code. Also,
107
162
  # raise an error immediately if the connection doesn't have a
108
163
  # uri, since JDBC requires one.
109
164
  def initialize(opts)
110
165
  super
166
+ @connection_prepared_statements = {}
167
+ @connection_prepared_statements_mutex = Mutex.new
111
168
  @convert_types = typecast_value_boolean(@opts.fetch(:convert_types, true))
112
169
  raise(Error, "No connection string specified") unless uri
113
170
 
@@ -158,8 +215,9 @@ module Sequel
158
215
  args = [uri(opts)]
159
216
  args.concat([opts[:user], opts[:password]]) if opts[:user] && opts[:password]
160
217
  begin
218
+ JavaSQL::DriverManager.setLoginTimeout(opts[:login_timeout]) if opts[:login_timeout]
161
219
  JavaSQL::DriverManager.getConnection(*args)
162
- rescue => e
220
+ rescue JavaSQL::SQLException, NativeException, StandardError => e
163
221
  raise e unless driver
164
222
  # If the DriverManager can't get the connection - use the connect
165
223
  # method of the driver. (This happens under Tomcat for instance)
@@ -170,8 +228,10 @@ module Sequel
170
228
  end
171
229
  opts[:jdbc_properties].each{|k,v| props.setProperty(k.to_s, v)} if opts[:jdbc_properties]
172
230
  begin
173
- driver.new.connect(args[0], props)
174
- rescue => e2
231
+ c = driver.new.connect(args[0], props)
232
+ raise(Sequel::DatabaseError, 'driver.new.connect returned nil: probably bad JDBC connection string') unless c
233
+ c
234
+ rescue JavaSQL::SQLException, NativeException, StandardError => e2
175
235
  e.message << "\n#{e2.class.name}: #{e2.message}"
176
236
  raise e
177
237
  end
@@ -179,10 +239,11 @@ module Sequel
179
239
  end
180
240
  setup_connection(conn)
181
241
  end
182
-
183
- # Return instances of JDBC::Dataset with the given opts.
184
- def dataset(opts = nil)
185
- JDBC::Dataset.new(self, opts)
242
+
243
+ # Close given adapter connections, and delete any related prepared statements.
244
+ def disconnect_connection(c)
245
+ @connection_prepared_statements_mutex.synchronize{@connection_prepared_statements.delete(c)}
246
+ c.close
186
247
  end
187
248
 
188
249
  # Execute the given SQL. If a block is given, if should be a SELECT
@@ -199,13 +260,7 @@ module Sequel
199
260
  when :ddl
200
261
  log_yield(sql){stmt.execute(sql)}
201
262
  when :insert
202
- log_yield(sql) do
203
- if requires_return_generated_keys?
204
- stmt.executeUpdate(sql, JavaSQL::Statement.RETURN_GENERATED_KEYS)
205
- else
206
- stmt.executeUpdate(sql)
207
- end
208
- end
263
+ log_yield(sql){execute_statement_insert(stmt, sql)}
209
264
  last_insert_id(conn, opts.merge(:stmt=>stmt))
210
265
  else
211
266
  log_yield(sql){stmt.executeUpdate(sql)}
@@ -215,7 +270,7 @@ module Sequel
215
270
  end
216
271
  end
217
272
  alias execute_dui execute
218
-
273
+
219
274
  # Execute the given DDL SQL, which should not return any
220
275
  # values or rows.
221
276
  def execute_ddl(sql, opts={})
@@ -246,12 +301,14 @@ module Sequel
246
301
  indexes
247
302
  end
248
303
 
304
+ # Whether or not JNDI is being used for this connection.
305
+ def jndi?
306
+ !!(uri =~ JNDI_URI_REGEXP)
307
+ end
308
+
249
309
  # All tables in this database
250
310
  def tables(opts={})
251
- ts = []
252
- m = output_identifier_meth
253
- metadata(:getTables, nil, nil, nil, ['TABLE'].to_java(:string)){|h| ts << m.call(h[:table_name])}
254
- ts
311
+ get_tables('TABLE', opts)
255
312
  end
256
313
 
257
314
  # The uri for this connection. You can specify the uri
@@ -264,18 +321,29 @@ module Sequel
264
321
  ur =~ /^\Ajdbc:/ ? ur : "jdbc:#{ur}"
265
322
  end
266
323
 
267
- # Whether or not JNDI is being used for this connection.
268
- def jndi?
269
- !!(uri =~ JNDI_URI_REGEXP)
324
+ # All views in this database
325
+ def views(opts={})
326
+ get_tables('VIEW', opts)
270
327
  end
271
-
328
+
272
329
  private
273
330
 
274
- # Close given adapter connections
275
- def disconnect_connection(c)
276
- c.close
331
+ # Yield the native prepared statements hash for the given connection
332
+ # to the block in a thread-safe manner.
333
+ def cps_sync(conn, &block)
334
+ @connection_prepared_statements_mutex.synchronize{yield(@connection_prepared_statements[conn] ||= {})}
277
335
  end
278
-
336
+
337
+ def database_error_classes
338
+ [NativeException]
339
+ end
340
+
341
+ # Raise a disconnect error if the SQL state of the cause of the exception indicates so.
342
+ def disconnect_error?(exception, opts)
343
+ cause = exception.respond_to?(:cause) ? exception.cause : exception
344
+ super || (cause.respond_to?(:getSQLState) && cause.getSQLState =~ /^08/)
345
+ end
346
+
279
347
  # Execute the prepared statement. If the provided name is a
280
348
  # dataset, use that as the prepared statement, otherwise use
281
349
  # it as a key to look it up in the prepared_statements hash.
@@ -289,20 +357,25 @@ module Sequel
289
357
  ps = name
290
358
  name = ps.prepared_statement_name
291
359
  else
292
- ps = prepared_statements[name]
360
+ ps = prepared_statement(name)
293
361
  end
294
362
  sql = ps.prepared_sql
295
363
  synchronize(opts[:server]) do |conn|
296
- if name and cps = conn.prepared_statements[name] and cps[0] == sql
364
+ if name and cps = cps_sync(conn){|cpsh| cpsh[name]} and cps[0] == sql
297
365
  cps = cps[1]
298
366
  else
299
- log_yield("Closing #{name}"){cps[1].close} if cps
300
- cps = log_yield("Preparing#{" #{name}:" if name} #{sql}"){conn.prepareStatement(sql)}
301
- conn.prepared_statements[name] = [sql, cps] if name
367
+ log_yield("CLOSE #{name}"){cps[1].close} if cps
368
+ cps = log_yield("PREPARE#{" #{name}:" if name} #{sql}"){prepare_jdbc_statement(conn, sql, opts)}
369
+ cps_sync(conn){|cpsh| cpsh[name] = [sql, cps]} if name
302
370
  end
303
371
  i = 0
304
372
  args.each{|arg| set_ps_arg(cps, arg, i+=1)}
305
- msg = "Executing#{" #{name}" if name}"
373
+ msg = "EXECUTE#{" #{name}" if name}"
374
+ if ps.log_sql
375
+ msg << " ("
376
+ msg << sql
377
+ msg << ")"
378
+ end
306
379
  begin
307
380
  if block_given?
308
381
  yield log_yield(msg, args){cps.executeQuery}
@@ -311,8 +384,8 @@ module Sequel
311
384
  when :ddl
312
385
  log_yield(msg, args){cps.execute}
313
386
  when :insert
314
- log_yield(msg, args){cps.executeUpdate}
315
- last_insert_id(conn, opts.merge(:prepared=>true))
387
+ log_yield(msg, args){execute_prepared_statement_insert(cps)}
388
+ last_insert_id(conn, opts.merge(:prepared=>true, :stmt=>cps))
316
389
  else
317
390
  log_yield(msg, args){cps.executeUpdate}
318
391
  end
@@ -325,6 +398,22 @@ module Sequel
325
398
  end
326
399
  end
327
400
 
401
+ # Execute the prepared insert statement
402
+ def execute_prepared_statement_insert(stmt)
403
+ stmt.executeUpdate
404
+ end
405
+
406
+ # Execute the insert SQL using the statement
407
+ def execute_statement_insert(stmt, sql)
408
+ stmt.executeUpdate(sql)
409
+ end
410
+
411
+ # Gets the connection from JNDI.
412
+ def get_connection_from_jndi
413
+ jndi_name = JNDI_URI_REGEXP.match(uri)[1]
414
+ JavaxNaming::InitialContext.new.lookup(jndi_name).connection
415
+ end
416
+
328
417
  # Gets the JDBC connection uri from the JNDI resource.
329
418
  def get_uri_from_jndi
330
419
  conn = get_connection_from_jndi
@@ -333,12 +422,14 @@ module Sequel
333
422
  conn.close if conn
334
423
  end
335
424
 
336
- # Gets the connection from JNDI.
337
- def get_connection_from_jndi
338
- jndi_name = JNDI_URI_REGEXP.match(uri)[1]
339
- JavaxNaming::InitialContext.new.lookup(jndi_name).connection
425
+ # Backbone of the tables and views support.
426
+ def get_tables(type, opts)
427
+ ts = []
428
+ m = output_identifier_meth
429
+ metadata(:getTables, nil, nil, nil, [type].to_java(:string)){|h| ts << m.call(h[:table_name])}
430
+ ts
340
431
  end
341
-
432
+
342
433
  # Support Date objects used in bound variables
343
434
  def java_sql_date(date)
344
435
  java.sql.Date.new(Time.local(date.year, date.month, date.day).to_i * 1000)
@@ -354,7 +445,8 @@ module Sequel
354
445
  # Support fractional seconds for Time objects used in bound variables
355
446
  def java_sql_timestamp(time)
356
447
  ts = java.sql.Timestamp.new(time.to_i * 1000)
357
- ts.setNanos(RUBY_VERSION >= '1.9.0' ? time.nsec : time.usec * 1000)
448
+ # Work around jruby 1.6 ruby 1.9 mode bug
449
+ ts.setNanos((RUBY_VERSION >= '1.9.0' && time.nsec != 0) ? time.nsec : time.usec * 1000)
358
450
  ts
359
451
  end
360
452
 
@@ -383,10 +475,9 @@ module Sequel
383
475
  end
384
476
  end
385
477
 
386
- # Treat SQLExceptions with a "Connection Error" SQLState as disconnects
387
- def raise_error(exception, opts={})
388
- cause = exception.respond_to?(:cause) ? exception.cause : exception
389
- super(exception, {:disconnect => cause.respond_to?(:getSQLState) && cause.getSQLState =~ /^08/}.merge(opts))
478
+ # Created a JDBC prepared statement on the connection with the given SQL.
479
+ def prepare_jdbc_statement(conn, sql, opts)
480
+ conn.prepareStatement(sql)
390
481
  end
391
482
 
392
483
  # Java being java, you need to specify the type of each argument
@@ -405,8 +496,8 @@ module Sequel
405
496
  cps.setDouble(i, arg)
406
497
  when TrueClass, FalseClass
407
498
  cps.setBoolean(i, arg)
408
- when nil
409
- cps.setNull(i, JavaSQL::Types::NULL)
499
+ when NilClass
500
+ set_ps_arg_nil(cps, i)
410
501
  when DateTime
411
502
  cps.setTimestamp(i, java_sql_datetime(arg))
412
503
  when Date
@@ -421,22 +512,22 @@ module Sequel
421
512
  cps.setObject(i, arg)
422
513
  end
423
514
  end
515
+
516
+ # Use setString with a nil value by default, but this doesn't work on all subadapters.
517
+ def set_ps_arg_nil(cps, i)
518
+ cps.setString(i, nil)
519
+ end
424
520
 
425
- # Add a prepared_statements accessor to the connection,
426
- # and set it to an empty hash. This is used to store
427
- # adapter specific prepared statements.
521
+ # Return the connection. Used to do configuration on the
522
+ # connection object before adding it to the connection pool.
428
523
  def setup_connection(conn)
429
- class << conn
430
- attr_accessor :prepared_statements
431
- end
432
- conn.prepared_statements = {}
433
524
  conn
434
525
  end
435
526
 
436
527
  # Parse the table schema for the given table.
437
528
  def schema_parse_table(table, opts={})
438
- m = output_identifier_meth
439
- im = input_identifier_meth
529
+ m = output_identifier_meth(opts[:dataset])
530
+ im = input_identifier_meth(opts[:dataset])
440
531
  ds = dataset
441
532
  schema, table = schema_and_table(table)
442
533
  schema ||= opts[:schema]
@@ -444,9 +535,11 @@ module Sequel
444
535
  table = im.call(table)
445
536
  pks, ts = [], []
446
537
  metadata(:getPrimaryKeys, nil, schema, table) do |h|
538
+ next if schema_parse_table_skip?(h, schema)
447
539
  pks << h[:column_name]
448
540
  end
449
541
  metadata(:getColumns, nil, schema, table, nil) do |h|
542
+ next if schema_parse_table_skip?(h, schema)
450
543
  s = {:type=>schema_column_type(h[:type_name]), :db_type=>h[:type_name], :default=>(h[:column_def] == '' ? nil : h[:column_def]), :allow_null=>(h[:nullable] != 0), :primary_key=>pks.include?(h[:column_name]), :column_size=>h[:column_size], :scale=>h[:decimal_digits]}
451
544
  if s[:db_type] =~ DECIMAL_TYPE_RE && s[:scale] == 0
452
545
  s[:type] = :integer
@@ -456,6 +549,12 @@ module Sequel
456
549
  ts
457
550
  end
458
551
 
552
+ # Whether schema_parse_table should skip the given row when
553
+ # parsing the schema.
554
+ def schema_parse_table_skip?(h, schema)
555
+ h[:table_schem] == 'INFORMATION_SCHEMA'
556
+ end
557
+
459
558
  # Yield a new statement object, and ensure that it is closed before returning.
460
559
  def statement(conn)
461
560
  stmt = conn.createStatement
@@ -465,17 +564,12 @@ module Sequel
465
564
  ensure
466
565
  stmt.close if stmt
467
566
  end
468
-
469
- # This method determines whether or not to add
470
- # Statement.RETURN_GENERATED_KEYS as an argument when inserting rows.
471
- # Sub-adapters that require this should override this method.
472
- def requires_return_generated_keys?
473
- false
474
- end
475
567
  end
476
568
 
477
569
  class Dataset < Sequel::Dataset
478
570
  include StoredProcedures
571
+
572
+ Database::DatasetClass = self
479
573
 
480
574
  # Use JDBC PreparedStatements instead of emulated ones. Statements
481
575
  # created using #prepare are cached at the connection level to allow
@@ -489,17 +583,17 @@ module Sequel
489
583
  # Execute the prepared SQL using the stored type and
490
584
  # arguments derived from the hash passed to call.
491
585
  def execute(sql, opts={}, &block)
492
- super(self, {:arguments=>bind_arguments, :type=>sql_query_type}.merge(opts), &block)
586
+ super(self, {:arguments=>bind_arguments}.merge(opts), &block)
493
587
  end
494
588
 
495
589
  # Same as execute, explicit due to intricacies of alias and super.
496
590
  def execute_dui(sql, opts={}, &block)
497
- super(self, {:arguments=>bind_arguments, :type=>sql_query_type}.merge(opts), &block)
591
+ super(self, {:arguments=>bind_arguments}.merge(opts), &block)
498
592
  end
499
593
 
500
594
  # Same as execute, explicit due to intricacies of alias and super.
501
595
  def execute_insert(sql, opts={}, &block)
502
- super(self, {:arguments=>bind_arguments, :type=>sql_query_type}.merge(opts), &block)
596
+ super(self, {:arguments=>bind_arguments, :type=>:insert}.merge(opts), &block)
503
597
  end
504
598
  end
505
599
 
@@ -512,17 +606,17 @@ module Sequel
512
606
 
513
607
  # Execute the database stored procedure with the stored arguments.
514
608
  def execute(sql, opts={}, &block)
515
- super(@sproc_name, {:args=>@sproc_args, :sproc=>true, :type=>sql_query_type}.merge(opts), &block)
609
+ super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
516
610
  end
517
611
 
518
612
  # Same as execute, explicit due to intricacies of alias and super.
519
613
  def execute_dui(sql, opts={}, &block)
520
- super(@sproc_name, {:args=>@sproc_args, :sproc=>true, :type=>sql_query_type}.merge(opts), &block)
614
+ super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
521
615
  end
522
616
 
523
617
  # Same as execute, explicit due to intricacies of alias and super.
524
618
  def execute_insert(sql, opts={}, &block)
525
- super(@sproc_name, {:args=>@sproc_args, :sproc=>true, :type=>sql_query_type}.merge(opts), &block)
619
+ super(@sproc_name, {:args=>@sproc_args, :sproc=>true, :type=>:insert}.merge(opts), &block)
526
620
  end
527
621
  end
528
622
 
@@ -530,13 +624,7 @@ module Sequel
530
624
  # Uses the database's setting by default, can be set to false to roughly
531
625
  # double performance when fetching rows.
532
626
  attr_accessor :convert_types
533
-
534
- # Use the convert_types default setting from the database
535
- def initialize(db, opts={})
536
- @convert_types = db.convert_types
537
- super
538
- end
539
-
627
+
540
628
  # Correctly return rows from the database and return them as hashes.
541
629
  def fetch_rows(sql, &block)
542
630
  execute(sql){|result| process_result_set(result, &block)}
@@ -550,32 +638,88 @@ module Sequel
550
638
  ps.extend(PreparedStatementMethods)
551
639
  if name
552
640
  ps.prepared_statement_name = name
553
- db.prepared_statements[name] = ps
641
+ db.set_prepared_statement(name, ps)
554
642
  end
555
643
  ps
556
644
  end
557
645
 
558
646
  private
559
-
560
- # Convert the type. Used for converting Java types to ruby types.
561
- def convert_type(v)
647
+
648
+ # Cache Java class constants to speed up lookups
649
+ JAVA_SQL_TIMESTAMP = Java::JavaSQL::Timestamp
650
+ JAVA_SQL_TIME = Java::JavaSQL::Time
651
+ JAVA_SQL_DATE = Java::JavaSQL::Date
652
+ JAVA_SQL_BLOB = Java::JavaSQL::Blob
653
+ JAVA_SQL_CLOB = Java::JavaSQL::Clob
654
+ JAVA_BUFFERED_READER = Java::JavaIo::BufferedReader
655
+ JAVA_BIG_DECIMAL = Java::JavaMath::BigDecimal
656
+ JAVA_BYTE_ARRAY = Java::byte[]
657
+ JAVA_UUID = Java::JavaUtil::UUID
658
+
659
+ # Handle type conversions for common Java types.
660
+ class TYPE_TRANSLATOR
661
+ LF = "\n".freeze
662
+ def time(v) Sequel.string_to_time("#{v.to_string}.#{sprintf('%03i', v.getTime.divmod(1000).last)}") end
663
+ def date(v) Date.civil(v.getYear + 1900, v.getMonth + 1, v.getDate) end
664
+ def decimal(v) BigDecimal.new(v.to_string) end
665
+ def byte_array(v) Sequel::SQL::Blob.new(String.from_java_bytes(v)) end
666
+ def blob(v) Sequel::SQL::Blob.new(String.from_java_bytes(v.getBytes(1, v.length))) end
667
+ def clob(v) v.getSubString(1, v.length) end
668
+ def buffered_reader(v)
669
+ lines = ""
670
+ c = false
671
+ while(line = v.read_line) do
672
+ lines << LF if c
673
+ lines << line
674
+ c ||= true
675
+ end
676
+ lines
677
+ end
678
+ def uuid(v) v.to_string end
679
+ end
680
+ TYPE_TRANSLATOR_INSTANCE = tt = TYPE_TRANSLATOR.new
681
+
682
+ # Cache type translator methods so that duplicate Method
683
+ # objects are not created.
684
+ DECIMAL_METHOD = tt.method(:decimal)
685
+ TIME_METHOD = tt.method(:time)
686
+ DATE_METHOD = tt.method(:date)
687
+ BUFFERED_READER_METHOD = tt.method(:buffered_reader)
688
+ BYTE_ARRAY_METHOD = tt.method(:byte_array)
689
+ BLOB_METHOD = tt.method(:blob)
690
+ CLOB_METHOD = tt.method(:clob)
691
+ UUID_METHOD = tt.method(:uuid)
692
+
693
+ # Convert the given Java timestamp to an instance of Sequel.datetime_class.
694
+ def convert_type_timestamp(v)
695
+ db.to_application_timestamp([v.getYear + 1900, v.getMonth + 1, v.getDate, v.getHours, v.getMinutes, v.getSeconds, v.getNanos])
696
+ end
697
+
698
+ # Return a callable object that will convert any value of <tt>v</tt>'s
699
+ # class to a ruby object. If no callable object can handle <tt>v</tt>'s
700
+ # class, return false so that the negative lookup is cached.
701
+ def convert_type_proc(v)
562
702
  case v
563
- when Java::JavaSQL::Timestamp, Java::JavaSQL::Time
564
- Sequel.database_to_application_timestamp(v.to_string)
565
- when Java::JavaSQL::Date
566
- Sequel.string_to_date(v.to_string)
567
- when Java::JavaIo::BufferedReader
568
- lines = []
569
- while(line = v.read_line) do lines << line end
570
- lines.join("\n")
571
- when Java::JavaMath::BigDecimal
572
- BigDecimal.new(v.to_string)
573
- when Java::byte[]
574
- Sequel::SQL::Blob.new(String.from_java_bytes(v))
575
- when Java::JavaSQL::Blob
576
- convert_type(v.getBytes(1, v.length))
703
+ when JAVA_BIG_DECIMAL
704
+ DECIMAL_METHOD
705
+ when JAVA_SQL_TIMESTAMP
706
+ method(:convert_type_timestamp)
707
+ when JAVA_SQL_TIME
708
+ TIME_METHOD
709
+ when JAVA_SQL_DATE
710
+ DATE_METHOD
711
+ when JAVA_BUFFERED_READER
712
+ BUFFERED_READER_METHOD
713
+ when JAVA_BYTE_ARRAY
714
+ BYTE_ARRAY_METHOD
715
+ when JAVA_SQL_BLOB
716
+ BLOB_METHOD
717
+ when JAVA_SQL_CLOB
718
+ CLOB_METHOD
719
+ when JAVA_UUID
720
+ UUID_METHOD
577
721
  else
578
- v
722
+ false
579
723
  end
580
724
  end
581
725
 
@@ -586,32 +730,82 @@ module Sequel
586
730
 
587
731
  # Split out from fetch rows to allow processing of JDBC result sets
588
732
  # that don't come from issuing an SQL string.
589
- def process_result_set(result)
733
+ def process_result_set(result, &block)
590
734
  # get column names
591
735
  meta = result.getMetaData
592
736
  cols = []
593
737
  i = 0
594
738
  meta.getColumnCount.times{cols << [output_identifier(meta.getColumnLabel(i+=1)), i]}
595
- @columns = cols.map{|c| c.at(0)}
596
- blk = result_set_object_getter
597
- # get rows
739
+ columns = cols.map{|c| c.at(0)}
740
+ if opts[:offset] && offset_returns_row_number_column?
741
+ rn = row_number_column
742
+ columns.delete(rn)
743
+ end
744
+ @columns = columns
745
+ ct = @convert_types
746
+ if (ct.nil? ? db.convert_types : ct)
747
+ cols.each{|c| c << nil}
748
+ process_result_set_convert(cols, result, rn, &block)
749
+ else
750
+ process_result_set_no_convert(cols, result, rn, &block)
751
+ end
752
+ ensure
753
+ result.close
754
+ end
755
+
756
+ # Use conversion procs to convert data retrieved
757
+ # from the database. This has been optimized, the algorithm it uses
758
+ # is roughly, for each column value in each row:
759
+ # * check if the value is truthy (not false/nil)
760
+ # * if not truthy, return object
761
+ # * otherwise, see if a conversion method exists for
762
+ # the column. All columns start with a nil conversion proc,
763
+ # since unlike other adapters, Sequel doesn't get the type of
764
+ # the column when parsing the column metadata.
765
+ # * if a conversion proc is not false/nil, call it with the object
766
+ # and return the result.
767
+ # * if a conversion proc has already been looked up and doesn't
768
+ # exist (false value), return object.
769
+ # * if a conversion proc hasn't been looked up yet (nil value),
770
+ # call convert_type_proc to get the conversion method. Cache
771
+ # the result of as the column's conversion proc to speed up
772
+ # later processing. If the conversion proc exists, call it
773
+ # and return the result, otherwise, return the object.
774
+ def process_result_set_convert(cols, result, rn)
598
775
  while result.next
599
776
  row = {}
600
- cols.each do |n, i|
601
- row[n] = blk.call(result, n, i)
777
+ cols.each do |n, i, p|
778
+ v = result.getObject(i)
779
+ row[n] = if v
780
+ if p
781
+ p.call(v)
782
+ elsif p.nil?
783
+ cols[i-1][2] = p = convert_type_proc(v)
784
+ if p
785
+ p.call(v)
786
+ else
787
+ v
788
+ end
789
+ else
790
+ v
791
+ end
792
+ else
793
+ v
794
+ end
602
795
  end
796
+ row.delete(rn) if rn
603
797
  yield row
604
798
  end
605
799
  end
606
800
 
607
- # Proc that takes the ResultSet and an integer field number and returns
608
- # the object for that field. Respects the @convert_types setting to
609
- # convert Java types to ruby types.
610
- def result_set_object_getter
611
- if @convert_types
612
- lambda {|result, n, i| convert_type(result.getObject(i))}
613
- else
614
- lambda {|result, n, i| result.getObject(i)}
801
+ # Yield rows without calling any conversion procs. This
802
+ # may yield Java values and not ruby values.
803
+ def process_result_set_no_convert(cols, result, rn)
804
+ while result.next
805
+ row = {}
806
+ cols.each{|n, i| row[n] = result.getObject(i)}
807
+ row.delete(rn) if rn
808
+ yield row
615
809
  end
616
810
  end
617
811
  end