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
@@ -13,6 +13,21 @@ module Sequel
13
13
  # ORA-03114: not connected to ORACLE
14
14
  CONNECTION_ERROR_CODES = [ 28, 1012, 3113, 3114 ]
15
15
 
16
+ ORACLE_TYPES = {
17
+ :blob=>lambda{|b| Sequel::SQL::Blob.new(b.read)},
18
+ :clob=>lambda{|b| b.read}
19
+ }
20
+
21
+ # Hash of conversion procs for this database.
22
+ attr_reader :conversion_procs
23
+
24
+ def initialize(opts={})
25
+ super
26
+ @autosequence = opts[:autosequence]
27
+ @primary_key_sequences = {}
28
+ @conversion_procs = ORACLE_TYPES.dup
29
+ end
30
+
16
31
  def connect(server)
17
32
  opts = server_opts(server)
18
33
  if opts[:database]
@@ -22,115 +37,407 @@ module Sequel
22
37
  dbname = opts[:host]
23
38
  end
24
39
  conn = OCI8.new(opts[:user], opts[:password], dbname, opts[:privilege])
40
+ conn.prefetch_rows = typecast_value_integer(opts[:prefetch_rows]) if opts[:prefetch_rows]
25
41
  conn.autocommit = true
26
42
  conn.non_blocking = true
43
+
44
+ # The ruby-oci8 gem which retrieves oracle columns with a type of
45
+ # DATE, TIMESTAMP, TIMESTAMP WITH TIME ZONE is complex based on the
46
+ # ruby version (1.9.2 or later) and Oracle version (9 or later)
47
+ # In the now standard case of 1.9.2 and Oracle 9 or later, the timezone
48
+ # is determined by the Oracle session timezone. Thus if the user
49
+ # requests Sequel provide UTC timezone to the application,
50
+ # we need to alter the session timezone to be UTC
51
+ if Sequel.application_timezone == :utc
52
+ conn.exec("ALTER SESSION SET TIME_ZONE='-00:00'")
53
+ end
54
+
55
+ class << conn
56
+ attr_reader :prepared_statements
57
+ end
58
+ conn.instance_variable_set(:@prepared_statements, {})
59
+
27
60
  conn
28
61
  end
29
-
30
- def dataset(opts = nil)
31
- Oracle::Dataset.new(self, opts)
62
+
63
+ def disconnect_connection(c)
64
+ c.logoff
65
+ rescue OCIInvalidHandle
66
+ nil
32
67
  end
33
68
 
34
- def schema_parse_table(table, opts={})
35
- ds = dataset
36
- ds.identifier_output_method = :downcase
37
- schema_and_table = "#{"#{quote_identifier(opts[:schema])}." if opts[:schema]}#{quote_identifier(table)}"
38
- table_schema = []
39
- metadata = transaction(opts){|conn| conn.describe_table(schema_and_table)}
40
- metadata.columns.each do |column|
41
- table_schema << [
42
- column.name.downcase.to_sym,
43
- {
44
- :type => column.data_type,
45
- :db_type => column.type_string.split(' ')[0],
46
- :type_string => column.type_string,
47
- :charset_form => column.charset_form,
48
- :char_used => column.char_used?,
49
- :char_size => column.char_size,
50
- :data_size => column.data_size,
51
- :precision => column.precision,
52
- :scale => column.scale,
53
- :fsprecision => column.fsprecision,
54
- :lfprecision => column.lfprecision,
55
- :allow_null => column.nullable?
56
- }
57
- ]
58
- end
59
- table_schema
69
+ def execute(sql, opts={}, &block)
70
+ _execute(nil, sql, opts, &block)
60
71
  end
72
+ alias do execute
73
+
74
+ def execute_insert(sql, opts={})
75
+ _execute(:insert, sql, opts)
76
+ end
77
+
78
+ private
61
79
 
62
- def execute(sql, opts={})
80
+ def _execute(type, sql, opts={}, &block)
63
81
  synchronize(opts[:server]) do |conn|
64
82
  begin
