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
@@ -0,0 +1,453 @@
1
+ require 'ibm_db'
2
+ Sequel.require 'adapters/shared/db2'
3
+
4
+ module Sequel
5
+
6
+ module IBMDB
7
+ @convert_smallint_to_bool = true
8
+
9
+ class << self
10
+ # Whether to convert smallint values to bool, true by default.
11
+ # Can also be overridden per dataset.
12
+ attr_accessor :convert_smallint_to_bool
13
+ end
14
+
15
+ tt = Class.new do
16
+ def boolean(s) !s.to_i.zero? end
17
+ def int(s) s.to_i end
18
+ end.new
19
+
20
+ # Hash holding type translation methods, used by Dataset#fetch_rows.
21
+ DB2_TYPES = {
22
+ :boolean => tt.method(:boolean),
23
+ :int => tt.method(:int),
24
+ :blob => ::Sequel::SQL::Blob.method(:new),
25
+ :time => ::Sequel.method(:string_to_time),
26
+ :date => ::Sequel.method(:string_to_date)
27
+ }
28
+ DB2_TYPES[:clob] = DB2_TYPES[:blob]
29
+
30
+ # Wraps an underlying connection to DB2 using IBM_DB.
31
+ class Connection
32
+ # A hash with prepared statement name symbol keys, where each value is
33
+ # a two element array with an sql string and cached Statement value.
34
+ attr_accessor :prepared_statements
35
+
36
+ # Error class for exceptions raised by the connection.
37
+ class Error < StandardError
38
+ end
39
+
40
+ # Create the underlying IBM_DB connection.
41
+ def initialize(connection_string)
42
+ @conn = IBM_DB.connect(connection_string, '', '')
43
+ self.autocommit = true
44
+ @prepared_statements = {}
45
+ end
46
+
47
+ # Check whether the connection is in autocommit state or not.
48
+ def autocommit
49
+ IBM_DB.autocommit(@conn) == 1
50
+ end
51
+
52
+ # Turn autocommit on or off for the connection.
53
+ def autocommit=(value)
54
+ IBM_DB.autocommit(@conn, value ? IBM_DB::SQL_AUTOCOMMIT_ON : IBM_DB::SQL_AUTOCOMMIT_OFF)
55
+ end
56
+
57
+ # Close the connection, disconnecting from DB2.
58
+ def close
59
+ IBM_DB.close(@conn)
60
+ end
61
+
62
+ # Commit the currently outstanding transaction on this connection.
63
+ def commit
64
+ IBM_DB.commit(@conn)
65
+ end
66
+
67
+ # Return the related error message for the connection.
68
+ def error_msg
69
+ IBM_DB.getErrormsg(@conn, IBM_DB::DB_CONN)
70
+ end
71
+
72
+ # Execute the given SQL on the database, and return a Statement instance
73
+ # holding the results.
74
+ def execute(sql)
75
+ stmt = IBM_DB.exec(@conn, sql)
76
+ raise Error, error_msg unless stmt
77
+ Statement.new(stmt)
78
+ end
79
+
80
+ # Execute the related prepared statement on the database with the given
81
+ # arguments.
82
+ def execute_prepared(ps_name, *values)
83
+ stmt = @prepared_statements[ps_name].last
84
+ res = stmt.execute(*values)
85
+ unless res
86
+ raise Error, "Error executing statement #{ps_name}: #{error_msg}"
87
+ end
88
+ stmt
89
+ end
90
+
91
+ # Prepare a statement with the given +sql+ on the database, and
92
+ # cache the prepared statement value by name.
93
+ def prepare(sql, ps_name)
94
+ if stmt = IBM_DB.prepare(@conn, sql)
95
+ ps_name = ps_name.to_sym
96
+ stmt = Statement.new(stmt)
97
+ @prepared_statements[ps_name] = [sql, stmt]
98
+ else
99
+ err = error_msg
100
+ err = "Error preparing #{ps_name} with SQL: #{sql}" if error_msg.nil? || error_msg.empty?
101
+ raise Error, err
102
+ end
103
+ end
104
+
105
+ # Rollback the currently outstanding transaction on this connection.
106
+ def rollback
107
+ IBM_DB.rollback(@conn)
108
+ end
109
+ end
110
+
111
+ # Wraps results returned by queries on IBM_DB.
112
+ class Statement
113
+ # Hold the given statement.
114
+ def initialize(stmt)
115
+ @stmt = stmt
116
+ end
117
+
118
+ # Return the number of rows affected.
119
+ def affected
120
+ IBM_DB.num_rows(@stmt)
121
+ end
122
+
123
+ # If this statement is a prepared statement, execute it on the database
124
+ # with the given values.
125
+ def execute(*values)
126
+ IBM_DB.execute(@stmt, values)
127
+ end
128
+
129
+ # Return the results of a query as an array of values.
130
+ def fetch_array
131
+ IBM_DB.fetch_array(@stmt) if @stmt
132
+ end
133
+
134
+ # Return the field name at the given column in the result set.
135
+ def field_name(ind)
136
+ IBM_DB.field_name(@stmt, ind)
137
+ end
138
+
139
+ # Return the field type for the given field name in the result set.
140
+ def field_type(key)
141
+ IBM_DB.field_type(@stmt, key)
142
+ end
143
+
144
+ # Return the field precision for the given field name in the result set.
145
+ def field_precision(key)
146
+ IBM_DB.field_precision(@stmt, key)
147
+ end
148
+
149
+ # Free the memory related to this result set.
150
+ def free
151
+ IBM_DB.free_result(@stmt)
152
+ end
153
+
154
+ # Return the number of fields in the result set.
155
+ def num_fields
156
+ IBM_DB.num_fields(@stmt)
157
+ end
158
+ end
159
+
160
+ class Database < Sequel::Database
161
+ include Sequel::DB2::DatabaseMethods
162
+
163
+ set_adapter_scheme :ibmdb
164
+
165
+ # Hash of connection procs for converting
166
+ attr_reader :conversion_procs
167
+
168
+ def initialize(opts={})
169
+ super
170
+ @conversion_procs = DB2_TYPES.dup
171
+ @conversion_procs[:timestamp] = method(:to_application_timestamp)
172
+ end
173
+
174
+ # REORG the related table whenever it is altered. This is not always
175
+ # required, but it is necessary for compatibilty with other Sequel
176
+ # code in many cases.
177
+ def alter_table(name, generator=nil)
178
+ res = super
179
+ reorg(name)
180
+ res
181
+ end
182
+
183
+ # Create a new connection object for the given server.
184
+ def connect(server)
185
+ opts = server_opts(server)
186
+
187
+ # use uncataloged connection so that host and port can be supported
188
+ connection_string = ( \
189
+ 'Driver={IBM DB2 ODBC DRIVER};' \
190
+ "Database=#{opts[:database]};" \
191
+ "Hostname=#{opts[:host]};" \
192
+ "Port=#{opts[:port] || 50000};" \
193
+ 'Protocol=TCPIP;' \
194
+ "Uid=#{opts[:user]};" \
195
+ "Pwd=#{opts[:password]};" \
196
+ )
197
+
198
+ Connection.new(connection_string)
199
+ end
200
+
201
+ # Execute the given SQL on the database.
202
+ def execute(sql, opts={}, &block)
203
+ if sql.is_a?(Symbol)
204
+ execute_prepared_statement(sql, opts, &block)
205
+ else
206
+ synchronize(opts[:server]){|c| _execute(c, sql, opts, &block)}
207
+ end
208
+ rescue Connection::Error => e
209
+ raise_error(e)
210
+ end
211
+
212
+ # Execute the given SQL on the database, returning the last inserted
213
+ # identity value.
214
+ def execute_insert(sql, opts={})
215
+ synchronize(opts[:server]) do |c|
216
+ if sql.is_a?(Symbol)
217
+ execute_prepared_statement(sql, opts)
218
+ else
219
+ _execute(c, sql, opts)
220
+ end
221
+ _execute(c, "SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1", opts){|stmt| i = stmt.fetch_array.first.to_i; stmt.free; i}
222
+ end
223
+ rescue Connection::Error => e
224
+ raise_error(e)
225
+ end
226
+
227
+ # Execute a prepared statement named by name on the database.
228
+ def execute_prepared_statement(ps_name, opts)
229
+ args = opts[:arguments]
230
+ ps = prepared_statement(ps_name)
231
+ sql = ps.prepared_sql
232
+ synchronize(opts[:server]) do |conn|
233
+ unless conn.prepared_statements.fetch(ps_name, []).first == sql
234
+ log_yield("PREPARE #{ps_name}: #{sql}"){conn.prepare(sql, ps_name)}
235
+ end
236
+ args = args.map{|v| v.nil? ? nil : prepared_statement_arg(v)}
237
+ log_sql = "EXECUTE #{ps_name}"
238
+ if ps.log_sql
239
+ log_sql << " ("
240
+ log_sql << sql
241
+ log_sql << ")"
242
+ end
243
+ stmt = log_yield(log_sql, args){conn.execute_prepared(ps_name, *args)}
244
+ if block_given?
245
+ begin
246
+ yield(stmt)
247
+ ensure
248
+ stmt.free
249
+ end
250
+ else
251
+ stmt.affected
252
+ end
253
+ end
254
+ end
255
+
256
+ # Convert smallint type to boolean if convert_smallint_to_bool is true
257
+ def schema_column_type(db_type)
258
+ if Sequel::IBMDB.convert_smallint_to_bool && db_type =~ /smallint/i
259
+ :boolean
260
+ else
261
+ super
262
+ end
263
+ end
264
+
265
+ # On DB2, a table might need to be REORGed if you are testing existence
266
+ # of it. This REORGs automatically if the database raises a specific
267
+ # error that indicates it should be REORGed.
268
+ def table_exists?(name)
269
+ v ||= false # only retry once
270
+ sch, table_name = schema_and_table(name)
271
+ name = SQL::QualifiedIdentifier.new(sch, table_name) if sch
272
+ from(name).first
273
+ true
274
+ rescue DatabaseError => e
275
+ if e.to_s =~ /Operation not allowed for reason code "7" on table/ && v == false
276
+ # table probably needs reorg
277
+ reorg(name)
278
+ v = true
279
+ retry
280
+ end
281
+ false
282
+ end
283
+
284
+ private
285
+
286
+ # Execute the given SQL on the database.
287
+ def _execute(conn, sql, opts)
288
+ stmt = log_yield(sql){conn.execute(sql)}
289
+ if block_given?
290
+ begin
291
+ yield(stmt)
292
+ ensure
293
+ stmt.free
294
+ end
295
+ else
296
+ stmt.affected
297
+ end
298
+ end
299
+
300
+ # IBM_DB uses an autocommit setting instead of sending SQL queries.
301
+ # So starting a transaction just turns autocommit off.
302
+ def begin_transaction(conn, opts={})
303
+ log_yield(TRANSACTION_BEGIN){conn.autocommit = false}
304
+ end
305
+
306
+ # This commits transaction in progress on the
307
+ # connection and sets autocommit back on.
308
+ def commit_transaction(conn, opts={})
309
+ log_yield(TRANSACTION_COMMIT){conn.commit}
310
+ end
311
+
312
+ # Close the given connection.
313
+ def disconnect_connection(conn)
314
+ conn.close
315
+ end
316
+
317
+ # Don't convert smallint to boolean for the metadata
318
+ # dataset, since the DB2 metadata does not use
319
+ # boolean columns, and some smallint columns are
320
+ # accidently treated as booleans.
321
+ def metadata_dataset
322
+ ds = super
323
+ ds.convert_smallint_to_bool = false
324
+ ds
325
+ end
326
+
327
+ # Format Numeric, Date, and Time types specially for use
328
+ # as IBM_DB prepared statements argument vlaues.
329
+ def prepared_statement_arg(v)
330
+ case v
331
+ when Numeric
332
+ v.to_s
333
+ when Date, Time
334
+ literal(v).gsub("'", '')
335
+ else
336
+ v
337
+ end
338
+ end
339
+
340
+ # Set autocommit back on
341
+ def remove_transaction(conn, committed)
342
+ conn.autocommit = true
343
+ ensure
344
+ super
345
+ end
346
+
347
+ # This rolls back the transaction in progress on the
348
+ # connection and sets autocommit back on.
349
+ def rollback_transaction(conn, opts={})
350
+ log_yield(TRANSACTION_ROLLBACK){conn.rollback}
351
+ end
352
+ end
353
+
354
+ class Dataset < Sequel::Dataset
355
+ include Sequel::DB2::DatasetMethods
356
+
357
+ Database::DatasetClass = self
358
+
359
+ module CallableStatementMethods
360
+ # Extend given dataset with this module so subselects inside subselects in
361
+ # prepared statements work.
362
+ def subselect_sql_append(sql, ds)
363
+ ps = ds.to_prepared_statement(:select).clone(:append_sql=>sql)
364
+ ps.extend(CallableStatementMethods)
365
+ ps = ps.bind(@opts[:bind_vars]) if @opts[:bind_vars]
366
+ ps.prepared_args = prepared_args
367
+ ps.prepared_sql
368
+ end
369
+ end
370
+
371
+ # Methods for DB2 prepared statements using the native driver.
372
+ module PreparedStatementMethods
373
+ include Sequel::Dataset::UnnumberedArgumentMapper
374
+
375
+ private
376
+ # Execute the prepared statement with arguments instead of the given SQL.
377
+ def execute(sql, opts={}, &block)
378
+ super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
379
+ end
380
+
381
+ # Execute the prepared statment with arguments instead of the given SQL.
382
+ def execute_dui(sql, opts={}, &block)
383
+ super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
384
+ end
385
+
386
+ # Execute the prepared statement with arguments instead of the given SQL.
387
+ def execute_insert(sql, opts={}, &block)
388
+ super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
389
+ end
390
+
391
+ end
392
+
393
+ # Emulate support of bind arguments in called statements.
394
+ def call(type, bind_arguments={}, *values, &block)
395
+ ps = to_prepared_statement(type, values)
396
+ ps.extend(CallableStatementMethods)
397
+ ps.call(bind_arguments, &block)
398
+ end
399
+
400
+ # Whether to convert smallint to boolean arguments for this dataset.
401
+ # Defaults to the IBMDB module setting.
402
+ def convert_smallint_to_bool
403
+ defined?(@convert_smallint_to_bool) ? @convert_smallint_to_bool : (@convert_smallint_to_bool = IBMDB.convert_smallint_to_bool)
404
+ end
405
+
406
+ # Override the default IBMDB.convert_smallint_to_bool setting for this dataset.
407
+ attr_writer :convert_smallint_to_bool
408
+
409
+ # Fetch the rows from the database and yield plain hashes.
410
+ def fetch_rows(sql)
411
+ execute(sql) do |stmt|
412
+ offset = @opts[:offset]
413
+ columns = []
414
+ convert = convert_smallint_to_bool
415
+ cps = db.conversion_procs
416
+ stmt.num_fields.times do |i|
417
+ k = stmt.field_name i
418
+ key = output_identifier(k)
419
+ type = stmt.field_type(k).downcase.to_sym
420
+ # decide if it is a smallint from precision
421
+ type = :boolean if type ==:int && convert && stmt.field_precision(k) < 8
422
+ columns << [key, cps[type]]
423
+ end
424
+ cols = columns.map{|c| c.at(0)}
425
+ cols.delete(row_number_column) if offset
426
+ @columns = cols
427
+
428
+ while res = stmt.fetch_array
429
+ row = {}
430
+ res.zip(columns).each do |v, (k, pr)|
431
+ row[k] = ((pr ? pr.call(v) : v) if v)
432
+ end
433
+ row.delete(row_number_column) if offset
434
+ yield row
435
+ end
436
+ end
437
+ self
438
+ end
439
+
440
+ # Store the given type of prepared statement in the associated database
441
+ # with the given name.
442
+ def prepare(type, name=nil, *values)
443
+ ps = to_prepared_statement(type, values)
444
+ ps.extend(PreparedStatementMethods)
445
+ if name
446
+ ps.prepared_statement_name = name
447
+ db.set_prepared_statement(name, ps)
448
+ end
449
+ ps
450
+ end
451
+ end
452
+ end
453
+ end
@@ -1,21 +1,18 @@
1
1
  require 'informix'
