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