65
- r = log_yield(sql){conn.exec(sql)}
66
- yield(r) if block_given?
67
- r
68
- rescue OCIException => e
69
- raise_error(e, :disconnect=>CONNECTION_ERROR_CODES.include?(e.code))
83
+ return execute_prepared_statement(conn, type, sql, opts, &block) if sql.is_a?(Symbol)
84
+ if args = opts[:arguments]
85
+ r = conn.parse(sql)
86
+ args = cursor_bind_params(conn, r, args)
87
+ nr = log_yield(sql, args){r.exec}
88
+ r = nr unless block_given?
89
+ else
90
+ r = log_yield(sql){conn.exec(sql)}
91
+ end
92
+ if block_given?
93
+ begin
94
+ yield(r)
95
+ ensure
96
+ r.close
97
+ end
98
+ elsif type == :insert
99
+ last_insert_id(conn, opts)
100
+ else
101
+ r
102
+ end
103
+ rescue OCIException, RuntimeError => e
104
+ # ruby-oci8 is naughty and raises strings in some places
105
+ raise_error(e)
106
+ end
107
+ end
108
+ end
109
+
110
+ PS_TYPES = {'string'.freeze=>String, 'integer'.freeze=>Integer, 'float'.freeze=>Float,
111
+ 'decimal'.freeze=>Float, 'date'.freeze=>Time, 'datetime'.freeze=>Time,
112
+ 'time'.freeze=>Time, 'boolean'.freeze=>String, 'blob'.freeze=>OCI8::BLOB}
113
+ def cursor_bind_params(conn, cursor, args)
114
+ cursor
115
+ i = 0
116
+ args.map do |arg, type|
117
+ i += 1
118
+ case arg
119
+ when true
120
+ arg = 'Y'
121
+ when false
122
+ arg = 'N'
123
+ when BigDecimal
124
+ arg = arg.to_f
125
+ when ::Sequel::SQL::Blob
126
+ raise Error, "Sequel's oracle adapter does not currently support using a blob in a bound variable"
127
+ end
128
+ if t = PS_TYPES[type]
129
+ cursor.bind_param(i, arg, t)
130
+ else
131
+ cursor.bind_param(i, arg, arg.class)
132
+ end
133
+ arg
134
+ end
135
+ end
136
+
137
+ def connection_execute_method
138
+ :exec
139
+ end
140
+
141
+ def database_error_classes
142
+ [OCIException, RuntimeError]
143
+ end
144
+
145
+ def execute_prepared_statement(conn, type, name, opts)
146
+ ps = prepared_statement(name)
147
+ sql = ps.prepared_sql
148
+ if cursora = conn.prepared_statements[name]
149
+ cursor, cursor_sql = cursora
150
+ if cursor_sql != sql
151
+ cursor.close
152
+ cursor = nil
153
+ end
154
+ end
155
+ unless cursor
156
+ cursor = log_yield("PREPARE #{name}: #{sql}"){conn.parse(sql)}
157
+ conn.prepared_statements[name] = [cursor, sql]
158
+ end
159
+ args = cursor_bind_params(conn, cursor, opts[:arguments])
160
+ log_sql = "EXECUTE #{name}"
161
+ if ps.log_sql
162
+ log_sql << " ("
163
+ log_sql << sql
164
+ log_sql << ")"
165
+ end
166
+ r = log_yield(log_sql, args){cursor.exec}
167
+ if block_given?
168
+ yield(cursor)
169
+ elsif type == :insert
170
+ last_insert_id(conn, opts)
171
+ else
172
+ r
173
+ end
174
+ end
175
+
176
+ def last_insert_id(conn, opts)
177
+ unless sequence = opts[:sequence]
178
+ if t = opts[:table]
179
+ sequence = sequence_for_table(t)
180
+ end
181
+ end
182
+ if sequence
183
+ sql = "SELECT #{literal(sequence)}.currval FROM dual"
184
+ begin
185
+ cursor = log_yield(sql){conn.exec(sql)}
186
+ row = cursor.fetch
187
+ row.each{|v| return (v.to_i if v)}
188
+ rescue OCIError
189
+ nil
190
+ ensure
191
+ cursor.close if cursor
70
192
  end
71
193
  end
72
194
  end
73
- alias_method :do, :execute
74
195
 
75
- private
76
-
77
196
  def begin_transaction(conn, opts={})
78
197
  log_yield(TRANSACTION_BEGIN){conn.autocommit = false}
79
- conn
80
198
  end
81
199
 
82
200
  def commit_transaction(conn, opts={})
83
201
  log_yield(TRANSACTION_COMMIT){conn.commit}
84
202
  end
85
203
 
86
- def disconnect_connection(c)
87
- c.logoff
88
- rescue OCIInvalidHandle
89
- nil
204
+ def disconnect_error?(e, opts)
205
+ super || (e.is_a?(::OCIError) && CONNECTION_ERROR_CODES.include?(e.code))
90
206
  end