2
+ Sequel.require 'adapters/shared/informix'
2
3
 
3
4
  module Sequel
4
5
  module Informix
5
6
  class Database < Sequel::Database
7
+ include DatabaseMethods
8
+
6
9
  set_adapter_scheme :informix
7
10
 
8
- TEMPORARY = 'TEMP '.freeze
9
-
10
11
  def connect(server)
11
12
  opts = server_opts(server)
12
13
  ::Informix.connect(opts[:database], opts[:user], opts[:password])
13
14
  end
14
15
 
15
- def dataset(opts = nil)
16
- Sequel::Informix::Dataset.new(self, opts)
17
- end
18
-
19
16
  # Returns number of rows affected
20
17
  def execute_dui(sql, opts={})
21
18
  synchronize(opts[:server]){|c| log_yield(sql){c.immediate(sql)}}
@@ -35,7 +32,9 @@ module Sequel
35
32
  end
36
33
 
37
34
  class Dataset < Sequel::Dataset
38
- SELECT_CLAUSE_METHODS = clause_methods(:select, %w'limit distinct columns from join where having group compounds order')
35
+ include DatasetMethods
36
+
37
+ Database::DatasetClass = self
39
38
 
40
39
  def fetch_rows(sql)
41
40
  execute(sql) do |cursor|