91
207
 
92
- def remove_transaction(conn)
93
- conn.autocommit = true if conn
208
+ def oracle_column_type(h)
209
+ case h[:oci8_type]
210
+ when :number
211
+ case h[:scale]
212
+ when 0
213
+ :integer
214
+ when -127
215
+ :float
216
+ else
217
+ :decimal
218
+ end
219
+ when :date
220
+ :datetime
221
+ else
222
+ schema_column_type(h[:db_type])
223
+ end
224
+ end
225
+
226
+ def remove_transaction(conn, committed)
227
+ conn.autocommit = true
228
+ ensure
94
229
  super
95
230
  end
96
231
 
97
232
  def rollback_transaction(conn, opts={})
98
233
  log_yield(TRANSACTION_ROLLBACK){conn.rollback}
99
234
  end
235
+
236
+ def schema_parse_table(table, opts={})
237
+ schema, table = schema_and_table(table)
238
+ schema ||= opts[:schema]
239
+ schema_and_table = if ds = opts[:dataset]
240
+ ds.literal(schema ? SQL::QualifiedIdentifier.new(schema, table) : SQL::Identifier.new(table))
241
+ else
242
+ "#{"#{quote_identifier(schema)}." if schema}#{quote_identifier(table)}"
243
+ end
244
+ table_schema = []
245
+ m = output_identifier_meth(ds)
246
+ im = input_identifier_meth(ds)
247
+
248
+ # Primary Keys
249
+ ds = metadata_dataset.from(:all_constraints___cons, :all_cons_columns___cols).
250
+ where(:cols__table_name=>im.call(table), :cons__constraint_type=>'P',
251
+ :cons__constraint_name=>:cols__constraint_name, :cons__owner=>:cols__owner)
252
+ ds = ds.where(:cons__owner=>im.call(schema)) if schema
253
+ pks = ds.select_map(:cols__column_name)
254
+
255
+ # Default values
256
+ defaults = begin
257
+ metadata_dataset.from(:user_tab_cols).
258
+ where(:table_name=>im.call(table)).
259
+ to_hash(:column_name, :data_default)
260
+ rescue DatabaseError
261
+ {}
262
+ end
263
+
264
+ metadata = synchronize(opts[:server]) do |conn|
265
+ begin
266
+ log_yield("Connection.describe_table"){conn.describe_table(schema_and_table)}
267
+ rescue OCIError => e
268
+ raise_error(e)
269
+ end
270
+ end
271
+ metadata.columns.each do |column|
272
+ h = {
273
+ :primary_key => pks.include?(column.name),
274
+ :default => defaults[column.name],
275
+ :oci8_type => column.data_type,
276
+ :db_type => column.type_string.split(' ')[0],
277
+ :type_string => column.type_string,
278
+ :charset_form => column.charset_form,
279
+ :char_used => column.char_used?,
280
+ :char_size => column.char_size,
281
+ :data_size => column.data_size,
282
+ :precision => column.precision,
283
+ :scale => column.scale,
284
+ :fsprecision => column.fsprecision,
285
+ :lfprecision => column.lfprecision,
286
+ :allow_null => column.nullable?
287
+ }
288
+ h[:type] = oracle_column_type(h)
289
+ table_schema << [m.call(column.name), h]
290
+ end
291
+ table_schema
292
+ end
100
293
  end
101
294
 
102
295
  class Dataset < Sequel::Dataset
103
296
  include DatasetMethods
104
297
 
298
+ Database::DatasetClass = self
299
+
300
+ PREPARED_ARG_PLACEHOLDER = ':'.freeze
301
+
302
+ # Oracle already supports named bind arguments, so use directly.
303
+ module ArgumentMapper
304
+ include Sequel::Dataset::ArgumentMapper
305
+
306
+ protected
307
+
308
+ # Return a hash with the same values as the given hash,
309
+ # but with the keys converted to strings.
310
+ def map_to_prepared_args(bind_vars)
311
+ prepared_args.map{|v, t| [bind_vars[v], t]}
312
+ end
313
+
314
+ private
315
+
316
+ # Oracle uses a : before the name of the argument for named
317
+ # arguments.
318
+ def prepared_arg(k)
319
+ y, type = k.to_s.split("__", 2)
320
+ prepared_args << [y.to_sym, type]
321
+ i = prepared_args.length
322
+ LiteralString.new(":#{i}")
323
+ end
324
+
325
+ # Always assume a prepared argument.
326
+ def prepared_arg?(k)
327
+ true
328
+ end
329
+ end
330
+
331
+ # Oracle prepared statement uses a new prepared statement each time
332
+ # it is called, but it does use the bind arguments.
333
+ module BindArgumentMethods
334
+ include ArgumentMapper
335
+
336
+ private
337
+
338
+ # Run execute_select on the database with the given SQL and the stored
339
+ # bind arguments.
340
+ def execute(sql, opts={}, &block)
341
+ super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
342
+ end
343
+
344
+ # Same as execute, explicit due to intricacies of alias and super.
345
+ def execute_dui(sql, opts={}, &block)
346
+ super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
347
+ end
348
+
349
+ # Same as execute, explicit due to intricacies of alias and super.
350
+ def execute_insert(sql, opts={}, &block)
351
+ super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
352
+ end
353
+ end
354
+
355
+ module PreparedStatementMethods
356
+ include BindArgumentMethods
357
+
358
+ private
359
+
360
+ # Execute the stored prepared statement name and the stored bind
361
+ # arguments instead of the SQL given.
362
+ def execute(sql, opts={}, &block)
363
+ super(prepared_statement_name, opts, &block)
364
+ end
365
+
366
+ # Same as execute, explicit due to intricacies of alias and super.
367
+ def execute_dui(sql, opts={}, &block)
368
+ super(prepared_statement_name, opts, &block)
369
+ end
370
+
371
+ # Same as execute, explicit due to intricacies of alias and super.
372
+ def execute_insert(sql, opts={}, &block)
373
+ super(prepared_statement_name, opts, &block)
374
+ end
375
+ end
376
+
377
+ # Execute the given type of statement with the hash of values.
378
+ def call(type, bind_vars={}, *values, &block)
379
+ ps = to_prepared_statement(type, values)
380
+ ps.extend(BindArgumentMethods)
381
+ ps.call(bind_vars, &block)
382
+ end
383
+
105
384
  def fetch_rows(sql)
106
385
  execute(sql) do |cursor|
107
- begin
108
- @columns = cursor.get_col_names.map{|c| output_identifier(c)}
109
- while r = cursor.fetch
110
- row = {}
111
- r.each_with_index {|v, i| row[@columns[i]] = v unless @columns[i] == :raw_rnum_}
112
- yield row
113
- end
114
- ensure
115
- cursor.close
386
+ offset = @opts[:offset]
387
+ rn = row_number_column
388
+ cps = db.conversion_procs
389
+ cols = columns = cursor.get_col_names.map{|c| output_identifier(c)}
390
+ metadata = cursor.column_metadata
391
+ cm = cols.zip(metadata).map{|c, m| [c, cps[m.data_type]]}
392
+ columns = cols.reject{|x| x == rn} if offset
393
+ @columns = columns
394
+ while r = cursor.fetch
395
+ row = {}
396
+ r.zip(cm).each{|v, (c, cp)| row[c] = ((v && cp) ? cp.call(v) : v)}
397
+ row.delete(rn) if offset
398
+ yield row
116
399
  end
117
400
  end
118
401
  self
119
402
  end
120
403
 
404
+ # Prepare the given type of query with the given name and store
405
+ # it in the database. Note that a new native prepared statement is
406
+ # created on each call to this prepared statement.
407
+ def prepare(type, name=nil, *values)
408
+ ps = to_prepared_statement(type, values)
409
+ ps.extend(PreparedStatementMethods)
410
+ if name
411
+ ps.prepared_statement_name = name
412
+ db.set_prepared_statement(name, ps)
413
+ end
414
+ ps
415
+ end
416
+
417
+ # Oracle requires type specifiers for placeholders, at least
418
+ # if you ever want to use a nil/NULL value as the value for
419
+ # the placeholder.
420
+ def requires_placeholder_type_specifiers?
421
+ true
422
+ end
423
+
121
424
  private
122
425
 
123
- def literal_other(v)
426
+ def literal_other_append(sql, v)
124
427
  case v
125
428
  when OraDate
126
- literal(Sequel.database_to_application_timestamp(v))
429
+ literal_append(sql, db.to_application_timestamp(v))
127
430
  when OCI8::CLOB
128
431
  v.rewind
129
- literal(v.read)
432
+ literal_append(sql, v.read)
130
433
  else
131
434
  super
132
435
  end
133
436
  end
437
+
438
+ def prepared_arg_placeholder
439
+ PREPARED_ARG_PLACEHOLDER
440
+ end
134
441
  end
135
442
  end
136
443
  end