@@ -56,22 +55,6 @@ module Sequel
56
55
  end
57
56
  self
58
57
  end
59
-
60
- private
61
-
62
- # Informix does not support INTERSECT or EXCEPT
63
- def supports_intersect_except?
64
- false
65
- end
66
-
67
- def select_clause_methods
68
- SELECT_CLAUSE_METHODS
69
- end
70
-
71
- def select_limit_sql(sql)
72
- sql << " SKIP #{@opts[:offset]}" if @opts[:offset]
73
- sql << " FIRST #{@opts[:limit]}" if @opts[:limit]
74
- end
75
58
  end
76
59
  end
77
60
  end
@@ -1,9 +1,13 @@
1
+ Sequel.require 'adapters/jdbc/transactions'
2
+
1
3
  module Sequel
2
4
  module JDBC
3
5
  # Database and Dataset support for AS400 databases accessed via JDBC.
4
6
  module AS400
5
7
  # Instance methods for AS400 Database objects accessed via JDBC.
6
8
  module DatabaseMethods
9
+ include Sequel::JDBC::Transactions
10
+
7
11
  TRANSACTION_BEGIN = 'Transaction.begin'.freeze
8
12
  TRANSACTION_COMMIT = 'Transaction.commit'.freeze
9
13
  TRANSACTION_ROLLBACK = 'Transaction.rollback'.freeze
@@ -13,11 +17,6 @@ module Sequel
13
17
  :as400
14
18
  end
15
19
 
16
- # Return Sequel::JDBC::AS400::Dataset object with the given opts.
17
- def dataset(opts=nil)
18
- Sequel::JDBC::AS400::Dataset.new(self, opts)
19
- end
20
-
21
20
  # TODO: Fix for AS400
22
21
  def last_insert_id(conn, opts={})
23
22
  nil
@@ -33,31 +32,16 @@ module Sequel
33
32
  # Use JDBC connection's setAutoCommit to false to start transactions
34
33
  def begin_transaction(conn, opts={})
35
34
  set_transaction_isolation(conn, opts)
36
- log_yield(TRANSACTION_BEGIN){conn.setAutoCommit(false)}
37
- conn
38
- end
39
-
40
- # Use JDBC connection's commit method to commit transactions
41
- def commit_transaction(conn, opts={})
42
- log_yield(TRANSACTION_COMMIT){conn.commit}
43
- end
44
-
45
- # Use JDBC connection's setAutoCommit to true to enable default
46
- # auto-commit mode
47
- def remove_transaction(conn)
48
- conn.setAutoCommit(true) if conn
49
- @transactions.delete(Thread.current)
50
- end
51
-
52
- # Use JDBC connection's rollback method to rollback transactions
53
- def rollback_transaction(conn, opts={})
54
- log_yield(TRANSACTION_ROLLBACK){conn.rollback}
35
+ super
55
36
  end
56
37
  end
57
38
 
58
39
  # Dataset class for AS400 datasets accessed via JDBC.
59
40
  class Dataset < JDBC::Dataset
60
41
  WILDCARD = Sequel::LiteralString.new('*').freeze
42
+ FETCH_FIRST_ROW_ONLY = " FETCH FIRST ROW ONLY".freeze
43
+ FETCH_FIRST = " FETCH FIRST ".freeze
44
+ ROWS_ONLY = " ROWS ONLY".freeze
61
45
 
62
46
  # AS400 needs to use a couple of subselects for queries with offsets.
63
47
  def select_sql
@@ -79,22 +63,20 @@ module Sequel
79
63
 
80
64
  # Modify the sql to limit the number of rows returned
81
65
  def select_limit_sql(sql)
82
- if @opts[:limit]
83
- sql << " FETCH FIRST ROW ONLY" if @opts[:limit] == 1
84
- sql << " FETCH FIRST #{@opts[:limit]} ROWS ONLY" if @opts[:limit] > 1
66
+ if l = @opts[:limit]
67
+ if l == 1
68
+ sql << FETCH_FIRST_ROW_ONLY
69
+ elsif l > 1
70
+ sql << FETCH_FIRST
71
+ literal_append(sql, l)
72
+ sql << ROWS_ONLY
73
+ end
85
74
  end
86
75
  end
87
76
 
88
77
  def supports_window_functions?
89
78
  true
90
79
  end
91
-
92
- private
93
-
94
- # The alias to use for the row_number column when emulating LIMIT and OFFSET
95
- def row_number_column
96
- :x_sequel_row_number_x
97
- end
98
80
  end
99
81
  end
100
82
  end
@@ -0,0 +1,56 @@
1
+ Sequel.require 'adapters/shared/db2'
2
+ Sequel.require 'adapters/jdbc/transactions'
3
+
4
+ module Sequel
5
+ module JDBC
6
+ class Database
7
+ # Alias the generic JDBC versions so they can be called directly later
8
+ alias jdbc_schema_parse_table schema_parse_table
9
+ alias jdbc_tables tables
10
+ alias jdbc_views views
11
+ alias jdbc_indexes indexes
12
+ end
13
+
14
+ # Database and Dataset instance methods for DB2 specific
15
+ # support via JDBC.
16
+ module DB2
17
+ # Database instance methods for DB2 databases accessed via JDBC.
18
+ module DatabaseMethods
19
+ PRIMARY_KEY_INDEX_RE = /\Asql\d+\z/i.freeze
20
+
21
+ include Sequel::DB2::DatabaseMethods
22
+ include Sequel::JDBC::Transactions
23
+ IDENTITY_VAL_LOCAL = "SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1".freeze
24
+
25
+ %w'schema_parse_table tables views indexes'.each do |s|
26
+ class_eval("def #{s}(*a) jdbc_#{s}(*a) end", __FILE__, __LINE__)
27
+ end
28
+
29
+ private
30
+
31
+ def set_ps_arg(cps, arg, i)
32
+ case arg
33
+ when Sequel::SQL::Blob
34
+ cps.setString(i, arg)
35
+ else
36
+ super
37
+ end
38
+ end
39
+
40
+ def last_insert_id(conn, opts={})
41
+ statement(conn) do |stmt|
42
+ sql = IDENTITY_VAL_LOCAL
43
+ rs = log_yield(sql){stmt.executeQuery(sql)}
44
+ rs.next
45
+ rs.getInt(1)
46
+ end
47
+ end
48
+
49
+ # Primary key indexes appear to be named sqlNNNN on DB2
50
+ def primary_key_index_re
51
+ PRIMARY_KEY_INDEX_RE
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end