ibm_db 5.2.0-x86-mingw32 → 5.4.0-x86-mingw32

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 (625) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +9 -0
  3. data/LICENSE +55 -18
  4. data/README +1 -1
  5. data/debug.log +1 -0
  6. data/ext/Makefile +28 -24
  7. data/ext/ibm_db.c +66 -65
  8. data/ext/ibm_db.o +0 -0
  9. data/ext/ibm_db.so +0 -0
  10. data/ext/mkmf.log +26 -24
  11. data/ext/ruby_ibm_db_cli.c +1 -0
  12. data/ext/ruby_ibm_db_cli.o +0 -0
  13. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +1520 -1282
  14. data/lib/ibm_db.so +1 -0
  15. data/lib/mswin32/ibm_db.rb +3 -1
  16. data/lib/mswin32/rb3x/i386/ruby30/ibm_db.so +0 -0
  17. data/lib/mswin32/rb3x/i386/ruby31/ibm_db.so +0 -0
  18. data/test/active_record/connection_adapters/fake_adapter.rb +5 -2
  19. data/test/activejob/destroy_association_async_test.rb +305 -0
  20. data/test/activejob/destroy_async_job_not_present_test.rb +31 -0
  21. data/test/activejob/helper.rb +15 -0
  22. data/test/assets/schema_dump_5_1.yml +345 -0
  23. data/test/cases/adapter_prevent_writes_test.rb +334 -0
  24. data/test/cases/adapter_test.rb +432 -218
  25. data/test/cases/adapters/mysql2/active_schema_test.rb +85 -75
  26. data/test/cases/adapters/mysql2/auto_increment_test.rb +34 -0
  27. data/test/cases/adapters/mysql2/bind_parameter_test.rb +5 -3
  28. data/test/cases/adapters/mysql2/boolean_test.rb +6 -4
  29. data/test/cases/adapters/mysql2/case_sensitivity_test.rb +26 -24
  30. data/test/cases/adapters/mysql2/charset_collation_test.rb +20 -17
  31. data/test/cases/adapters/mysql2/connection_test.rb +48 -50
  32. data/test/cases/adapters/mysql2/count_deleted_rows_with_lock_test.rb +28 -0
  33. data/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb +23 -19
  34. data/test/cases/adapters/mysql2/enum_test.rb +32 -11
  35. data/test/cases/adapters/mysql2/explain_test.rb +13 -11
  36. data/test/cases/adapters/mysql2/json_test.rb +17 -188
  37. data/test/cases/adapters/mysql2/mysql2_adapter_prevent_writes_test.rb +208 -0
  38. data/test/cases/adapters/mysql2/mysql2_adapter_test.rb +183 -28
  39. data/test/cases/adapters/mysql2/nested_deadlock_test.rb +75 -0
  40. data/test/cases/adapters/mysql2/optimizer_hints_test.rb +69 -0
  41. data/test/cases/adapters/mysql2/schema_migrations_test.rb +26 -21
  42. data/test/cases/adapters/mysql2/schema_test.rb +24 -22
  43. data/test/cases/adapters/mysql2/set_test.rb +32 -0
  44. data/test/cases/adapters/mysql2/sp_test.rb +10 -8
  45. data/test/cases/adapters/mysql2/sql_types_test.rb +8 -6
  46. data/test/cases/adapters/mysql2/table_options_test.rb +93 -10
  47. data/test/cases/adapters/mysql2/transaction_test.rb +151 -0
  48. data/test/cases/adapters/mysql2/unsigned_type_test.rb +11 -9
  49. data/test/cases/adapters/mysql2/virtual_column_test.rb +66 -0
  50. data/test/cases/adapters/postgresql/active_schema_test.rb +40 -25
  51. data/test/cases/adapters/postgresql/array_test.rb +118 -63
  52. data/test/cases/adapters/postgresql/bit_string_test.rb +12 -10
  53. data/test/cases/adapters/postgresql/bytea_test.rb +26 -25
  54. data/test/cases/adapters/postgresql/case_insensitive_test.rb +10 -9
  55. data/test/cases/adapters/postgresql/change_schema_test.rb +7 -5
  56. data/test/cases/adapters/postgresql/cidr_test.rb +2 -0
  57. data/test/cases/adapters/postgresql/citext_test.rb +58 -58
  58. data/test/cases/adapters/postgresql/collation_test.rb +17 -15
  59. data/test/cases/adapters/postgresql/composite_test.rb +25 -23
  60. data/test/cases/adapters/postgresql/connection_test.rb +73 -85
  61. data/test/cases/adapters/postgresql/create_unlogged_tables_test.rb +74 -0
  62. data/test/cases/adapters/postgresql/datatype_test.rb +19 -22
  63. data/test/cases/adapters/postgresql/date_test.rb +42 -0
  64. data/test/cases/adapters/postgresql/domain_test.rb +9 -7
  65. data/test/cases/adapters/postgresql/enum_test.rb +12 -10
  66. data/test/cases/adapters/postgresql/explain_test.rb +10 -8
  67. data/test/cases/adapters/postgresql/extension_migration_test.rb +13 -12
  68. data/test/cases/adapters/postgresql/foreign_table_test.rb +109 -0
  69. data/test/cases/adapters/postgresql/full_text_test.rb +8 -6
  70. data/test/cases/adapters/postgresql/geometric_test.rb +57 -63
  71. data/test/cases/adapters/postgresql/hstore_test.rb +288 -280
  72. data/test/cases/adapters/postgresql/infinity_test.rb +54 -15
  73. data/test/cases/adapters/postgresql/integer_test.rb +2 -0
  74. data/test/cases/adapters/postgresql/interval_test.rb +99 -0
  75. data/test/cases/adapters/postgresql/json_test.rb +16 -201
  76. data/test/cases/adapters/postgresql/ltree_test.rb +14 -16
  77. data/test/cases/adapters/postgresql/money_test.rb +47 -16
  78. data/test/cases/adapters/postgresql/network_test.rb +36 -28
  79. data/test/cases/adapters/postgresql/numbers_test.rb +7 -5
  80. data/test/cases/adapters/postgresql/optimizer_hints_test.rb +71 -0
  81. data/test/cases/adapters/postgresql/partitions_test.rb +22 -0
  82. data/test/cases/adapters/postgresql/postgresql_adapter_prevent_writes_test.rb +205 -0
  83. data/test/cases/adapters/postgresql/postgresql_adapter_test.rb +178 -136
  84. data/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb +27 -0
  85. data/test/cases/adapters/postgresql/quoting_test.rb +12 -6
  86. data/test/cases/adapters/postgresql/range_test.rb +406 -292
  87. data/test/cases/adapters/postgresql/referential_integrity_test.rb +16 -15
  88. data/test/cases/adapters/postgresql/rename_table_test.rb +9 -8
  89. data/test/cases/adapters/postgresql/schema_authorization_test.rb +14 -23
  90. data/test/cases/adapters/postgresql/schema_test.rb +207 -91
  91. data/test/cases/adapters/postgresql/serial_test.rb +9 -7
  92. data/test/cases/adapters/postgresql/statement_pool_test.rb +26 -6
  93. data/test/cases/adapters/postgresql/timestamp_test.rb +17 -15
  94. data/test/cases/adapters/postgresql/transaction_nested_test.rb +114 -0
  95. data/test/cases/adapters/postgresql/transaction_test.rb +189 -0
  96. data/test/cases/adapters/postgresql/type_lookup_test.rb +12 -10
  97. data/test/cases/adapters/postgresql/utils_test.rb +11 -9
  98. data/test/cases/adapters/postgresql/uuid_test.rb +226 -109
  99. data/test/cases/adapters/postgresql/xml_test.rb +10 -14
  100. data/test/cases/adapters/sqlite3/collation_test.rb +26 -15
  101. data/test/cases/adapters/sqlite3/copy_table_test.rb +31 -28
  102. data/test/cases/adapters/sqlite3/explain_test.rb +13 -11
  103. data/test/cases/adapters/sqlite3/json_test.rb +29 -0
  104. data/test/cases/adapters/sqlite3/quoting_test.rb +35 -57
  105. data/test/cases/adapters/sqlite3/sqlite3_adapter_prevent_writes_test.rb +186 -0
  106. data/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +318 -131
  107. data/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb +11 -11
  108. data/test/cases/adapters/sqlite3/statement_pool_test.rb +7 -6
  109. data/test/cases/adapters/sqlite3/transaction_test.rb +123 -0
  110. data/test/cases/aggregations_test.rb +14 -12
  111. data/test/cases/annotate_test.rb +46 -0
  112. data/test/cases/ar_schema_test.rb +153 -86
  113. data/test/cases/arel/attributes/attribute_test.rb +1145 -0
  114. data/test/cases/arel/attributes/math_test.rb +83 -0
  115. data/test/cases/arel/attributes_test.rb +27 -0
  116. data/test/cases/arel/collectors/bind_test.rb +40 -0
  117. data/test/cases/arel/collectors/composite_test.rb +47 -0
  118. data/test/cases/arel/collectors/sql_string_test.rb +41 -0
  119. data/test/cases/arel/collectors/substitute_bind_collector_test.rb +48 -0
  120. data/test/cases/arel/crud_test.rb +65 -0
  121. data/test/cases/arel/delete_manager_test.rb +53 -0
  122. data/test/cases/arel/factory_methods_test.rb +46 -0
  123. data/test/cases/arel/helper.rb +45 -0
  124. data/test/cases/arel/insert_manager_test.rb +241 -0
  125. data/test/cases/arel/nodes/and_test.rb +30 -0
  126. data/test/cases/arel/nodes/as_test.rb +36 -0
  127. data/test/cases/arel/nodes/ascending_test.rb +46 -0
  128. data/test/cases/arel/nodes/bin_test.rb +35 -0
  129. data/test/cases/arel/nodes/binary_test.rb +29 -0
  130. data/test/cases/arel/nodes/bind_param_test.rb +22 -0
  131. data/test/cases/arel/nodes/case_test.rb +96 -0
  132. data/test/cases/arel/nodes/casted_test.rb +18 -0
  133. data/test/cases/arel/nodes/comment_test.rb +22 -0
  134. data/test/cases/arel/nodes/count_test.rb +35 -0
  135. data/test/cases/arel/nodes/delete_statement_test.rb +36 -0
  136. data/test/cases/arel/nodes/descending_test.rb +46 -0
  137. data/test/cases/arel/nodes/distinct_test.rb +21 -0
  138. data/test/cases/arel/nodes/equality_test.rb +62 -0
  139. data/test/cases/arel/nodes/extract_test.rb +43 -0
  140. data/test/cases/arel/nodes/false_test.rb +21 -0
  141. data/test/cases/arel/nodes/grouping_test.rb +26 -0
  142. data/test/cases/arel/nodes/infix_operation_test.rb +42 -0
  143. data/test/cases/arel/nodes/insert_statement_test.rb +44 -0
  144. data/test/cases/arel/nodes/named_function_test.rb +48 -0
  145. data/test/cases/arel/nodes/node_test.rb +22 -0
  146. data/test/cases/arel/nodes/not_test.rb +31 -0
  147. data/test/cases/arel/nodes/or_test.rb +36 -0
  148. data/test/cases/arel/nodes/over_test.rb +69 -0
  149. data/test/cases/arel/nodes/select_core_test.rb +79 -0
  150. data/test/cases/arel/nodes/select_statement_test.rb +51 -0
  151. data/test/cases/arel/nodes/sql_literal_test.rb +75 -0
  152. data/test/cases/arel/nodes/sum_test.rb +35 -0
  153. data/test/cases/arel/nodes/table_alias_test.rb +29 -0
  154. data/test/cases/arel/nodes/true_test.rb +21 -0
  155. data/test/cases/arel/nodes/unary_operation_test.rb +41 -0
  156. data/test/cases/arel/nodes/update_statement_test.rb +60 -0
  157. data/test/cases/arel/nodes/window_test.rb +81 -0
  158. data/test/cases/arel/nodes_test.rb +34 -0
  159. data/test/cases/arel/select_manager_test.rb +1238 -0
  160. data/test/cases/arel/support/fake_record.rb +135 -0
  161. data/test/cases/arel/table_test.rb +216 -0
  162. data/test/cases/arel/update_manager_test.rb +126 -0
  163. data/test/cases/arel/visitors/dispatch_contamination_test.rb +78 -0
  164. data/test/cases/arel/visitors/dot_test.rb +90 -0
  165. data/test/cases/arel/visitors/mysql_test.rb +157 -0
  166. data/test/cases/arel/visitors/postgres_test.rb +366 -0
  167. data/test/cases/arel/visitors/sqlite_test.rb +75 -0
  168. data/test/cases/arel/visitors/to_sql_test.rb +750 -0
  169. data/test/cases/associations/belongs_to_associations_test.rb +510 -158
  170. data/test/cases/associations/bidirectional_destroy_dependencies_test.rb +4 -2
  171. data/test/cases/associations/callbacks_test.rb +56 -38
  172. data/test/cases/associations/cascaded_eager_loading_test.rb +118 -61
  173. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +138 -18
  174. data/test/cases/associations/eager_load_nested_include_test.rb +38 -37
  175. data/test/cases/associations/eager_singularization_test.rb +21 -21
  176. data/test/cases/associations/eager_test.rb +559 -415
  177. data/test/cases/associations/extension_test.rb +18 -12
  178. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +234 -213
  179. data/test/cases/associations/has_many_associations_test.rb +1038 -465
  180. data/test/cases/associations/has_many_through_associations_test.rb +558 -249
  181. data/test/cases/associations/has_one_associations_test.rb +294 -129
  182. data/test/cases/associations/has_one_through_associations_test.rb +121 -75
  183. data/test/cases/associations/inner_join_association_test.rb +114 -38
  184. data/test/cases/associations/inverse_associations_test.rb +606 -398
  185. data/test/cases/associations/join_model_test.rb +158 -148
  186. data/test/cases/associations/left_outer_join_association_test.rb +59 -24
  187. data/test/cases/associations/nested_through_associations_test.rb +166 -109
  188. data/test/cases/associations/required_test.rb +35 -10
  189. data/test/cases/associations_test.rb +241 -110
  190. data/test/cases/attribute_methods/read_test.rb +11 -11
  191. data/test/cases/attribute_methods_test.rb +413 -298
  192. data/test/cases/attributes_test.rb +145 -27
  193. data/test/cases/autosave_association_test.rb +681 -436
  194. data/test/cases/base_prevent_writes_test.rb +229 -0
  195. data/test/cases/base_test.rb +599 -542
  196. data/test/cases/batches_test.rb +288 -82
  197. data/test/cases/binary_test.rb +26 -31
  198. data/test/cases/bind_parameter_test.rb +194 -21
  199. data/test/cases/boolean_test.rb +52 -0
  200. data/test/cases/cache_key_test.rb +110 -5
  201. data/test/cases/calculations_test.rb +740 -177
  202. data/test/cases/callbacks_test.rb +74 -207
  203. data/test/cases/clone_test.rb +15 -10
  204. data/test/cases/coders/json_test.rb +2 -0
  205. data/test/cases/coders/yaml_column_test.rb +16 -13
  206. data/test/cases/collection_cache_key_test.rb +177 -20
  207. data/test/cases/column_alias_test.rb +9 -7
  208. data/test/cases/column_definition_test.rb +10 -68
  209. data/test/cases/comment_test.rb +166 -107
  210. data/test/cases/connection_adapters/adapter_leasing_test.rb +14 -10
  211. data/test/cases/connection_adapters/connection_handler_test.rb +358 -51
  212. data/test/cases/connection_adapters/connection_handlers_multi_db_test.rb +400 -0
  213. data/test/cases/connection_adapters/connection_handlers_multi_pool_config_test.rb +103 -0
  214. data/test/cases/connection_adapters/connection_handlers_sharding_db_test.rb +499 -0
  215. data/test/cases/connection_adapters/connection_swapping_nested_test.rb +457 -0
  216. data/test/cases/connection_adapters/legacy_connection_handlers_multi_db_test.rb +486 -0
  217. data/test/cases/connection_adapters/legacy_connection_handlers_sharding_db_test.rb +586 -0
  218. data/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +319 -138
  219. data/test/cases/connection_adapters/mysql_type_lookup_test.rb +62 -50
  220. data/test/cases/connection_adapters/schema_cache_test.rb +259 -26
  221. data/test/cases/connection_adapters/type_lookup_test.rb +96 -95
  222. data/test/cases/connection_management_test.rb +13 -11
  223. data/test/cases/connection_pool_test.rb +316 -83
  224. data/test/cases/core_test.rb +82 -58
  225. data/test/cases/counter_cache_test.rb +204 -50
  226. data/test/cases/custom_locking_test.rb +5 -3
  227. data/test/cases/database_configurations/hash_config_test.rb +74 -0
  228. data/test/cases/database_configurations/resolver_test.rb +150 -0
  229. data/test/cases/database_configurations_test.rb +145 -0
  230. data/test/cases/database_selector_test.rb +296 -0
  231. data/test/cases/database_statements_test.rb +18 -16
  232. data/test/cases/date_test.rb +8 -16
  233. data/test/cases/date_time_precision_test.rb +100 -78
  234. data/test/cases/date_time_test.rb +23 -8
  235. data/test/cases/defaults_test.rb +106 -71
  236. data/test/cases/delegated_type_test.rb +57 -0
  237. data/test/cases/dirty_test.rb +419 -223
  238. data/test/cases/disconnected_test.rb +6 -6
  239. data/test/cases/dup_test.rb +54 -27
  240. data/test/cases/enum_test.rb +461 -82
  241. data/test/cases/errors_test.rb +7 -7
  242. data/test/cases/explain_subscriber_test.rb +17 -15
  243. data/test/cases/explain_test.rb +11 -19
  244. data/test/cases/filter_attributes_test.rb +153 -0
  245. data/test/cases/finder_respond_to_test.rb +14 -14
  246. data/test/cases/finder_test.rb +669 -287
  247. data/test/cases/fixture_set/file_test.rb +34 -38
  248. data/test/cases/fixtures_test.rb +833 -176
  249. data/test/cases/forbidden_attributes_protection_test.rb +32 -67
  250. data/test/cases/habtm_destroy_order_test.rb +25 -25
  251. data/test/cases/helper.rb +78 -49
  252. data/test/cases/hot_compatibility_test.rb +33 -32
  253. data/test/cases/i18n_test.rb +18 -17
  254. data/test/cases/inheritance_test.rb +180 -115
  255. data/test/cases/insert_all_test.rb +489 -0
  256. data/test/cases/instrumentation_test.rb +101 -0
  257. data/test/cases/integration_test.rb +119 -31
  258. data/test/cases/invalid_connection_test.rb +18 -16
  259. data/test/cases/invertible_migration_test.rb +183 -43
  260. data/test/cases/json_attribute_test.rb +35 -0
  261. data/test/cases/json_serialization_test.rb +57 -58
  262. data/test/cases/json_shared_test_cases.rb +290 -0
  263. data/test/cases/locking_test.rb +413 -119
  264. data/test/cases/log_subscriber_test.rb +68 -26
  265. data/test/cases/marshal_serialization_test.rb +39 -0
  266. data/test/cases/migration/change_schema_test.rb +118 -72
  267. data/test/cases/migration/change_table_test.rb +138 -30
  268. data/test/cases/migration/check_constraint_test.rb +162 -0
  269. data/test/cases/migration/column_attributes_test.rb +45 -35
  270. data/test/cases/migration/column_positioning_test.rb +18 -6
  271. data/test/cases/migration/columns_test.rb +93 -77
  272. data/test/cases/migration/command_recorder_test.rb +121 -34
  273. data/test/cases/migration/compatibility_test.rb +578 -23
  274. data/test/cases/migration/create_join_table_test.rb +35 -25
  275. data/test/cases/migration/foreign_key_test.rb +503 -284
  276. data/test/cases/migration/helper.rb +4 -3
  277. data/test/cases/migration/index_test.rb +119 -70
  278. data/test/cases/migration/logger_test.rb +9 -6
  279. data/test/cases/migration/pending_migrations_test.rb +88 -34
  280. data/test/cases/migration/references_foreign_key_test.rb +164 -150
  281. data/test/cases/migration/references_index_test.rb +38 -19
  282. data/test/cases/migration/references_statements_test.rb +15 -14
  283. data/test/cases/migration/rename_table_test.rb +53 -30
  284. data/test/cases/migration_test.rb +637 -269
  285. data/test/cases/migrator_test.rb +191 -135
  286. data/test/cases/mixin_test.rb +7 -11
  287. data/test/cases/modules_test.rb +36 -34
  288. data/test/cases/multi_db_migrator_test.rb +223 -0
  289. data/test/cases/multiparameter_attributes_test.rb +60 -33
  290. data/test/cases/multiple_db_test.rb +16 -22
  291. data/test/cases/nested_attributes_test.rb +341 -320
  292. data/test/cases/nested_attributes_with_callbacks_test.rb +26 -24
  293. data/test/cases/null_relation_test.rb +84 -0
  294. data/test/cases/numeric_data_test.rb +93 -0
  295. data/test/cases/persistence_test.rb +361 -269
  296. data/test/cases/pooled_connections_test.rb +18 -26
  297. data/test/cases/prepared_statement_status_test.rb +48 -0
  298. data/test/cases/primary_keys_test.rb +210 -104
  299. data/test/cases/query_cache_test.rb +610 -141
  300. data/test/cases/quoting_test.rb +132 -31
  301. data/test/cases/readonly_test.rb +49 -48
  302. data/test/cases/reaper_test.rb +146 -32
  303. data/test/cases/reflection_test.rb +167 -156
  304. data/test/cases/relation/delegation_test.rb +49 -36
  305. data/test/cases/relation/delete_all_test.rb +117 -0
  306. data/test/cases/relation/merging_test.rb +319 -42
  307. data/test/cases/relation/mutation_test.rb +55 -93
  308. data/test/cases/relation/or_test.rb +129 -29
  309. data/test/cases/relation/predicate_builder_test.rb +21 -6
  310. data/test/cases/relation/record_fetch_warning_test.rb +5 -3
  311. data/test/cases/relation/select_test.rb +67 -0
  312. data/test/cases/relation/update_all_test.rb +317 -0
  313. data/test/cases/relation/where_chain_test.rb +68 -32
  314. data/test/cases/relation/where_clause_test.rb +136 -61
  315. data/test/cases/relation/where_test.rb +155 -48
  316. data/test/cases/relation_test.rb +266 -112
  317. data/test/cases/relations_test.rb +969 -744
  318. data/test/cases/reload_models_test.rb +13 -9
  319. data/test/cases/reserved_word_test.rb +141 -0
  320. data/test/cases/result_test.rb +68 -17
  321. data/test/cases/sanitize_test.rb +87 -71
  322. data/test/cases/schema_dumper_test.rb +221 -128
  323. data/test/cases/schema_loading_test.rb +3 -2
  324. data/test/cases/scoping/default_scoping_test.rb +185 -144
  325. data/test/cases/scoping/named_scoping_test.rb +177 -89
  326. data/test/cases/scoping/relation_scoping_test.rb +197 -75
  327. data/test/cases/secure_token_test.rb +18 -3
  328. data/test/cases/serialization_test.rb +30 -28
  329. data/test/cases/serialized_attribute_test.rb +133 -42
  330. data/test/cases/signed_id_test.rb +168 -0
  331. data/test/cases/statement_cache_test.rb +41 -24
  332. data/test/cases/statement_invalid_test.rb +42 -0
  333. data/test/cases/store_test.rb +180 -55
  334. data/test/cases/strict_loading_test.rb +473 -0
  335. data/test/cases/suppressor_test.rb +26 -12
  336. data/test/cases/tasks/database_tasks_test.rb +1258 -194
  337. data/test/cases/tasks/mysql_rake_test.rb +370 -298
  338. data/test/cases/tasks/postgresql_rake_test.rb +481 -251
  339. data/test/cases/tasks/sqlite_rake_test.rb +225 -178
  340. data/test/cases/test_case.rb +51 -40
  341. data/test/cases/test_databases_test.rb +79 -0
  342. data/test/cases/test_fixtures_test.rb +79 -19
  343. data/test/cases/time_precision_test.rb +98 -76
  344. data/test/cases/timestamp_test.rb +102 -99
  345. data/test/cases/touch_later_test.rb +12 -10
  346. data/test/cases/transaction_callbacks_test.rb +344 -90
  347. data/test/cases/transaction_isolation_test.rb +12 -12
  348. data/test/cases/transactions_test.rb +612 -162
  349. data/test/cases/type/adapter_specific_registry_test.rb +14 -2
  350. data/test/cases/type/date_time_test.rb +4 -2
  351. data/test/cases/type/integer_test.rb +4 -2
  352. data/test/cases/type/string_test.rb +10 -8
  353. data/test/cases/type/time_test.rb +28 -0
  354. data/test/cases/type/type_map_test.rb +29 -28
  355. data/test/cases/type/unsigned_integer_test.rb +19 -0
  356. data/test/cases/type_test.rb +2 -0
  357. data/test/cases/types_test.rb +3 -1
  358. data/test/cases/unconnected_test.rb +14 -1
  359. data/test/cases/unsafe_raw_sql_test.rb +274 -0
  360. data/test/cases/validations/absence_validation_test.rb +19 -17
  361. data/test/cases/validations/association_validation_test.rb +30 -28
  362. data/test/cases/validations/i18n_generate_message_validation_test.rb +34 -16
  363. data/test/cases/validations/i18n_validation_test.rb +22 -21
  364. data/test/cases/validations/length_validation_test.rb +34 -33
  365. data/test/cases/validations/numericality_validation_test.rb +181 -0
  366. data/test/cases/validations/presence_validation_test.rb +21 -19
  367. data/test/cases/validations/uniqueness_validation_test.rb +156 -86
  368. data/test/cases/validations_repair_helper.rb +2 -0
  369. data/test/cases/validations_test.rb +61 -26
  370. data/test/cases/view_test.rb +122 -116
  371. data/test/cases/yaml_serialization_test.rb +79 -34
  372. data/test/config.example.yml +19 -19
  373. data/test/config.rb +3 -1
  374. data/test/config.yml +16 -6
  375. data/test/fixtures/all/namespaced/accounts.yml +2 -0
  376. data/test/fixtures/author_addresses.yml +1 -8
  377. data/test/fixtures/authors.yml +1 -7
  378. data/test/fixtures/binaries.yml +4 -0
  379. data/test/fixtures/books.yml +9 -2
  380. data/test/fixtures/categories_posts.yml +3 -0
  381. data/test/fixtures/citations.yml +5 -0
  382. data/test/fixtures/comments.yml +7 -0
  383. data/test/fixtures/companies.yml +5 -0
  384. data/test/fixtures/computers.yml +2 -0
  385. data/test/fixtures/customers.yml +10 -1
  386. data/test/fixtures/developers.yml +1 -1
  387. data/test/fixtures/essays.yml +10 -0
  388. data/test/fixtures/faces.yml +3 -3
  389. data/test/fixtures/humans.yml +5 -0
  390. data/test/fixtures/interests.yml +7 -7
  391. data/test/fixtures/memberships.yml +7 -0
  392. data/test/fixtures/minimalistics.yml +3 -0
  393. data/test/fixtures/mixed_case_monkeys.yml +2 -2
  394. data/test/fixtures/naked/yml/courses_with_invalid_key.yml +3 -0
  395. data/test/fixtures/naked/yml/parrots.yml +1 -0
  396. data/test/fixtures/other_books.yml +26 -0
  397. data/test/fixtures/other_posts.yml +1 -0
  398. data/test/fixtures/parrots.yml +7 -1
  399. data/test/fixtures/pirates.yml +3 -0
  400. data/test/fixtures/posts.yml +11 -3
  401. data/test/fixtures/readers.yml +6 -0
  402. data/test/fixtures/reserved_words/values.yml +2 -2
  403. data/test/fixtures/sponsors.yml +3 -0
  404. data/test/fixtures/strict_zines.yml +2 -0
  405. data/test/fixtures/subscribers.yml +1 -1
  406. data/test/fixtures/tasks.yml +1 -1
  407. data/test/fixtures/warehouse-things.yml +3 -0
  408. data/test/migrations/10_urban/9_add_expressions.rb +2 -0
  409. data/test/migrations/decimal/1_give_me_big_numbers.rb +6 -4
  410. data/test/migrations/magic/1_currencies_have_symbols.rb +3 -2
  411. data/test/migrations/missing/1000_people_have_middle_names.rb +2 -0
  412. data/test/migrations/missing/1_people_have_last_names.rb +2 -0
  413. data/test/migrations/missing/3_we_need_reminders.rb +2 -0
  414. data/test/migrations/missing/4_innocent_jointable.rb +3 -1
  415. data/test/migrations/rename/1_we_need_things.rb +2 -0
  416. data/test/migrations/rename/2_rename_things.rb +2 -0
  417. data/test/migrations/to_copy/1_people_have_hobbies.rb +3 -1
  418. data/test/migrations/to_copy/2_people_have_descriptions.rb +3 -1
  419. data/test/migrations/to_copy2/1_create_articles.rb +2 -0
  420. data/test/migrations/to_copy2/2_create_comments.rb +3 -1
  421. data/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb +3 -1
  422. data/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb +3 -1
  423. data/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb +3 -1
  424. data/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb +2 -0
  425. data/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb +2 -0
  426. data/test/migrations/valid/1_valid_people_have_last_names.rb +2 -0
  427. data/test/migrations/valid/2_we_need_reminders.rb +2 -0
  428. data/test/migrations/valid/3_innocent_jointable.rb +3 -1
  429. data/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb +2 -0
  430. data/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb +2 -0
  431. data/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb +3 -1
  432. data/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb +2 -0
  433. data/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb +2 -0
  434. data/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb +3 -1
  435. data/test/migrations/version_check/20131219224947_migration_version_check.rb +2 -0
  436. data/test/models/account.rb +46 -0
  437. data/test/models/admin/account.rb +3 -1
  438. data/test/models/admin/randomly_named_c1.rb +2 -0
  439. data/test/models/admin/user.rb +16 -8
  440. data/test/models/admin.rb +4 -2
  441. data/test/models/aircraft.rb +3 -1
  442. data/test/models/arunit2_model.rb +2 -0
  443. data/test/models/author.rb +153 -102
  444. data/test/models/auto_id.rb +2 -0
  445. data/test/models/autoloadable/extra_firm.rb +2 -0
  446. data/test/models/binary.rb +3 -1
  447. data/test/models/binary_field.rb +6 -0
  448. data/test/models/bird.rb +13 -1
  449. data/test/models/book.rb +14 -4
  450. data/test/models/book_destroy_async.rb +24 -0
  451. data/test/models/boolean.rb +5 -0
  452. data/test/models/bulb.rb +13 -4
  453. data/test/models/cake_designer.rb +2 -0
  454. data/test/models/car.rb +17 -10
  455. data/test/models/carrier.rb +2 -0
  456. data/test/models/cart.rb +5 -0
  457. data/test/models/cat.rb +2 -0
  458. data/test/models/categorization.rb +8 -6
  459. data/test/models/category.rb +28 -16
  460. data/test/models/chef.rb +2 -0
  461. data/test/models/citation.rb +5 -1
  462. data/test/models/club.rb +13 -10
  463. data/test/models/college.rb +4 -2
  464. data/test/models/column.rb +2 -0
  465. data/test/models/column_name.rb +2 -0
  466. data/test/models/comment.rb +32 -10
  467. data/test/models/company.rb +102 -106
  468. data/test/models/company_in_module.rb +27 -26
  469. data/test/models/computer.rb +3 -1
  470. data/test/models/contact.rb +15 -13
  471. data/test/models/content.rb +5 -3
  472. data/test/models/contract.rb +21 -3
  473. data/test/models/country.rb +2 -4
  474. data/test/models/course.rb +3 -1
  475. data/test/models/customer.rb +10 -8
  476. data/test/models/customer_carrier.rb +2 -0
  477. data/test/models/dashboard.rb +2 -0
  478. data/test/models/default.rb +2 -0
  479. data/test/models/department.rb +2 -0
  480. data/test/models/destroy_async_parent.rb +15 -0
  481. data/test/models/destroy_async_parent_soft_delete.rb +20 -0
  482. data/test/models/developer.rb +152 -85
  483. data/test/models/dl_keyed_belongs_to.rb +13 -0
  484. data/test/models/dl_keyed_belongs_to_soft_delete.rb +19 -0
  485. data/test/models/dl_keyed_has_many.rb +5 -0
  486. data/test/models/dl_keyed_has_many_through.rb +5 -0
  487. data/test/models/dl_keyed_has_one.rb +5 -0
  488. data/test/models/dl_keyed_join.rb +10 -0
  489. data/test/models/dog.rb +2 -0
  490. data/test/models/dog_lover.rb +2 -0
  491. data/test/models/doubloon.rb +3 -1
  492. data/test/models/drink_designer.rb +17 -0
  493. data/test/models/edge.rb +4 -2
  494. data/test/models/electron.rb +2 -0
  495. data/test/models/engine.rb +3 -2
  496. data/test/models/entrant.rb +2 -0
  497. data/test/models/entry.rb +5 -0
  498. data/test/models/essay.rb +6 -3
  499. data/test/models/essay_destroy_async.rb +12 -0
  500. data/test/models/event.rb +3 -1
  501. data/test/models/eye.rb +5 -3
  502. data/test/models/face.rb +14 -6
  503. data/test/models/family.rb +6 -0
  504. data/test/models/family_tree.rb +6 -0
  505. data/test/models/friendship.rb +5 -3
  506. data/test/models/frog.rb +8 -0
  507. data/test/models/guid.rb +3 -1
  508. data/test/models/guitar.rb +2 -0
  509. data/test/models/hotel.rb +5 -3
  510. data/test/models/human.rb +39 -0
  511. data/test/models/image.rb +3 -1
  512. data/test/models/interest.rb +14 -3
  513. data/test/models/invoice.rb +4 -2
  514. data/test/models/item.rb +3 -1
  515. data/test/models/job.rb +5 -3
  516. data/test/models/joke.rb +4 -2
  517. data/test/models/keyboard.rb +3 -1
  518. data/test/models/legacy_thing.rb +2 -0
  519. data/test/models/lesson.rb +2 -0
  520. data/test/models/line_item.rb +3 -1
  521. data/test/models/liquid.rb +2 -0
  522. data/test/models/matey.rb +3 -1
  523. data/test/models/measurement.rb +4 -0
  524. data/test/models/member.rb +23 -20
  525. data/test/models/member_detail.rb +3 -0
  526. data/test/models/member_type.rb +2 -0
  527. data/test/models/membership.rb +4 -1
  528. data/test/models/mentor.rb +3 -1
  529. data/test/models/message.rb +5 -0
  530. data/test/models/minimalistic.rb +2 -0
  531. data/test/models/minivan.rb +3 -2
  532. data/test/models/mixed_case_monkey.rb +3 -1
  533. data/test/models/molecule.rb +2 -0
  534. data/test/models/mouse.rb +6 -0
  535. data/test/models/movie.rb +2 -0
  536. data/test/models/node.rb +4 -2
  537. data/test/models/non_primary_key.rb +2 -0
  538. data/test/models/notification.rb +2 -0
  539. data/test/models/numeric_data.rb +12 -0
  540. data/test/models/order.rb +4 -2
  541. data/test/models/organization.rb +9 -7
  542. data/test/models/other_dog.rb +3 -1
  543. data/test/models/owner.rb +6 -4
  544. data/test/models/parrot.rb +12 -4
  545. data/test/models/person.rb +59 -54
  546. data/test/models/personal_legacy_thing.rb +3 -1
  547. data/test/models/pet.rb +4 -2
  548. data/test/models/pet_treasure.rb +2 -0
  549. data/test/models/pirate.rb +67 -43
  550. data/test/models/possession.rb +3 -1
  551. data/test/models/post.rb +184 -86
  552. data/test/models/price_estimate.rb +11 -1
  553. data/test/models/professor.rb +3 -1
  554. data/test/models/project.rb +14 -12
  555. data/test/models/publisher/article.rb +2 -0
  556. data/test/models/publisher/magazine.rb +2 -0
  557. data/test/models/publisher.rb +2 -0
  558. data/test/models/randomly_named_c1.rb +2 -0
  559. data/test/models/rating.rb +5 -1
  560. data/test/models/reader.rb +7 -5
  561. data/test/models/recipe.rb +2 -0
  562. data/test/models/record.rb +2 -0
  563. data/test/models/reference.rb +6 -3
  564. data/test/models/reply.rb +39 -21
  565. data/test/models/room.rb +6 -0
  566. data/test/models/section.rb +6 -0
  567. data/test/models/seminar.rb +6 -0
  568. data/test/models/session.rb +6 -0
  569. data/test/models/ship.rb +12 -9
  570. data/test/models/ship_part.rb +5 -3
  571. data/test/models/shop.rb +4 -2
  572. data/test/models/shop_account.rb +2 -0
  573. data/test/models/speedometer.rb +2 -0
  574. data/test/models/sponsor.rb +8 -5
  575. data/test/models/squeak.rb +6 -0
  576. data/test/models/strict_zine.rb +7 -0
  577. data/test/models/string_key_object.rb +2 -0
  578. data/test/models/student.rb +2 -0
  579. data/test/models/subscriber.rb +4 -2
  580. data/test/models/subscription.rb +5 -1
  581. data/test/models/tag.rb +6 -3
  582. data/test/models/tagging.rb +13 -6
  583. data/test/models/task.rb +2 -0
  584. data/test/models/topic.rb +54 -19
  585. data/test/models/toy.rb +4 -0
  586. data/test/models/traffic_light.rb +2 -0
  587. data/test/models/treasure.rb +5 -3
  588. data/test/models/treaty.rb +2 -4
  589. data/test/models/tree.rb +2 -0
  590. data/test/models/tuning_peg.rb +2 -0
  591. data/test/models/tyre.rb +2 -0
  592. data/test/models/user.rb +12 -4
  593. data/test/models/uuid_child.rb +2 -0
  594. data/test/models/uuid_item.rb +2 -0
  595. data/test/models/uuid_parent.rb +2 -0
  596. data/test/models/vegetables.rb +12 -3
  597. data/test/models/vertex.rb +6 -4
  598. data/test/models/warehouse_thing.rb +2 -0
  599. data/test/models/wheel.rb +3 -1
  600. data/test/models/without_table.rb +3 -1
  601. data/test/models/zine.rb +3 -1
  602. data/test/schema/mysql2_specific_schema.rb +49 -35
  603. data/test/schema/oracle_specific_schema.rb +13 -15
  604. data/test/schema/postgresql_specific_schema.rb +51 -40
  605. data/test/schema/schema.rb +334 -154
  606. data/test/schema/sqlite_specific_schema.rb +9 -16
  607. data/test/support/config.rb +26 -26
  608. data/test/support/connection.rb +14 -8
  609. data/test/support/connection_helper.rb +3 -1
  610. data/test/support/ddl_helper.rb +2 -0
  611. data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_6_0_topic.dump +0 -0
  612. data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_6_0_topic_associations.dump +0 -0
  613. data/test/support/marshal_compatibility_fixtures/Mysql2/rails_6_0_topic.dump +0 -0
  614. data/test/support/marshal_compatibility_fixtures/Mysql2/rails_6_0_topic_associations.dump +0 -0
  615. data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_6_0_topic.dump +0 -0
  616. data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_6_0_topic_associations.dump +0 -0
  617. data/test/support/marshal_compatibility_fixtures/SQLite/rails_6_0_topic.dump +0 -0
  618. data/test/support/marshal_compatibility_fixtures/SQLite/rails_6_0_topic_associations.dump +0 -0
  619. data/test/support/marshal_compatibility_fixtures/legacy_6_0_record_mysql.dump +0 -0
  620. data/test/support/marshal_compatibility_fixtures/legacy_relation.dump +0 -0
  621. data/test/support/schema_dumping_helper.rb +2 -0
  622. data/test/support/stubs/strong_parameters.rb +40 -0
  623. data/test/support/yaml_compatibility_fixtures/rails_v1_mysql.yml +206 -0
  624. data/test/support/yaml_compatibility_fixtures/rails_v2.yml +55 -0
  625. metadata +192 -14
@@ -1,7 +1,7 @@
1
1
  # +----------------------------------------------------------------------+
2
2
  # | Licensed Materials - Property of IBM |
3
3
  # | |
4
- # | (C) Copyright IBM Corporation 2006- 2018 |
4
+ # | (C) Copyright IBM Corporation 2006 - 2022 |
5
5
  # +----------------------------------------------------------------------+
6
6
  # | Authors: Antonio Cangiano <cangiano@ca.ibm.com> |
7
7
  # | : Mario Ds Briggs <mario.briggs@in.ibm.com> |
@@ -15,157 +15,196 @@ require 'arel/visitors/visitor'
15
15
  require 'active_support/core_ext/string/strip'
16
16
  require 'active_record/type'
17
17
  require 'active_record/connection_adapters/sql_type_metadata'
18
-
19
-
18
+ require "active_record/connection_adapters/statement_pool"
19
+ require 'active_record/connection_adapters'
20
20
 
21
21
  module CallChain
22
22
  def self.caller_method(depth=1)
23
- parse_caller(caller(depth+1).first).last
24
- end
25
-
26
- private
23
+ parse_caller(caller(depth+1).first).last
24
+ end
27
25
 
28
- # Copied from ActionMailer
29
- def self.parse_caller(at)
30
- if /^(.+?):(\d+)(?::in `(.*)')?/ =~ at
31
- file = Regexp.last_match[1]
32
- line = Regexp.last_match[2].to_i
33
- method = Regexp.last_match[3]
34
- [file, line, method]
35
- end
36
- end
26
+ private
27
+ # Copied from ActionMailer
28
+ def self.parse_caller(at)
29
+ if /^(.+?):(\d+)(?::in `(.*)')?/ =~ at
30
+ file = Regexp.last_match[1]
31
+ line = Regexp.last_match[2].to_i
32
+ method = Regexp.last_match[3]
33
+ [file, line, method]
34
+ end
35
+ end
37
36
  end
38
37
 
39
-
40
38
  module ActiveRecord
41
-
42
-
43
- class SchemaMigration < ActiveRecord::Base
44
- class << self
39
+ class SchemaMigration < ActiveRecord::Base
40
+ class << self
45
41
  def create_table
46
- #puts "Calling method : " << CallChain.caller_method << "\n"
47
- #puts "Calling method for create_table(): " << String(caller(start=1, length=nil) )
48
- unless table_exists?
49
- version_options = connection.internal_string_options_for_primary_key
50
-
51
- connection.create_table(table_name,id:false) do |t|
52
- t.string :version, version_options
53
- end
42
+ unless connection.table_exists?(table_name)
43
+ connection.create_table(table_name, id: false) do |t|
44
+ t.string :version, **connection.internal_string_options_for_primary_key
45
+ end
46
+ end
54
47
  end
55
- end
56
- end
57
48
  end
58
-
59
-
60
-
61
- class Relation
49
+ end
62
50
 
63
- def insert(values)
64
- primary_key_value = nil
51
+ module ConnectionAdapters
52
+ class SchemaDumper
53
+ private
54
+ def header(stream)
55
+ stream.puts <<~HEADER
56
+ ActiveRecord::Schema[#{ActiveRecord::Migration.current_version}].define(#{define_params}) do
57
+ HEADER
58
+ end
65
59
 
66
- if primary_key && Hash === values
67
- primary_key_value = values[values.keys.find { |k|
68
- k.name == primary_key
69
- }]
60
+ def default_primary_key?(column)
61
+ schema_type(column) == :integer
62
+ end
70
63
 
71
- if !primary_key_value && connection.prefetch_primary_key?(klass.table_name)
72
- primary_key_value = connection.next_sequence_value(klass.sequence_name)
73
- values[klass.arel_table[klass.primary_key]] = primary_key_value
74
- end
75
- end
64
+ def explicit_primary_key_default?(column)
65
+ column.bigint? and column.name == 'id'
66
+ end
67
+ end
76
68
 
77
- im = arel.create_insert
78
- im.into @table
69
+ class SchemaCreation
70
+ private
71
+ def visit_TableDefinition(o)
72
+ create_sql = +"CREATE#{table_modifier_in_create(o)} TABLE "
73
+ create_sql << "IF NOT EXISTS " if o.if_not_exists
74
+ create_sql << "#{quote_table_name(o.name)} "
79
75
 
80
- conn = @klass.connection
76
+ statements = o.columns.map { |c| accept c }
77
+ statements << accept(o.primary_keys) if o.primary_keys
81
78
 
82
- substitutes = values.sort_by { |arel_attr,_| arel_attr.name }
83
- binds = substitutes.map do |arel_attr, value|
84
- [@klass.columns_hash[arel_attr.name], value]
85
- end
79
+ if supports_indexes_in_create?
80
+ statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
81
+ end
86
82
 
87
- #substitutes.each_with_index do |tuple, i|
88
- # tuple[1] = conn.substitute_at(binds[i][0], i)
89
- #end
83
+ if supports_foreign_keys?
84
+ statements.concat(o.foreign_keys.map { |fk| accept fk })
85
+ end
90
86
 
91
- substitutes, binds = substitute_values values
87
+ if supports_check_constraints?
88
+ statements.concat(o.check_constraints.map { |chk| accept chk })
89
+ end
92
90
 
91
+ create_sql << "(#{statements.join(', ')})" if statements.present?
92
+ add_table_options!(create_sql, o)
93
+ create_sql << " AS (#{to_sql(o.as)}) WITH DATA" if o.as
94
+ create_sql
95
+ end
93
96
 
94
- if values.empty? # empty insert
95
- im.values = Arel.sql(connection.empty_insert_statement_value(klass.primary_key))
96
- else
97
- im.insert substitutes
98
- end
99
- conn.insert(
100
- im,
101
- 'SQL',
102
- primary_key,
103
- primary_key_value,
104
- nil,
105
- binds)
106
- end
107
- end
108
-
109
-
97
+ def add_column_options!(sql, options)
98
+ sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
99
+ if options[:auto_increment] == true
100
+ sql << " GENERATED BY DEFAULT AS IDENTITY (START WITH 1000)"
101
+ end
102
+ if options[:primary_key] == true
103
+ sql << " PRIMARY KEY"
104
+ end
105
+ # must explicitly check for :null to allow change_column to work on migrations
106
+ if options[:null] == false
107
+ sql << " NOT NULL"
108
+ end
109
+ sql
110
+ end
111
+ end
112
+ end
113
+
114
+ class Relation
115
+
116
+ def insert(values)
117
+ primary_key_value = nil
118
+
119
+ if primary_key && Hash === values
120
+ primary_key_value = values[values.keys.find { |k|
121
+ k.name == primary_key
122
+ }]
123
+
124
+ if !primary_key_value && connection.prefetch_primary_key?(klass.table_name)
125
+ primary_key_value = connection.next_sequence_value(klass.sequence_name)
126
+ values[klass.arel_table[klass.primary_key]] = primary_key_value
127
+ end
128
+ end
129
+
130
+ im = arel.create_insert
131
+ im.into @table
132
+
133
+ conn = @klass.connection
134
+ substitutes = values.sort_by { |arel_attr,_| arel_attr.name }
135
+ binds = substitutes.map do |arel_attr, value|
136
+ [@klass.columns_hash[arel_attr.name], value]
137
+ end
110
138
 
111
- class Base
139
+ substitutes, binds = substitute_values values
140
+ if values.empty? # empty insert
141
+ im.values = Arel.sql(connection.empty_insert_statement_value(klass.primary_key, klass.table_name))
142
+ else
143
+ im.insert substitutes
144
+ end
145
+
146
+ conn.insert(im, 'SQL', primary_key, primary_key_value, nil, binds)
147
+ end
148
+ end
149
+
150
+ class Base
112
151
  # Method required to handle LOBs and XML fields.
113
152
  # An after save callback checks if a marker has been inserted through
114
153
  # the insert or update, and then proceeds to update that record with
115
154
  # the actual large object through a prepared statement (param binding).
116
155
  after_save :handle_lobs
117
- def handle_lobs()
118
- if self.class.connection.kind_of?(ConnectionAdapters::IBM_DBAdapter)
119
- # Checks that the insert or update had at least a BLOB, CLOB or XML field
120
- self.class.connection.sql.each do |clob_sql|
121
- if clob_sql =~ /BLOB\('(.*)'\)/i ||
122
- clob_sql =~ /@@@IBMTEXT@@@/i ||
123
- clob_sql =~ /@@@IBMXML@@@/i ||
124
- clob_sql =~ /@@@IBMBINARY@@@/i
125
- update_query = "UPDATE #{self.class.table_name} SET ("
126
- counter = 0
127
- values = []
128
- params = []
129
- # Selects only binary, text and xml columns
130
- self.class.columns.select{|col| col.sql_type.to_s =~ /blob|binary|clob|text|xml/i }.each do |col|
131
-
132
- if counter == 0
133
- update_query << "#{col.name}"
134
- else
135
- update_query << ",#{col.name}"
136
- end
137
-
138
- # Add a '?' for the parameter or a NULL if the value is nil or empty
139
- # (except for a CLOB field where '' can be a value)
140
- if self[col.name].nil? ||
141
- self[col.name] == {} ||
142
- self[col.name] == [] ||
143
- (self[col.name] == '' && !(col.sql_type.to_s =~ /text|clob/i))
144
- params << 'NULL'
145
- else
146
- if (col.cast_type.is_a?(::ActiveRecord::Type::Serialized))
147
- values << YAML.dump(self[col.name])
148
- else
149
- values << self[col.name]
150
- end
151
- params << '?'
152
- end
153
- counter += 1
154
- end
155
- # no subsequent update is required if no relevant columns are found
156
- next if counter == 0
157
-
158
- update_query << ") = "
159
- # IBM_DB accepts 'SET (column) = NULL' but not (NULL),
160
- # therefore the sql needs to be changed for a single NULL field.
161
- if params.size==1 && params[0] == 'NULL'
162
- update_query << "NULL"
163
- else
164
- update_query << "(" + params.join(',') + ")"
165
- end
166
-
167
- update_query << " WHERE #{self.class.primary_key} = ?"
168
- values << self[self.class.primary_key.downcase]
156
+ def handle_lobs()
157
+ if self.class.connection.kind_of?(ConnectionAdapters::IBM_DBAdapter)
158
+ # Checks that the insert or update had at least a BLOB, CLOB or XML field
159
+ self.class.connection.sql.each do |clob_sql|
160
+ if clob_sql =~ /BLOB\('(.*)'\)/i ||
161
+ clob_sql =~ /@@@IBMTEXT@@@/i ||
162
+ clob_sql =~ /@@@IBMXML@@@/i ||
163
+ clob_sql =~ /@@@IBMBINARY@@@/i
164
+ update_query = "UPDATE #{self.class.table_name} SET ("
165
+ counter = 0
166
+ values = []
167
+ params = []
168
+ # Selects only binary, text and xml columns
169
+ self.class.columns.select{|col| col.sql_type.to_s =~ /blob|binary|clob|text|xml/i }.each do |col|
170
+ if counter == 0
171
+ update_query << "#{col.name}"
172
+ else
173
+ update_query << ",#{col.name}"
174
+ end
175
+
176
+ # Add a '?' for the parameter or a NULL if the value is nil or empty
177
+ # (except for a CLOB field where '' can be a value)
178
+ if self[col.name].nil? ||
179
+ self[col.name] == {} ||
180
+ self[col.name] == [] ||
181
+ (self[col.name] == '' && !(col.sql_type.to_s =~ /text|clob/i))
182
+ params << 'NULL'
183
+ else
184
+ if (col.cast_type.is_a?(::ActiveRecord::Type::Serialized))
185
+ values << YAML.dump(self[col.name])
186
+ else
187
+ values << self[col.name]
188
+ end
189
+ params << '?'
190
+ end
191
+ counter += 1
192
+ end
193
+
194
+ # no subsequent update is required if no relevant columns are found
195
+ next if counter == 0
196
+
197
+ update_query << ") = "
198
+ # IBM_DB accepts 'SET (column) = NULL' but not (NULL),
199
+ # therefore the sql needs to be changed for a single NULL field.
200
+ if params.size==1 && params[0] == 'NULL'
201
+ update_query << "NULL"
202
+ else
203
+ update_query << "(" + params.join(',') + ")"
204
+ end
205
+
206
+ update_query << " WHERE #{self.class.primary_key} = ?"
207
+ values << self[self.class.primary_key.downcase]
169
208
 
170
209
  begin
171
210
  unless stmt = IBM_DB.prepare(self.class.connection.connection, update_query)
@@ -176,14 +215,15 @@ module ActiveRecord
176
215
  raise StandardError.new('An unexpected error occurred during update of LOB/XML column')
177
216
  end
178
217
  end
179
- self.class.connection.log_query(update_query,'update of LOB/XML field(s)in handle_lobs')
218
+
219
+ self.class.connection.log_query(update_query,'update of LOB/XML field(s)in handle_lobs')
180
220
 
181
221
  # rollback any failed LOB/XML field updates (and remove associated marker)
182
222
  unless IBM_DB.execute(stmt, values)
183
223
  error_msg = "Failed to insert/update LOB/XML field(s) due to: #{IBM_DB.getErrormsg( stmt, IBM_DB::DB_STMT )}"
184
224
  self.class.connection.execute("ROLLBACK")
185
225
  raise error_msg
186
- end
226
+ end
187
227
  rescue StandardError => error
188
228
  raise error
189
229
  ensure
@@ -194,8 +234,8 @@ module ActiveRecord
194
234
  self.class.connection.handle_lobs_triggered = true
195
235
  end # if connection.kind_of?
196
236
  end # handle_lobs
197
- private :handle_lobs
198
237
 
238
+ private :handle_lobs
199
239
 
200
240
  # Establishes a connection to a specified database using the credentials provided
201
241
  # with the +config+ argument. All the ActiveRecord objects will use this connection
@@ -208,10 +248,6 @@ module ActiveRecord
208
248
  raise LoadError, "Failed to load IBM_DB Ruby driver."
209
249
  end
210
250
 
211
- #if( config.has_key?(:parameterized) && config[:parameterized] == true )
212
- # require 'active_record/connection_adapters/ibm_db_pstmt'
213
- # end
214
-
215
251
  # Check if class TableDefinition responds to indexes method to determine if we are on AR 3 or AR 4.
216
252
  # This is a interim hack ti ensure backward compatibility. To remove as we move out of AR 3 support or have a better way to determine which version of AR being run against.
217
253
  checkClass = ActiveRecord::ConnectionAdapters::TableDefinition.new(self,nil)
@@ -326,381 +362,308 @@ module ActiveRecord
326
362
  end
327
363
  end # class Base
328
364
 
329
-
330
-
331
365
  module ConnectionAdapters
332
366
  class Column
333
- def self.binary_to_string(value)
334
- # Returns a string removing the eventual BLOB scalar function
335
- value.to_s.gsub(/"SYSIBM"."BLOB"\('(.*)'\)/i,'\1')
336
- end
337
- end
367
+ def self.binary_to_string(value)
368
+ puts_log "binary_to_string"
369
+ # Returns a string removing the eventual BLOB scalar function
370
+ value.to_s.gsub(/"SYSIBM"."BLOB"\('(.*)'\)/i,'\1')
371
+ end
372
+ end
338
373
 
339
374
  module Quoting
340
- def lookup_cast_type_from_column(column) # :nodoc:
341
- lookup_cast_type(column.sql_type_metadata)
342
- end
375
+ def lookup_cast_type_from_column(column) # :nodoc:
376
+ lookup_cast_type(column.sql_type_metadata.sql_type)
377
+ end
343
378
  end
344
379
 
345
380
  module Savepoints
346
- def create_savepoint(name = current_savepoint_name)
347
- execute("SAVEPOINT #{name} ON ROLLBACK RETAIN CURSORS")
348
- end
381
+ def create_savepoint(name = current_savepoint_name)
382
+ puts_log "create_savepoint"
383
+ execute("SAVEPOINT #{name} ON ROLLBACK RETAIN CURSORS", "TRANSACTION")
384
+ end
349
385
  end
350
386
 
351
387
 
352
388
  module ColumnDumper
353
389
  def prepare_column_options(column)
354
- spec = {}
355
-
356
- if limit = schema_limit(column)
357
- spec[:limit] = limit
358
- end
359
-
360
- if precision = schema_precision(column)
361
- spec[:precision] = precision
362
- end
390
+ puts_log "prepare_column_options"
391
+ spec = {}
392
+
393
+ if limit = schema_limit(column)
394
+ spec[:limit] = limit
395
+ end
396
+
397
+ if precision = schema_precision(column)
398
+ spec[:precision] = precision
399
+ end
363
400
 
364
- if scale = schema_scale(column)
365
- spec[:scale] = scale
366
- end
367
-
368
- default = schema_default(column) if column.has_default?
369
- spec[:default] = default unless default.nil?
401
+ if scale = schema_scale(column)
402
+ spec[:scale] = scale
403
+ end
370
404
 
371
- spec[:null] = 'false' unless column.null
405
+ default = schema_default(column) if column.has_default?
406
+ spec[:default] = default unless default.nil?
407
+ spec[:null] = 'false' unless column.null
372
408
 
373
- if collation = schema_collation(column)
374
- spec[:collation] = collation
375
- end
409
+ if collation = schema_collation(column)
410
+ spec[:collation] = collation
411
+ end
376
412
 
377
- spec[:comment] = column.comment.inspect if column.comment.present?
413
+ spec[:comment] = column.comment.inspect if column.comment.present?
378
414
 
379
- spec
380
- end
381
-
382
-
383
- def schema_limit(column)
384
- limit = column.limit unless column.bigint?
385
- #limit.inspect if limit && limit != native_database_types[column.type][:limit]
386
-
387
- limit.inspect if limit && limit != native_database_types[column.type.to_sym][:limit]
388
-
389
- end
390
-
391
- =begin
392
- def column_spec_for_primary_key(column)
393
- if column.bigint?
394
- spec = { id: :bigint.inspect }
395
- spec[:default] = schema_default(column) || 'nil' unless column.auto_increment?
396
- else
397
- #spec = super
398
- end
399
- #spec[:unsigned] = 'true' if column.unsigned?
400
- #spec
401
- ""
402
- end
403
- =end
415
+ spec
416
+ end
404
417
 
405
- end
418
+ def schema_limit(column)
419
+ puts_log "schema_limit"
420
+ limit = column.limit unless column.bigint?
421
+ limit.inspect if limit && limit != native_database_types[column.type.to_sym][:limit]
422
+ end
423
+ end #end of module ColumnDumper
406
424
 
407
425
  module SchemaStatements
408
-
409
- def internal_string_options_for_primary_key # :nodoc:
410
- { primary_key: true}
411
- { version_options: "PRIMARY KEY NOT NULL"}
412
- end
426
+
427
+ def internal_string_options_for_primary_key # :nodoc:
428
+ { primary_key: true, null: false }
429
+ end
413
430
 
414
- def drop_table(table_name,options={})
415
- execute("DROP TABLE #{quote_table_name(table_name)}", options)
416
- #execute("DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
417
- end
431
+ def drop_table(table_name, options={})
432
+ puts_log "drop_table - #{table_name}"
433
+ if options[:if_exists]
434
+ execute("DROP TABLE IF EXISTS #{quote_table_name(table_name)}")
435
+ else
436
+ execute("DROP TABLE #{quote_table_name(table_name)}", options)
437
+ end
438
+ end
418
439
 
419
- =begin
420
- def create_table_definition(name, temporary, options,as = nil)
421
- TableDefinition.new self, name, temporary, options
440
+ def create_table_definition(*args, **options)
441
+ puts_log "create_table_definition"
442
+ TableDefinition.new(self, *args, **options)
422
443
  end
423
- =end
424
- def create_table_definition(*args, **options)
425
- TableDefinition.new(self, *args, **options)
426
- end
427
444
 
428
- def remove_foreign_key(from_table, options_or_to_table = {})
429
- return unless supports_foreign_keys?
430
-
431
- if options_or_to_table.is_a?(Hash)
432
- options = options_or_to_table
433
- else
434
- options = { column: foreign_key_column_for(options_or_to_table) }
435
- end
436
-
437
- fk_name_to_delete = options.fetch(:name) do
438
- fk_to_delete = foreign_keys(@servertype.set_case(from_table)).detect {|fk| "#{@servertype.set_case(fk.column)}" == "#{servertype.set_case(options[:column])}"}
439
-
440
- if fk_to_delete
441
- fk_to_delete.name
442
- else
443
- raise ArgumentError, "Table '#{from_table}' has no foreign key on column '#{options[:column]}'"
444
- end
445
- end
446
-
447
- at = create_alter_table from_table
448
- at.drop_foreign_key fk_name_to_delete
449
-
450
- execute schema_creation.accept(at)
451
- end
452
445
  end #end of Module SchemaStatements
453
-
454
-
455
- #class IBM_DBColumn < Column
456
- class IBM_DBColumn < ConnectionAdapters::Column # :nodoc:
457
- # delegate :precision, :scale, :limit, :type, :sql_type, to: :sql_type_metadata, allow_nil: true
458
-
459
- def initialize(*)
460
- super
461
- end
462
-
463
- #def initialize(column_name, column_default_value, sqltype_metadata, column_nullable, table_name, default_function, collation, comment)
464
- #super(column_name, column_default_value, sqltype_metadata, column_nullable, table_name)
465
- #end
466
-
467
- # Casts value (which is a String) to an appropriate instance
468
- =begin
469
- def type_cast(value)
470
- # Casts the database NULL value to nil
471
- return nil if value == 'NULL'
472
- # Invokes parent's method for default casts
446
+
447
+ class IBM_DBColumn < ConnectionAdapters::Column # :nodoc:
448
+ def initialize(*)
449
+ puts_log "15"
473
450
  super
474
451
  end
475
- =end
476
452
 
477
- # Used to convert from BLOBs to Strings
478
- def self.binary_to_string(value)
479
- # Returns a string removing the eventual BLOB scalar function
480
- value.to_s.gsub(/"SYSIBM"."BLOB"\('(.*)'\)/i,'\1')
481
- end
482
-
453
+ # Used to convert from BLOBs to Strings
454
+ def self.binary_to_string(value)
455
+ # Returns a string removing the eventual BLOB scalar function
456
+ value.to_s.gsub(/"SYSIBM"."BLOB"\('(.*)'\)/i,'\1')
457
+ end
483
458
  end #class IBM_DBColumn
484
459
 
485
-
486
- module ColumnMethods
487
-
460
+ module ColumnMethods
488
461
  def primary_key(name, type = :primary_key, **options)
489
- column(name, type, options.merge(primary_key: true))
490
- end
462
+ puts_log "16"
463
+ column(name, type, options.merge(primary_key: true))
464
+ end
491
465
 
492
- ##class Table
493
- class Table < ActiveRecord::ConnectionAdapters::Table
494
- include ColumnMethods
466
+ ##class Table
467
+ class Table < ActiveRecord::ConnectionAdapters::Table
468
+ include ColumnMethods
495
469
 
496
- #Method to parse the passed arguments and create the ColumnDefinition object of the specified type
497
- def ibm_parse_column_attributes_args(type, *args)
498
- options = {}
499
- if args.last.is_a?(Hash)
500
- options = args.delete_at(args.length-1)
501
- end
502
- args.each do | name |
503
- column name,type.to_sym,options
504
- end # end args.each
505
- end
506
- private :ibm_parse_column_attributes_args
470
+ #Method to parse the passed arguments and create the ColumnDefinition object of the specified type
471
+ def ibm_parse_column_attributes_args(type, *args)
472
+ puts_log "ibm_parse_column_attributes_args"
473
+ options = {}
474
+ if args.last.is_a?(Hash)
475
+ options = args.delete_at(args.length-1)
476
+ end
477
+ args.each do | name |
478
+ column name,type.to_sym,options
479
+ end # end args.each
480
+ end
481
+ private :ibm_parse_column_attributes_args
507
482
 
508
- #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type xml
509
- #This method is different as compared to def char (sql is being issued explicitly
510
- #as compared to def char where method column(which will generate the sql is being called)
511
- #in order to handle the DEFAULT and NULL option for the native XML datatype
512
- def xml(*args )
513
- options = {}
514
- if args.last.is_a?(Hash)
515
- options = args.delete_at(args.length-1)
516
- end
517
- sql_segment = "ALTER TABLE #{@base.quote_table_name(@table_name)} ADD COLUMN "
518
- args.each do | name |
519
- sql = sql_segment + " #{@base.quote_column_name(name)} xml"
520
- @base.execute(sql,"add_xml_column")
521
- end
522
- return self
523
- end
524
-
525
- #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type double
526
- def double(*args)
527
- ibm_parse_column_attributes_args('double',*args)
528
- return self
529
- end
530
-
531
- #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type decfloat
532
- def decfloat(*args)
533
- ibm_parse_column_attributes_args('decfloat',*args)
534
- return self
535
- end
536
-
537
- def graphic(*args)
538
- ibm_parse_column_attributes_args('graphic',*args)
539
- return self
540
- end
483
+ #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type xml
484
+ #This method is different as compared to def char (sql is being issued explicitly
485
+ #as compared to def char where method column(which will generate the sql is being called)
486
+ #in order to handle the DEFAULT and NULL option for the native XML datatype
487
+ def xml(*args )
488
+ puts_log "18"
489
+ options = {}
490
+ if args.last.is_a?(Hash)
491
+ options = args.delete_at(args.length-1)
492
+ end
493
+ sql_segment = "ALTER TABLE #{@base.quote_table_name(@table_name)} ADD COLUMN "
494
+ args.each do | name |
495
+ sql = sql_segment + " #{@base.quote_column_name(name)} xml"
496
+ @base.execute(sql,"add_xml_column")
497
+ end
498
+ return self
499
+ end
500
+
501
+ #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type double
502
+ def double(*args)
503
+ puts_log "19"
504
+ ibm_parse_column_attributes_args('double',*args)
505
+ return self
506
+ end
541
507
 
542
- def vargraphic(*args)
543
- ibm_parse_column_attributes_args('vargraphic',*args)
544
- return self
545
- end
508
+ #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type decfloat
509
+ def decfloat(*args)
510
+ puts_log "20"
511
+ ibm_parse_column_attributes_args('decfloat',*args)
512
+ return self
513
+ end
546
514
 
547
- def bigint(*args)
548
- ibm_parse_column_attributes_args('bigint',*args)
549
- return self
550
- end
515
+ def graphic(*args)
516
+ puts_log "21"
517
+ ibm_parse_column_attributes_args('graphic',*args)
518
+ return self
519
+ end
551
520
 
552
- #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type char [character]
553
- def char(*args)
554
- ibm_parse_column_attributes_args('char',*args)
555
- return self
556
- end
557
- alias_method :character, :char
521
+ def vargraphic(*args)
522
+ puts_log "22"
523
+ ibm_parse_column_attributes_args('vargraphic',*args)
524
+ return self
558
525
  end
559
526
 
560
- #class TableDefinition
561
- class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
562
- include ColumnMethods
527
+ def bigint(*args)
528
+ puts_log "23"
529
+ ibm_parse_column_attributes_args('bigint',*args)
530
+ return self
531
+ end
563
532
 
564
- =begin
565
- def initialize(base, name=nil, temporary=nil, options=nil)
533
+ #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type char [character]
534
+ def char(*args)
535
+ puts_log "24"
536
+ ibm_parse_column_attributes_args('char',*args)
537
+ return self
538
+ end
539
+ alias_method :character, :char
566
540
 
567
- if(self.respond_to?(:indexes))
568
- @ar3 = false
569
- else
570
- @ar3 = true
571
- end
541
+ end # end of class Table
572
542
 
573
- @columns = []
574
- @columns_hash = {}
575
- @indexes = {}
576
- @base = base
577
- @temporary = temporary
578
- @options = options
579
- @name = name
580
- @foreign_keys = {}
581
- end
582
- =end
583
-
584
- def initialize(conn, name, temporary = false, options = nil, as = nil, comment: nil)
585
- @connection = conn
586
- @columns_hash = {}
587
- @indexes = []
588
- @foreign_keys = []
589
- @primary_keys = nil
590
- @temporary = temporary
591
- @options = options
592
- @as = as
593
- @name = name
594
- @comment = comment
595
- ##
596
- #@base = base
597
- end
598
-
599
- def primary_keys(name = nil) # :nodoc:
600
- @primary_keys = PrimaryKeyDefinition.new(name) if name
601
- @primary_keys
602
- end
543
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
544
+ include ColumnMethods
603
545
 
604
- def native
605
- @base.native_database_types
606
- end
546
+ def native
547
+ puts_log "25"
548
+ @base.native_database_types
549
+ end
607
550
 
608
- #Method to parse the passed arguments and create the ColumnDefinition object of the specified type
609
- def ibm_parse_column_attributes_args(type, *args)
610
- options = {}
611
- if args.last.is_a?(Hash)
612
- options = args.delete_at(args.length-1)
613
- end
614
- args.each do | name |
615
- column(name,type,options)
616
- end
551
+ #Method to parse the passed arguments and create the ColumnDefinition object of the specified type
552
+ def ibm_parse_column_attributes_args(type, *args)
553
+ puts_log "26"
554
+ options = {}
555
+ if args.last.is_a?(Hash)
556
+ options = args.delete_at(args.length-1)
617
557
  end
618
- private :ibm_parse_column_attributes_args
619
-
620
- #Method to support the new syntax of rails 2.0 migrations for columns of type xml
621
- def xml(*args )
622
- ibm_parse_column_attributes_args('xml', *args)
623
- return self
558
+ args.each do | name |
559
+ column(name,type,options)
624
560
  end
561
+ end
562
+ private :ibm_parse_column_attributes_args
625
563
 
626
- #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type double
627
- def double(*args)
628
- ibm_parse_column_attributes_args('double',*args)
629
- return self
630
- end
564
+ #Method to support the new syntax of rails 2.0 migrations for columns of type xml
565
+ def xml(*args )
566
+ puts_log "27"
567
+ ibm_parse_column_attributes_args('xml', *args)
568
+ return self
569
+ end
631
570
 
632
- #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type decfloat
633
- def decfloat(*args)
634
- ibm_parse_column_attributes_args('decfloat',*args)
635
- return self
636
- end
571
+ #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type double
572
+ def double(*args)
573
+ puts_log "28"
574
+ ibm_parse_column_attributes_args('double',*args)
575
+ return self
576
+ end
637
577
 
638
- def graphic(*args)
639
- ibm_parse_column_attributes_args('graphic',*args)
640
- return self
641
- end
578
+ #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type decfloat
579
+ def decfloat(*args)
580
+ puts_log "29"
581
+ ibm_parse_column_attributes_args('decfloat',*args)
582
+ return self
583
+ end
642
584
 
643
- def vargraphic(*args)
644
- ibm_parse_column_attributes_args('vargraphic',*args)
645
- return self
646
- end
585
+ def graphic(*args)
586
+ puts_log "30"
587
+ ibm_parse_column_attributes_args('graphic',*args)
588
+ return self
589
+ end
647
590
 
648
- def bigint(*args)
649
- ibm_parse_column_attributes_args('bigint',*args)
650
- return self
651
- end
591
+ def vargraphic(*args)
592
+ puts_log "31"
593
+ ibm_parse_column_attributes_args('vargraphic',*args)
594
+ return self
595
+ end
652
596
 
653
- #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type char [character]
654
- def char(*args)
655
- ibm_parse_column_attributes_args('char',*args)
656
- return self
657
- end
658
- alias_method :character, :char
597
+ def bigint(*args)
598
+ puts_log "32"
599
+ ibm_parse_column_attributes_args('bigint',*args)
600
+ return self
601
+ end
602
+
603
+ #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type char [character]
604
+ def char(*args)
605
+ puts_log "33"
606
+ ibm_parse_column_attributes_args('char',*args)
607
+ return self
608
+ end
609
+ alias_method :character, :char
610
+
611
+ # Overrides the abstract adapter in order to handle
612
+ # the DEFAULT option for the native XML datatype
613
+ def column(name, type, index: nil, **options)
614
+ puts_log "34 column"
615
+ name = name.to_s
616
+ type = type.to_sym if type
617
+
618
+ if @columns_hash[name]
619
+ if @columns_hash[name].primary_key?
620
+ raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
621
+ else
622
+ raise ArgumentError, "you can't define an already defined column '#{name}'."
623
+ end
624
+ end
659
625
 
660
- # Overrides the abstract adapter in order to handle
661
- # the DEFAULT option for the native XML datatype
662
- def column(name, type, options ={})
663
- # construct a column definition where @base is adaptor instance
664
- column = ColumnDefinition.new(name, type)
626
+ # construct a column definition where @base is adaptor instance
627
+ column = new_column_definition(name, type, **options)
665
628
 
666
- # DB2 does not accept DEFAULT NULL option for XML
667
- # for table create, but does accept nullable option
668
- unless type.to_s == 'xml'
669
- column.null = options[:null]
670
- column.default = options[:default]
671
- else
672
- column.null = options[:null]
673
- # Override column object's (instance of ColumnDefinition structure)
674
- # to_s which is expected to return the create_table SQL fragment
675
- # and bypass DEFAULT NULL option while still appending NOT NULL
676
- def column.to_s
677
- sql = "#{base.quote_column_name(name)} #{type}"
678
- unless self.null == nil
679
- sql << " NOT NULL" if (self.null == false)
680
- end
681
- return sql
629
+ # DB2 does not accept DEFAULT NULL option for XML
630
+ # for table create, but does accept nullable option
631
+ unless type.to_s == 'xml'
632
+ column.null = options[:null]
633
+ column.default = options[:default]
634
+ else
635
+ column.null = options[:null]
636
+ # Override column object's (instance of ColumnDefinition structure)
637
+ # to_s which is expected to return the create_table SQL fragment
638
+ # and bypass DEFAULT NULL option while still appending NOT NULL
639
+ def column.to_s
640
+ sql = "#{base.quote_column_name(name)} #{type}"
641
+ unless self.null == nil
642
+ sql << " NOT NULL" if (self.null == false)
682
643
  end
644
+ return sql
683
645
  end
646
+ end
684
647
 
685
- column.scale = options[:scale] if options[:scale]
686
- column.precision = options[:precision] if options[:precision]
687
- # append column's limit option and yield native limits
688
- if options[:limit]
689
- column.limit = options[:limit]
690
- elsif @base.native_database_types[type.to_sym]
691
- column.limit = @base.native_database_types[type.to_sym][:limit] if @base.native_database_types[type.to_sym].has_key? :limit
692
- end
648
+ column.scale = options[:scale] if options[:scale]
649
+ column.precision = options[:precision] if options[:precision]
650
+ # append column's limit option and yield native limits
651
+ if options[:limit]
652
+ column.limit = options[:limit]
653
+ elsif @base.native_database_types[type.to_sym]
654
+ column.limit = @base.native_database_types[type.to_sym][:limit] if @base.native_database_types[type.to_sym].has_key? :limit
655
+ end
693
656
 
694
- unless @columns.nil? or @columns.include? column
695
- @columns << column
696
- end
657
+ unless @columns.nil? or @columns.include? column
658
+ @columns << column
659
+ end
697
660
 
698
- @columns_hash[name] = column
661
+ @columns_hash[name] = column
699
662
 
700
- return self
701
- end
663
+ return self
702
664
  end
703
- end
665
+ end #end of class TableDefinition
666
+ end #end of module ColumnMethods
704
667
 
705
668
  # The IBM_DB Adapter requires the native Ruby driver (ibm_db)
706
669
  # for IBM data servers (ibm_db.so).
@@ -743,24 +706,36 @@ module ActiveRecord
743
706
  'IBM_DB'
744
707
  end
745
708
 
746
- class BindSubstitution < Arel::Visitors::IBM_DB # :nodoc:
747
- include Arel::Visitors
709
+ class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
710
+ private
711
+ def dealloc(stmt)
712
+ #stmt.close unless stmt.closed?
713
+ end
714
+ end
715
+
716
+ def build_statement_pool
717
+ StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
748
718
  end
749
719
 
750
720
  def initialize(connection, ar3, logger, config, conn_options)
751
721
  # Caching database connection configuration (+connect+ or +reconnect+ support)\
752
- @config = config
722
+ @config = config
753
723
  @connection = connection
754
724
  @isAr3 = ar3
755
725
  @conn_options = conn_options
756
726
  @database = config[:database]
757
727
  @username = config[:username]
758
728
  @password = config[:password]
729
+ @debug = config[:debug]
759
730
  if config.has_key?(:host)
760
731
  @host = config[:host]
761
732
  @port = config[:port] || 50000 # default port
762
733
  end
763
- @schema = config[:schema]
734
+ if config.has_key?(:schema)
735
+ @schema = config[:schema]
736
+ else
737
+ @schema = config[:username]
738
+ end
764
739
  @security = config[:security] || nil
765
740
  @authentication = config[:authentication] || nil
766
741
  @timeout = config[:timeout] || 0 # default timeout value is 0
@@ -800,7 +775,7 @@ module ActiveRecord
800
775
  @servertype = IBM_DB2_ZOS.new(self, @isAr3)
801
776
  when /10/
802
777
  @servertype = IBM_DB2_ZOS.new(self, @isAr3)
803
- when /11/
778
+ when /11/
804
779
  @servertype = IBM_DB2_ZOS.new(self, @isAr3)
805
780
  when /12/
806
781
  @servertype = IBM_DB2_ZOS.new(self, @isAr3)
@@ -818,6 +793,7 @@ module ActiveRecord
818
793
  warn "Forcing servertype to LUW: DBMS name could not be retrieved. Check if your client version is of the right level"
819
794
  @servertype = IBM_DB2_LUW.new(self, @isAr3)
820
795
  end
796
+ @database_version = server_info.DBMS_VER
821
797
  else
822
798
  error_msg = IBM_DB.getErrormsg( @connection, IBM_DB::DB_CONN )
823
799
  IBM_DB.close( @connection )
@@ -857,8 +833,25 @@ module ActiveRecord
857
833
  end
858
834
  end
859
835
 
836
+ def get_database_version
837
+ @database_version
838
+ end
839
+
840
+ def prepared_statements?
841
+ puts_log "prepared_statements?"
842
+ prepare = @prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
843
+ puts_log "prepare = #{prepare}"
844
+ prepare
845
+ end
846
+ alias :prepared_statements :prepared_statements?
847
+
848
+ def bind_params_length
849
+ 999
850
+ end
851
+
860
852
  # Optional connection attribute: database name space qualifier
861
853
  def schema=(name)
854
+ puts_log "schema="
862
855
  unless name == @schema
863
856
  @schema = name
864
857
  @servertype.set_schema(@schema)
@@ -867,6 +860,7 @@ module ActiveRecord
867
860
 
868
861
  # Optional connection attribute: authenticated application user
869
862
  def app_user=(name)
863
+ puts_log "app_user="
870
864
  unless name == @app_user
871
865
  option = {IBM_DB::SQL_ATTR_INFO_USERID => "#{name}"}
872
866
  if IBM_DB.set_option( @connection, option, 1 )
@@ -877,6 +871,7 @@ module ActiveRecord
877
871
 
878
872
  # Optional connection attribute: OS account (client workstation)
879
873
  def account=(name)
874
+ puts_log "account="
880
875
  unless name == @account
881
876
  option = {IBM_DB::SQL_ATTR_INFO_ACCTSTR => "#{name}"}
882
877
  if IBM_DB.set_option( @connection, option, 1 )
@@ -887,6 +882,7 @@ module ActiveRecord
887
882
 
888
883
  # Optional connection attribute: application name
889
884
  def application=(name)
885
+ puts_log "application="
890
886
  unless name == @application
891
887
  option = {IBM_DB::SQL_ATTR_INFO_APPLNAME => "#{name}"}
892
888
  if IBM_DB.set_option( @connection, option, 1 )
@@ -897,6 +893,7 @@ module ActiveRecord
897
893
 
898
894
  # Optional connection attribute: client workstation name
899
895
  def workstation=(name)
896
+ puts_log "workstation="
900
897
  unless name == @workstation
901
898
  option = {IBM_DB::SQL_ATTR_INFO_WRKSTNNAME => "#{name}"}
902
899
  if IBM_DB.set_option( @connection, option, 1 )
@@ -906,61 +903,101 @@ module ActiveRecord
906
903
  end
907
904
 
908
905
  def self.visitor_for(pool)
906
+ puts_log "visitor_for"
909
907
  Arel::Visitors::IBM_DB.new(pool)
910
908
  end
911
909
 
912
- #Check Arel version
910
+ #Check Arel version
913
911
  begin
914
912
  @arelVersion = Arel::VERSION.to_i
915
913
  rescue
916
914
  @arelVersion = 0
917
915
  end
918
916
  if(@arelVersion < 6 )
919
- def to_sql(arel, binds = [])
920
- if arel.respond_to?(:ast)
921
- visitor.accept(arel.ast) do
922
- quote(*binds.shift.reverse)
923
- end
924
- else
925
- arel
917
+ def to_sql(arel, binds = [])
918
+ if arel.respond_to?(:ast)
919
+ visitor.accept(arel.ast) do
920
+ quote(*binds.shift.reverse)
926
921
  end
922
+ else
923
+ arel
927
924
  end
928
- end
925
+ end
926
+ end
927
+
929
928
  # This adapter supports migrations.
930
929
  # Current limitations:
931
930
  # +rename_column+ is not currently supported by the IBM data servers
932
931
  # +remove_column+ is not currently supported by the DB2 for zOS data server
933
932
  # Tables containing columns of XML data type do not support +remove_column+
934
933
  def supports_migrations?
934
+ puts_log "supports_migrations?"
935
935
  true
936
936
  end
937
937
 
938
938
  def supports_foreign_keys?
939
+ puts_log "supports_foreign_keys?"
939
940
  true
940
941
  end
941
942
 
942
943
  def supports_datetime_with_precision?
943
- true
944
+ puts_log "supports_datetime_with_precision?"
945
+ true
944
946
  end
945
947
 
946
948
  # This Adapter supports DDL transactions.
947
949
  # This means CREATE TABLE and other DDL statements can be carried out as a transaction.
948
950
  # That is the statements executed can be ROLLED BACK in case of any error during the process.
949
951
  def supports_ddl_transactions?
952
+ puts_log "supports_ddl_transactions?"
953
+ true
954
+ end
955
+
956
+ def supports_explain?
957
+ puts_log "supports_explain?"
958
+ true
959
+ end
960
+
961
+ def supports_lazy_transactions?
962
+ puts_log "supports_lazy_transactions?"
963
+ true
964
+ end
965
+
966
+ def supports_comments?
967
+ true
968
+ end
969
+
970
+ def supports_views?
950
971
  true
951
972
  end
952
973
 
953
974
  def log_query(sql, name) #:nodoc:
975
+ puts_log "log_query"
954
976
  # Used by handle_lobs
955
977
  log(sql,name){}
956
978
  end
957
979
 
980
+ def supports_partitioned_indexes?
981
+ true
982
+ end
983
+
984
+ def puts_log (val)
985
+ begin
986
+ # puts val
987
+ rescue
988
+ end
989
+ if @debug == true
990
+ log(" IBM_DB = #{val}", "TRANSACTION"){}
991
+ end
992
+ end
993
+
958
994
  #==============================================
959
995
  # CONNECTION MANAGEMENT
960
996
  #==============================================
961
997
 
962
998
  # Tests the connection status
963
999
  def active?
1000
+ puts_log "active?"
964
1001
  IBM_DB.active @connection
965
1002
  rescue
966
1003
  false
@@ -969,6 +1006,7 @@ module ActiveRecord
969
1006
  # Private method used by +reconnect!+.
970
1007
  # It connects to the database with the initially provided credentials
971
1008
  def connect
1009
+ puts_log "connect"
972
1010
  # If the type of connection is net based
973
1011
  if(@username.nil? || @password.nil?)
974
1012
  raise ArgumentError, "Username/Password cannot be nil"
@@ -1006,6 +1044,7 @@ module ActiveRecord
1006
1044
 
1007
1045
  # Closes the current connection and opens a new one
1008
1046
  def reconnect!
1047
+ puts_log "reconnect!"
1009
1048
  disconnect!
1010
1049
  connect
1011
1050
  end
@@ -1016,100 +1055,50 @@ module ActiveRecord
1016
1055
  # * true if succesfull
1017
1056
  # * false if the connection is already closed
1018
1057
  # * nil if an error is raised
1058
+ puts_log "disconnect!"
1019
1059
  return nil if @connection.nil? || @connection == false
1020
1060
  IBM_DB.close(@connection) rescue nil
1061
+ @connection = nil
1062
+ reset_transaction
1021
1063
  end
1022
1064
 
1023
1065
  #==============================================
1024
1066
  # DATABASE STATEMENTS
1025
1067
  #==============================================
1026
1068
 
1027
- def create_table(name, options = {})
1069
+ def create_table(name, id: :primary_key, primary_key: nil, force: nil, **options)
1070
+ puts_log "create_table name=#{name}, id=#{id}, primary_key=#{primary_key}, force=#{force}"
1071
+ puts_log "create_table Options = #{options}"
1072
+ puts_log "primary_key_prefix_type = #{ActiveRecord::Base.primary_key_prefix_type}"
1073
+ puts_log caller
1028
1074
  @servertype.setup_for_lob_table
1029
1075
  #Table definition is complete only when a unique index is created on the primarykey column for DB2 V8 on zOS
1030
1076
 
1031
1077
  #create index on id column if options[:id] is nil or id ==true
1032
1078
  #else check if options[:primary_key]is not nil then create an unique index on that column
1033
- if !options[:id].nil? || !options[:primary_key].nil?
1034
- if (!options[:id].nil? && options[:id] == true)
1035
- @servertype.create_index_after_table(name,"id")
1036
- elsif !options[:primary_key].nil?
1037
- @servertype.create_index_after_table(name,options[:primary_key].to_s)
1079
+ if !id.nil? || !primary_key.nil?
1080
+ if (!id.nil? && id == true)
1081
+ @servertype.create_index_after_table(name,"id")
1082
+ elsif !primary_key.nil?
1083
+ @servertype.create_index_after_table(name,primary_key.to_s)
1038
1084
  end
1039
1085
  else
1040
- @servertype.create_index_after_table(name,"id")
1086
+ @servertype.create_index_after_table(name,"id")
1041
1087
  end
1042
- super(name, options)
1043
- end
1044
-
1045
- # Returns an array of hashes with the column names as keys and
1046
- # column values as values. +sql+ is the select query,
1047
- # and +name+ is an optional description for logging
1048
- def prepared_select(sql_param_hash, name = nil)
1049
- # Replaces {"= NULL" with " IS NULL"} OR {"IN (NULL)" with " IS NULL"}
1050
1088
 
1051
- results = []
1052
- # Invokes the method +prepare+ in order prepare the SQL
1053
- # IBM_DB.Statement is returned from which the statement is executed and results fetched
1054
- pstmt = prepare(sql_param_hash["sqlSegment"], name)
1055
- if(execute_prepared_stmt(pstmt, sql_param_hash["paramArray"]))
1056
- begin
1057
- results = @servertype.select(pstmt)
1058
- rescue StandardError => fetch_error # Handle driver fetch errors
1059
- error_msg = IBM_DB.getErrormsg(pstmt, IBM_DB::DB_STMT )
1060
- if error_msg && !error_msg.empty?
1061
- raise StatementInvalid,"Failed to retrieve data: #{error_msg}"
1062
- else
1063
- error_msg = "An unexpected error occurred during data retrieval"
1064
- error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
1065
- raise error_msg
1066
- end
1067
- ensure
1068
- # Ensures to free the resources associated with the statement
1069
- IBM_DB.free_stmt(pstmt) if pstmt
1070
- end
1089
+ #Just incase if id holds any other data type other than primary_key we override it,
1090
+ #otherwise it misses "GENERATED BY DEFAULT AS IDENTITY (START WITH 1000)"
1091
+ if !id.nil? && id != false && primary_key.nil? && ActiveRecord::Base.primary_key_prefix_type.nil?
1092
+ primary_key = :id
1093
+ options[:auto_increment] = true if options[:auto_increment].nil? and (id == :integer or id == :bigint)
1071
1094
  end
1072
- # The array of record hashes is returned
1073
- results
1074
- end
1075
1095
 
1076
- # Returns an array of hashes with the column names as keys and
1077
- # column values as values. +sql+ is the select query,
1078
- # and +name+ is an optional description for logging
1079
- def prepared_select_values(sql_param_hash, name = nil)
1080
- # Replaces {"= NULL" with " IS NULL"} OR {"IN (NULL)" with " IS NULL"}
1081
- results = []
1082
- # Invokes the method +prepare+ in order prepare the SQL
1083
- # IBM_DB.Statement is returned from which the statement is executed and results fetched
1084
- pstmt = prepare(sql_param_hash["sqlSegment"], name)
1085
- if(execute_prepared_stmt(pstmt, sql_param_hash["paramArray"]))
1086
- begin
1087
- results = @servertype.select_rows(sql_param_hash["sqlSegment"], name, pstmt, results)
1088
- if results
1089
- return results.map { |v| v[0] }
1090
- else
1091
- nil
1092
- end
1093
- rescue StandardError => fetch_error # Handle driver fetch errors
1094
- error_msg = IBM_DB.getErrormsg(pstmt, IBM_DB::DB_STMT )
1095
- if error_msg && !error_msg.empty?
1096
- raise StatementInvalid,"Failed to retrieve data: #{error_msg}"
1097
- else
1098
- error_msg = "An unexpected error occurred during data retrieval"
1099
- error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
1100
- raise error_msg
1101
- end
1102
- ensure
1103
- # Ensures to free the resources associated with the statement
1104
- IBM_DB.free_stmt(pstmt) if pstmt
1105
- end
1106
- end
1107
- # The array of record hashes is returned
1108
- results
1096
+ super(name, id: id, primary_key: primary_key, force: force, **options)
1109
1097
  end
1110
1098
 
1111
1099
  #Calls the servertype select method to fetch the data
1112
1100
  def fetch_data(stmt)
1101
+ puts_log "fetch_data"
1113
1102
  if(stmt)
1114
1103
  begin
1115
1104
  return @servertype.select(stmt)
@@ -1118,33 +1107,63 @@ module ActiveRecord
1118
1107
  if error_msg && !error_msg.empty?
1119
1108
  raise StatementInvalid,"Failed to retrieve data: #{error_msg}"
1120
1109
  else
1121
- error_msg = "An unexpected error occurred during data retrieval"
1122
1110
  error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
1123
1111
  raise error_msg
1124
1112
  end
1125
1113
  ensure
1126
1114
  # Ensures to free the resources associated with the statement
1127
- IBM_DB.free_stmt(stmt) if stmt
1115
+ if stmt
1116
+ puts_log "Free Statement #{stmt}"
1117
+ IBM_DB.free_stmt(stmt)
1118
+ end
1128
1119
  end
1129
1120
  end
1130
1121
  end
1131
1122
 
1132
- def select(sql, name = nil, binds = [])
1123
+ def select(sql, name = nil, binds = [], prepare: false, async: false)
1124
+ puts_log "select #{sql}"
1125
+ puts_log "prepare = #{prepare}"
1126
+
1133
1127
  # Replaces {"= NULL" with " IS NULL"} OR {"IN (NULL)" with " IS NULL"
1134
- sql.gsub( /(=\s*NULL|IN\s*\(NULL\))/i, " IS NULL" )
1128
+ begin
1129
+ sql.gsub( /(=\s*NULL|IN\s*\(NULL\))/i, " IS NULL" )
1130
+ rescue
1131
+ # ...
1132
+ end
1133
+
1134
+ if async && async_enabled?
1135
+ if current_transaction.joinable?
1136
+ raise AsynchronousQueryInsideTransactionError, "Asynchronous queries are not allowed inside transactions"
1137
+ end
1138
+
1139
+ future_result = async.new(
1140
+ pool,
1141
+ sql,
1142
+ name,
1143
+ binds,
1144
+ prepare: prepare,
1145
+ )
1146
+ if supports_concurrent_connections? && current_transaction.closed?
1147
+ future_result.schedule!(ActiveRecord::Base.asynchronous_queries_session)
1148
+ else
1149
+ future_result.execute!(self)
1150
+ end
1151
+ return future_result
1152
+ end
1135
1153
 
1136
1154
  results = []
1137
1155
 
1138
1156
  if(binds.nil? || binds.empty?)
1139
1157
  stmt = execute(sql, name)
1140
1158
  else
1141
- stmt = exec_query(sql, name, binds)
1159
+ stmt = exec_query_ret_stmt(sql, name, binds, prepare)
1142
1160
  end
1143
1161
 
1144
1162
  cols = IBM_DB.resultCols(stmt)
1145
1163
 
1146
1164
  if( stmt )
1147
1165
  results = fetch_data(stmt)
1166
+ puts_log "Results = #{results}"
1148
1167
  end
1149
1168
 
1150
1169
  if(@isAr3)
@@ -1154,57 +1173,96 @@ module ActiveRecord
1154
1173
  end
1155
1174
  end
1156
1175
 
1157
- #Returns an array of arrays containing the field values.
1158
- #This is an implementation for the abstract method
1159
- #+sql+ is the select query and +name+ is an optional description for logging
1160
- def select_rows(sql, name = nil,binds = [])
1161
- # Replaces {"= NULL" with " IS NULL"} OR {"IN (NULL)" with " IS NULL"}
1162
- sql.gsub( /(=\s*NULL|IN\s*\(NULL\))/i, " IS NULL" )
1163
-
1164
- results = []
1165
- # Invokes the method +execute+ in order to log and execute the SQL
1166
- # IBM_DB.Statement is returned from which results can be fetched
1167
- if !binds.nil? && !binds.empty?
1168
- param_array = binds.map do |column,value|
1169
- quote_value_for_pstmt(value, column)
1170
- end
1171
- return prepared_select({"sqlSegment" => sql, "paramArray" => param_array})
1176
+ def translate_exception(exception, message:, sql:, binds:)
1177
+ error_msg1 = /SQL0803N One or more values in the INSERT statement, UPDATE statement, or foreign key update caused by a DELETE statement are not valid because the primary key, unique constraint or unique index identified by .* constrains table .* from having duplicate values for the index key/
1178
+ error_msg2 = /SQL0204N .* is an undefined name/
1179
+ error_msg3 = /SQL0413N Overflow occurred during numeric data type conversion/
1180
+ error_msg4 = /SQL0407N Assignment of a NULL value to a NOT NULL column .* is not allowed/
1181
+ error_msg5 = /SQL0530N The insert or update value of the FOREIGN KEY .* is not equal to any value of the parent key of the parent table/
1182
+ error_msg6 = /SQL0532N A parent row cannot be deleted because the relationship .* restricts the deletion/
1183
+ error_msg7 = /SQL0433N Value .* is too long/
1184
+ error_msg8 = /CLI0109E String data right truncation/
1185
+ if !error_msg1.match(message).nil?
1186
+ RecordNotUnique.new(message, sql: sql, binds: binds)
1187
+ elsif !error_msg2.match(message).nil?
1188
+ ArgumentError.new(message)
1189
+ elsif !error_msg3.match(message).nil?
1190
+ RangeError.new(message, sql: sql, binds: binds)
1191
+ elsif !error_msg4.match(message).nil?
1192
+ NotNullViolation.new(message, sql: sql, binds: binds)
1193
+ elsif !error_msg5.match(message).nil? or !error_msg6.match(message).nil?
1194
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
1195
+ elsif !error_msg7.match(message).nil? or !error_msg8.match(message).nil?
1196
+ ValueTooLong.new(message, sql: sql, binds: binds)
1197
+ elsif exception.message.match?(/called on a closed database/i)
1198
+ ConnectionNotEstablished.new(exception)
1199
+ else
1200
+ super
1172
1201
  end
1202
+ end
1173
1203
 
1174
- stmt = execute(sql, name)
1175
- if(stmt)
1176
- begin
1177
- results = @servertype.select_rows(sql, name, stmt, results)
1178
- rescue StandardError => fetch_error # Handle driver fetch errors
1179
- error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
1180
- if error_msg && !error_msg.empty?
1181
- raise StatementInvalid,"Failed to retrieve data: #{error_msg}"
1204
+ def build_truncate_statement(table_name)
1205
+ puts_log "build_truncate_statement"
1206
+ "DELETE FROM #{quote_table_name(table_name)}"
1207
+ end
1208
+
1209
+ def build_fixture_statements(fixture_set)
1210
+ fixture_set.flat_map do |table_name, fixtures|
1211
+ next if fixtures.empty?
1212
+ fixtures.map { |fixture| build_fixture_sql([fixture], table_name) }
1213
+ end.compact
1214
+ end
1215
+
1216
+ def build_fixture_sql(fixtures, table_name)
1217
+ puts_log "build_fixture_sql"
1218
+ columns = schema_cache.columns_hash(table_name)
1219
+
1220
+ values_list = fixtures.map do |fixture|
1221
+ fixture = fixture.stringify_keys
1222
+ fixture = fixture.transform_keys(&:downcase)
1223
+
1224
+ unknown_columns = fixture.keys - columns.keys
1225
+ if unknown_columns.any?
1226
+ raise Fixture::FixtureError, %(table "#{table_name}" has no columns named #{unknown_columns.map(&:inspect).join(', ')}.)
1227
+ end
1228
+
1229
+ columns.map do |name, column|
1230
+ if fixture.key?(name)
1231
+ type = lookup_cast_type_from_column(column)
1232
+ with_yaml_fallback(type.serialize(fixture[name]))
1182
1233
  else
1183
- error_msg = "An unexpected error occurred during data retrieval"
1184
- error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
1185
- raise error_msg
1234
+ default_insert_value(column)
1186
1235
  end
1187
- ensure
1188
- # Ensures to free the resources associated with the statement
1189
- IBM_DB.free_stmt(stmt) if stmt
1190
1236
  end
1191
1237
  end
1192
- # The array of record hashes is returned
1193
- results
1194
- end
1195
1238
 
1196
- # Returns a record hash with the column names as keys and column values
1197
- # as values.
1198
- #def select_one(sql, name = nil)
1199
- # Gets the first hash from the array of hashes returned by
1200
- # select_all
1201
- # select_all(sql,name).first
1202
- #end
1239
+ table = Arel::Table.new(table_name)
1240
+ manager = Arel::InsertManager.new
1241
+ manager.into(table)
1242
+
1243
+ if values_list.size == 1
1244
+ values = values_list.shift
1245
+ new_values = []
1246
+ columns.each_key.with_index { |column, i|
1247
+ unless values[i].equal?(DEFAULT_INSERT_VALUE)
1248
+ new_values << values[i]
1249
+ manager.columns << table[column]
1250
+ end
1251
+ }
1252
+ values_list << new_values
1253
+ else
1254
+ columns.each_key { |column| manager.columns << table[column] }
1255
+ end
1256
+
1257
+ manager.values = manager.create_values_list(values_list)
1258
+ visitor.compile(manager.ast)
1259
+ end
1203
1260
 
1204
1261
  #inserts values from fixtures
1205
1262
  #overridden to handle LOB's fixture insertion, as, in normal inserts callbacks are triggered but during fixture insertion callbacks are not triggered
1206
1263
  #hence only markers like @@@IBMBINARY@@@ will be inserted and are not updated to actual data
1207
1264
  def insert_fixture(fixture, table_name)
1265
+ puts_log "insert_fixture = #{fixture}"
1208
1266
  if(fixture.respond_to?(:keys))
1209
1267
  insert_query = "INSERT INTO #{quote_table_name(table_name)} ( #{fixture.keys.join(', ')})"
1210
1268
  else
@@ -1253,7 +1311,6 @@ module ActiveRecord
1253
1311
  end
1254
1312
  end
1255
1313
 
1256
- #log_query(insert_query,'fixture insert')
1257
1314
  log(insert_query,'fixture insert') do
1258
1315
  unless IBM_DB.execute(stmt, insert_values)
1259
1316
  error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
@@ -1266,19 +1323,24 @@ module ActiveRecord
1266
1323
  end
1267
1324
 
1268
1325
  def empty_insert_statement_value(pkey)
1269
- "(#{pkey}) VALUES (DEFAULT)"
1326
+ if !pkey.nil?
1327
+ "(#{pkey}) VALUES (DEFAULT)"
1328
+ else
1329
+ raise ArgumentError, "Empty Insert Statement not allowed in DB2"
1330
+ end
1270
1331
  end
1271
1332
 
1272
1333
  # Perform an insert and returns the last ID generated.
1273
1334
  # This can be the ID passed to the method or the one auto-generated by the database,
1274
1335
  # and retrieved by the +last_generated_id+ method.
1275
1336
  def insert_direct(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
1337
+ puts_log "insert_direct"
1276
1338
  if @handle_lobs_triggered #Ensure the array of sql is cleared if they have been handled in the callback
1277
1339
  @sql = []
1278
1340
  @handle_lobs_triggered = false
1279
1341
  end
1280
1342
 
1281
- clear_query_cache if defined? clear_query_cache
1343
+ ActiveRecord::Base.clear_query_caches_for_current_thread
1282
1344
 
1283
1345
  if stmt = execute(sql, name)
1284
1346
  begin
@@ -1292,20 +1354,23 @@ module ActiveRecord
1292
1354
  end
1293
1355
 
1294
1356
  def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds=[])
1357
+ puts_log "insert Binds = #{binds}"
1295
1358
  if(@arelVersion < 6)
1296
- sql, binds = [to_sql(arel), binds]
1297
- else
1298
- sql, binds = [to_sql(arel),binds] #sql_for_insert(to_sql(arel, binds), binds) #[to_sql(arel),binds]
1299
- end
1359
+ sql, binds = [to_sql(arel), binds]
1360
+ else
1361
+ sql, binds = to_sql_and_binds(arel, binds)
1362
+ end
1300
1363
 
1364
+ puts_log "Binds 2 = #{binds}"
1365
+ puts_log "SQL = #{sql}"
1301
1366
  #unless IBM_DBAdapter.respond_to?(:exec_insert)
1302
1367
  if binds.nil? || binds.empty?
1303
1368
  return insert_direct(sql, name, pk, id_value, sequence_name)
1304
1369
  end
1305
1370
 
1306
- clear_query_cache if defined? clear_query_cache
1371
+ ActiveRecord::Base.clear_query_caches_for_current_thread
1307
1372
 
1308
- if stmt = exec_insert(sql, name, binds)
1373
+ if stmt = exec_insert_db2(sql, name, binds)
1309
1374
  begin
1310
1375
  @sql << sql
1311
1376
  return id_value || @servertype.last_generated_id(stmt)
@@ -1315,18 +1380,36 @@ module ActiveRecord
1315
1380
  end
1316
1381
  end
1317
1382
 
1383
+ def exec_insert_db2(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
1384
+ puts_log "exec_insert_db2"
1385
+ sql, binds = sql_for_insert(sql, pk, binds)
1386
+ exec_query_ret_stmt(sql, name, binds, prepare = false)
1387
+ end
1388
+
1389
+ def last_inserted_id(result)
1390
+ puts_log "last_inserted_id"
1391
+ return result
1392
+ end
1393
+
1394
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil) # :nodoc:
1395
+ puts_log "exec_insert"
1396
+ value = insert(sql)
1397
+ return value
1398
+ end
1399
+
1318
1400
  # Praveen
1319
1401
  # Performs an insert using the prepared statement and returns the last ID generated.
1320
1402
  # This can be the ID passed to the method or the one auto-generated by the database,
1321
1403
  # and retrieved by the +last_generated_id+ method.
1322
1404
  def prepared_insert(pstmt, param_array = nil, id_value = nil)
1405
+ puts_log "prepared_insert"
1323
1406
  if @handle_lobs_triggered #Ensure the array of sql is cleared if they have been handled in the callback
1324
1407
  @sql = []
1325
1408
  @sql_parameter_values = []
1326
1409
  @handle_lobs_triggered = false
1327
1410
  end
1328
1411
 
1329
- clear_query_cache if defined? clear_query_cache
1412
+ ActiveRecord::Base.clear_query_caches_for_current_thread
1330
1413
 
1331
1414
  begin
1332
1415
  if execute_prepared_stmt(pstmt, param_array)
@@ -1345,6 +1428,7 @@ module ActiveRecord
1345
1428
  # Prepares and logs +sql+ commands and
1346
1429
  # returns a +IBM_DB.Statement+ object.
1347
1430
  def prepare(sql,name = nil)
1431
+ puts_log "prepare"
1348
1432
  # The +log+ method is defined in the parent class +AbstractAdapter+
1349
1433
  @prepared_sql = sql
1350
1434
  log(sql,name) do
@@ -1356,17 +1440,15 @@ module ActiveRecord
1356
1440
  #Executes the prepared statement
1357
1441
  #ReturnsTrue on success and False on Failure
1358
1442
  def execute_prepared_stmt(pstmt, param_array = nil)
1443
+ puts_log "execute_prepared_stmt"
1444
+ puts_log "Param array = #{param_array}"
1359
1445
  if !param_array.nil? && param_array.size < 1
1360
1446
  param_array = nil
1361
1447
  end
1362
1448
 
1363
1449
  if( !IBM_DB.execute(pstmt, param_array) )
1364
1450
  error_msg = IBM_DB.getErrormsg(pstmt, IBM_DB::DB_STMT)
1365
- if !error_msg.empty?
1366
- error_msg = "Statement execution failed: " + error_msg
1367
- else
1368
- error_msg = "Statement execution failed"
1369
- end
1451
+ puts_log "Error = #{error_msg}"
1370
1452
  IBM_DB.free_stmt(pstmt) if pstmt
1371
1453
  raise StatementInvalid, error_msg
1372
1454
  else
@@ -1374,45 +1456,121 @@ module ActiveRecord
1374
1456
  end
1375
1457
  end
1376
1458
 
1459
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
1460
+ :desc, :describe
1461
+ ) # :nodoc:
1462
+ private_constant :READ_QUERY
1463
+
1464
+ def write_query?(sql) # :nodoc:
1465
+ !READ_QUERY.match?(sql)
1466
+ rescue ArgumentError # Invalid encoding
1467
+ !READ_QUERY.match?(sql.b)
1468
+ end
1469
+
1470
+ def explain(arel, binds = [])
1471
+ sql = "EXPLAIN ALL SET QUERYNO = 1 FOR #{to_sql(arel, binds)}"
1472
+ stmt = execute(sql, "EXPLAIN")
1473
+ result = select("select * from explain_statement where explain_level = 'P' and queryno = 1", "EXPLAIN")
1474
+ return result[0]["total_cost"].to_s
1475
+ # Ensures to free the resources associated with the statement
1476
+ ensure
1477
+ IBM_DB.free_stmt(stmt) if stmt
1478
+ end
1479
+
1377
1480
  # Executes +sql+ statement in the context of this connection using
1378
1481
  # +binds+ as the bind substitutes. +name+ is logged along with
1379
1482
  # the executed +sql+ statement.
1380
- def exec_query(sql, name = 'SQL', binds = [])
1483
+ # Here prepare argument is not used, by default this method creates prepared statment and execute.
1484
+ def exec_query_ret_stmt(sql, name = 'SQL', binds = [], prepare = false)
1485
+ puts_log "exec_query_ret_stmt #{sql}"
1486
+ sql = transform_query(sql)
1487
+ check_if_write_query(sql)
1488
+ materialize_transactions
1489
+ mark_transaction_written_if_write(sql)
1381
1490
  begin
1382
- param_array = binds.map do |column,value|
1383
- quote_value_for_pstmt(value, column)
1491
+ puts_log "SQL = #{sql}"
1492
+ puts_log "Binds = #{binds}"
1493
+ param_array = type_casted_binds(binds)
1494
+ puts_log "Param array = #{param_array}"
1495
+ puts_log "Prepare flag = #{prepare}"
1496
+ puts_log "#{caller}"
1497
+
1498
+ stmt = @servertype.prepare(sql, name)
1499
+ if prepare
1500
+ @statements[sql] = stmt
1501
+ end
1502
+
1503
+ puts_log "Statement = #{stmt}"
1504
+ log(sql, name, binds, param_array) do
1505
+ if( stmt )
1506
+ if(execute_prepared_stmt(stmt, param_array))
1507
+ return stmt
1508
+ end
1509
+ else
1510
+ return false
1511
+ end
1384
1512
  end
1385
-
1386
- stmt = prepare(sql, name)
1387
-
1388
- if( stmt )
1389
- if(execute_prepared_stmt(stmt, param_array))
1390
- return stmt
1391
- end
1392
- else
1393
- return false
1394
- end
1395
1513
  ensure
1396
1514
  @offset = @limit = nil
1397
1515
  end
1398
1516
  end
1399
1517
 
1518
+ def exec_query(sql, name = 'SQL', binds = [], prepare = false)
1519
+ select_prepared(sql, name, binds, prepare)
1520
+ end
1521
+
1522
+ def select_prepared(sql, name = nil, binds = [], prepare = true)
1523
+ puts_log "select_prepared"
1524
+ puts_log "select_prepared sql before = #{sql}"
1525
+ puts_log "select_prepared Binds = #{binds}"
1526
+ stmt = exec_query_ret_stmt(sql, name, binds, prepare)
1527
+ if !/^select .*/i.match(sql).nil?
1528
+ cols = IBM_DB.resultCols(stmt)
1529
+
1530
+ if( stmt )
1531
+ results = fetch_data(stmt)
1532
+ end
1533
+
1534
+ puts_log "select_prepared columns = #{cols}"
1535
+ puts_log "select_prepared sql after = #{sql}"
1536
+ puts_log "select_prepared result = #{results}"
1537
+ else
1538
+ cols = nil
1539
+ result = nil
1540
+ end
1541
+ if(@isAr3)
1542
+ return results
1543
+ else
1544
+ return ActiveRecord::Result.new(cols, results)
1545
+ end
1546
+ end
1547
+
1548
+ def check_if_write_query(sql) #For rails 7.1 just remove this function as it will be defined in AbstractAdapter class
1549
+ if preventing_writes? && write_query?(sql)
1550
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
1551
+ end
1552
+ end
1553
+
1400
1554
  # Executes and logs +sql+ commands and
1401
1555
  # returns a +IBM_DB.Statement+ object.
1402
1556
  def execute(sql, name=nil)
1403
1557
  # Logs and execute the sql instructions.
1404
1558
  # The +log+ method is defined in the parent class +AbstractAdapter+
1405
- #sql='INSERT INTO ar_internal_metadata (key, value, created_at, updated_at) VALUES ('10', '10', '10', '10')
1559
+ #sql='INSERT INTO ar_internal_metadata (key, value, created_at, updated_at) VALUES ('10', '10', '10', '10')
1560
+ puts_log "execute"
1561
+ puts_log "#{sql}"
1562
+ sql = transform_query(sql)
1563
+ check_if_write_query(sql)
1564
+ materialize_transactions
1565
+ mark_transaction_written_if_write(sql)
1406
1566
  log(sql , name) do
1407
1567
  @servertype.execute(sql, name)
1408
1568
  end
1409
1569
  end
1410
1570
 
1411
- def exec_insert(sql,name,binds,pk,sequence_name)
1412
- end
1413
-
1414
1571
  # Executes an "UPDATE" SQL statement
1415
1572
  def update_direct(sql, name = nil)
1573
+ puts_log "update_direct"
1416
1574
  if @handle_lobs_triggered #Ensure the array of sql is cleared if they have been handled in the callback
1417
1575
  @sql = []
1418
1576
  @handle_lobs_triggered = false
@@ -1433,13 +1591,14 @@ module ActiveRecord
1433
1591
 
1434
1592
  #Praveen
1435
1593
  def prepared_update(pstmt, param_array = nil )
1594
+ puts_log "prepared_update"
1436
1595
  if @handle_lobs_triggered #Ensure the array of sql is cleared if they have been handled in the callback
1437
1596
  @sql = []
1438
1597
  @sql_parameter_values = []
1439
1598
  @handle_lobs_triggered = false
1440
1599
  end
1441
1600
 
1442
- clear_query_cache if defined? clear_query_cache
1601
+ ActiveRecord::Base.clear_query_caches_for_current_thread
1443
1602
 
1444
1603
  begin
1445
1604
  if execute_prepared_stmt(pstmt, param_array)
@@ -1461,11 +1620,12 @@ module ActiveRecord
1461
1620
  alias_method :prepared_delete, :prepared_update
1462
1621
 
1463
1622
  def update(arel, name = nil, binds = [])
1623
+ puts_log "update"
1464
1624
  if(@arelVersion < 6 )
1465
- sql = to_sql(arel)
1466
- else
1467
- sql = to_sql(arel,binds)
1468
- end
1625
+ sql = to_sql(arel)
1626
+ else
1627
+ sql, binds = to_sql_and_binds(arel, binds)
1628
+ end
1469
1629
 
1470
1630
  # Make sure the WHERE clause handles NULL's correctly
1471
1631
  sqlarray = sql.split(/\s*WHERE\s*/)
@@ -1482,13 +1642,13 @@ module ActiveRecord
1482
1642
  sql = sql + sqlarray[size-1]
1483
1643
  end
1484
1644
 
1485
- clear_query_cache if defined? clear_query_cache
1645
+ ActiveRecord::Base.clear_query_caches_for_current_thread
1486
1646
 
1487
1647
  if binds.nil? || binds.empty?
1488
1648
  update_direct(sql, name)
1489
1649
  else
1490
1650
  begin
1491
- if stmt = exec_query(sql,name,binds)
1651
+ if stmt = exec_query_ret_stmt(sql, name, binds, prepare = true)
1492
1652
  IBM_DB.num_rows(stmt)
1493
1653
  end
1494
1654
  ensure
@@ -1501,14 +1661,18 @@ module ActiveRecord
1501
1661
 
1502
1662
  # Begins the transaction (and turns off auto-committing)
1503
1663
  def begin_db_transaction
1664
+ puts_log "begin_db_transaction"
1665
+ log("begin transaction", "TRANSACTION") {
1504
1666
  # Turns off the auto-commit
1505
- IBM_DB.autocommit(@connection, IBM_DB::SQL_AUTOCOMMIT_OFF)
1667
+ IBM_DB.autocommit(@connection, IBM_DB::SQL_AUTOCOMMIT_OFF) }
1506
1668
  end
1507
1669
 
1508
1670
  # Commits the transaction and turns on auto-committing
1509
1671
  def commit_db_transaction
1672
+ puts_log "commit_db_transaction"
1673
+ log("commit transaction", "TRANSACTION") {
1510
1674
  # Commits the transaction
1511
- IBM_DB.commit @connection rescue nil
1675
+ IBM_DB.commit @connection rescue nil }
1512
1676
  # Turns on auto-committing
1513
1677
  IBM_DB.autocommit @connection, IBM_DB::SQL_AUTOCOMMIT_ON
1514
1678
  end
@@ -1516,85 +1680,26 @@ module ActiveRecord
1516
1680
  # Rolls back the transaction and turns on auto-committing. Must be
1517
1681
  # done if the transaction block raises an exception or returns false
1518
1682
  def rollback_db_transaction
1683
+ puts_log "rollback_db_transaction"
1684
+ log("rollback transaction", "TRANSACTION") {
1519
1685
  # ROLLBACK the transaction
1520
- IBM_DB.rollback(@connection) rescue nil
1686
+ IBM_DB.rollback(@connection) rescue nil }
1687
+ ActiveRecord::Base.clear_query_caches_for_current_thread
1521
1688
  # Turns on auto-committing
1522
1689
  IBM_DB.autocommit @connection, IBM_DB::SQL_AUTOCOMMIT_ON
1523
1690
  end
1524
1691
 
1525
- def get_limit_offset_clauses(limit,offset)
1526
-
1527
- if limit && limit == 0
1528
- clauses = @servertype.get_limit_offset_clauses(limit,0)
1529
- else
1530
- clauses = @servertype.get_limit_offset_clauses(limit, offset)
1531
- end
1532
- end
1533
-
1534
- # Modifies a sql statement in order to implement a LIMIT and an OFFSET.
1535
- # A LIMIT defines the number of rows that should be fetched, while
1536
- # an OFFSET defines from what row the records must be fetched.
1537
- # IBM data servers implement a LIMIT in SQL statements through:
1538
- # FETCH FIRST n ROWS ONLY, where n is the number of rows required.
1539
- # The implementation of OFFSET is more elaborate, and requires the usage of
1540
- # subqueries and the ROW_NUMBER() command in order to add row numbering
1541
- # as an additional column to a copy of the existing table.
1542
- # ==== Examples
1543
- # add_limit_offset!('SELECT * FROM staff', {:limit => 10})
1544
- # generates: "SELECT * FROM staff FETCH FIRST 10 ROWS ONLY"
1545
- #
1546
- # add_limit_offset!('SELECT * FROM staff', {:limit => 10, :offset => 30})
1547
- # generates "SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_rownum
1548
- # FROM (SELECT * FROM staff) AS I) AS O WHERE sys_row_num BETWEEN 31 AND 40"
1549
- def add_limit_offset!(sql, options)
1550
- limit = options[:limit]
1551
- offset = options[:offset]
1552
-
1553
- # if the limit is zero
1554
- if limit && limit == 0
1555
- # Returns a query that will always generate zero records
1556
- # (e.g. WHERE sys_row_num BETWEEN 1 and 0)
1557
- if( @pstmt_support_on )
1558
- sql = @servertype.query_offset_limit!(sql, 0, limit, options)
1559
- else
1560
- sql = @servertype.query_offset_limit(sql, 0, limit)
1561
- end
1562
- # If there is a non-zero limit
1563
- else
1564
- # If an offset is specified builds the query with offset and limit,
1565
- # otherwise retrieves only the first +limit+ rows
1566
- if( @pstmt_support_on )
1567
- sql = @servertype.query_offset_limit!(sql, offset, limit, options)
1568
- else
1569
- sql = @servertype.query_offset_limit(sql, offset, limit)
1570
- end
1571
- end
1572
- # Returns the sql query in any case
1573
- sql
1574
- end # method add_limit_offset!
1575
-
1576
1692
  def default_sequence_name(table, column) # :nodoc:
1693
+ puts_log "72"
1577
1694
  "#{table}_#{column}_seq"
1578
1695
  end
1579
1696
 
1580
-
1581
1697
  #==============================================
1582
1698
  # QUOTING
1583
1699
  #==============================================
1584
1700
 
1585
- # Quote date/time values for use in SQL input.
1586
- # Includes microseconds, if the value is a Time responding to usec.
1587
- =begin
1588
- def quoted_date(value) #:nodoc:
1589
- if value.respond_to?(:usec)
1590
- "#{super}.#{sprintf("%06d", value.usec)}"
1591
- else
1592
- super
1593
- end
1594
- end
1595
- =end
1596
1701
  def quote_value_for_pstmt(value, column=nil)
1597
-
1702
+ puts_log "quote_value_for_pstmt"
1598
1703
  return value.quoted_id if value.respond_to?(:quoted_id)
1599
1704
 
1600
1705
  case value
@@ -1621,98 +1726,55 @@ module ActiveRecord
1621
1726
  end
1622
1727
  end
1623
1728
  end
1624
- # Properly quotes the various data types.
1625
- # +value+ contains the data, +column+ is optional and contains info on the field
1626
- # def quote(value, column=nil)
1627
- # return value.quoted_id if value.respond_to?(:quoted_id)
1628
- # case value
1629
- # # If it's a numeric value and the column sql_type is not a string, it shouldn't be quoted
1630
- # # (IBM_DB doesn't accept quotes on numeric types)
1631
- # when Numeric
1632
- # # If the column sql_type is text or string, return the quote value
1633
- # if (column && ( column.sql_type.to_s =~ /text|char/i ))
1634
- # unless caller[0] =~ /insert_fixture/i
1635
- # "'#{value}'"
1636
- # else
1637
- # "#{value}"
1638
- # end
1639
- # else
1640
- # # value is Numeric, column.sql_type is not a string,
1641
- # # therefore it converts the number to string without quoting it
1642
- # value.to_s
1643
- # end
1644
- # when String, ActiveSupport::Multibyte::Chars
1645
- # if column && column.sql_type.to_s =~ /binary|blob/i && !(column.sql_type.to_s =~ /for bit data/i)
1646
- # # If quoting is required for the insert/update of a BLOB
1647
- # unless caller[0] =~ /add_column_options/i
1648
- # # Invokes a convertion from string to binary
1649
- # @servertype.set_binary_value
1650
- # else
1651
- # # Quoting required for the default value of a column
1652
- # @servertype.set_binary_default(value)
1653
- # end
1654
- # elsif column && column.sql_type.to_s =~ /text|clob/i
1655
- # unless caller[0] =~ /add_column_options/i
1656
- # @servertype.set_text_default(quote_string(value))
1657
- # else
1658
- # @servertype.set_text_default(quote_string(value))
1659
- # end
1660
- # elsif column && column.sql_type.to_s =~ /xml/i
1661
- # unless caller[0] =~ /add_column_options/i
1662
- # "#{value}"
1663
- # else
1664
- # "#{value}"
1665
- # end
1666
- # else
1667
- # unless caller[0] =~ /insert_fixture/i
1668
- # super(value)
1669
- # else
1670
- # "#{value}"
1671
- # end
1672
- # end
1673
- # #when TrueClass then quoted_true # return '1' for true
1674
- # when TrueClass
1675
- # quoted_true
1676
- # #when FalseClass then quoted_false # return '0' for false
1677
- # when FalseClass
1678
- # quoted_false
1679
- # when nil
1680
- # "NULL"
1681
- # when Date
1682
- # "'#{quoted_date(value)}'"
1683
- # when Time
1684
- # "'#{quoted_date(value)}'"
1685
- # when Symbol
1686
- # "'#{quote_string(value)}'"
1687
- # else
1688
- # unless caller[0] =~ /insert_fixture/i
1689
- # "'#{quote_string(YAML.dump(value))}'"
1690
- # else
1691
- # "#{quote_string(YAML.dump(value))}"
1692
- # end
1693
- # end
1694
- # end
1695
- # # Quotes a given string, escaping single quote (') characters.
1696
- # def quote_string(string)
1697
- # #string.gsub(/'/, "''")
1698
- # string.gsub('\\', '\&\&').gsub("'", "''")
1699
- # end
1729
+
1730
+ # Quotes a given string, escaping single quote (') characters.
1731
+ def quote_string(string)
1732
+ puts_log "quote_string"
1733
+ string.gsub(/'/, "''")
1734
+ #string.gsub('\\', '\&\&').gsub("'", "''")
1735
+ end
1700
1736
 
1701
1737
  # *true* is represented by a smallint 1, *false*
1702
1738
  # by 0, as no native boolean type exists in DB2.
1703
1739
  # Numerics are not quoted in DB2.
1704
1740
  def quoted_true
1705
- "1"
1741
+ puts_log "quoted_true"
1742
+ "1".freeze
1706
1743
  end
1707
1744
 
1708
1745
  def quoted_false
1709
- "0"
1746
+ puts_log "quoted_false"
1747
+ "0".freeze
1748
+ end
1749
+
1750
+ def unquoted_true
1751
+ puts_log "unquoted_true"
1752
+ 1
1753
+ end
1754
+
1755
+ def unquoted_false
1756
+ puts_log "unquoted_false"
1757
+ 0
1758
+ end
1759
+
1760
+ def quote_table_name(name)
1761
+ puts_log "quote_table_name"
1762
+ if name.start_with?'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
1763
+ name = "\"#{name}\""
1764
+ end
1765
+ name
1766
+ #@servertype.check_reserved_words(name).gsub('"', '').gsub("'",'')
1710
1767
  end
1711
1768
 
1712
1769
  def quote_column_name(name)
1713
- @servertype.check_reserved_words(name)
1770
+ puts_log "quote_column_name"
1771
+ @servertype.check_reserved_words(name).gsub('"', '').gsub("'",'')
1714
1772
  end
1715
1773
 
1774
+ def quoted_binary(value)
1775
+ puts_log "quoted_binary"
1776
+ "CAST(x'#{value.hex}' AS BLOB)"
1777
+ end
1716
1778
  #==============================================
1717
1779
  # SCHEMA STATEMENTS
1718
1780
  #==============================================
@@ -1721,7 +1783,7 @@ module ActiveRecord
1721
1783
  # database types
1722
1784
  def native_database_types
1723
1785
  {
1724
- :primary_key => { :name => @servertype.primary_key_definition(@start_id)},
1786
+ :primary_key => { :name => @servertype.primary_key_definition(@start_id) },
1725
1787
  :string => { :name => "varchar", :limit => 255 },
1726
1788
  :text => { :name => "clob" },
1727
1789
  :integer => { :name => "integer" },
@@ -1750,6 +1812,7 @@ module ActiveRecord
1750
1812
  end
1751
1813
 
1752
1814
  def build_conn_str_for_dbops()
1815
+ puts_log "build_conn_str_for_dbops"
1753
1816
  connect_str = "DRIVER={IBM DB2 ODBC DRIVER};ATTACH=true;"
1754
1817
  if(!@host.nil?)
1755
1818
  connect_str << "HOSTNAME=#{@host};"
@@ -1761,6 +1824,7 @@ module ActiveRecord
1761
1824
  end
1762
1825
 
1763
1826
  def drop_database(dbName)
1827
+ puts_log "drop_database"
1764
1828
  connect_str = build_conn_str_for_dbops()
1765
1829
 
1766
1830
  #Ensure connection is closed before trying to drop a database.
@@ -1784,6 +1848,7 @@ module ActiveRecord
1784
1848
  end
1785
1849
 
1786
1850
  def create_database(dbName, codeSet=nil, mode=nil)
1851
+ puts_log "create_database"
1787
1852
  connect_str = build_conn_str_for_dbops()
1788
1853
 
1789
1854
  #Ensure connection is closed before trying to drop a database.
@@ -1805,18 +1870,41 @@ module ActiveRecord
1805
1870
  raise "Could not create Database due to: #{error}"
1806
1871
  end
1807
1872
  end
1808
-
1809
-
1810
-
1811
-
1812
- def valid_type?(type)
1813
- #!native_database_types[type].nil?
1814
- native_database_types[type].nil?
1873
+
1874
+ def valid_type?(type)
1875
+ !native_database_types[type].nil?
1815
1876
  end
1816
-
1877
+
1817
1878
  # IBM data servers do not support limits on certain data types (unlike MySQL)
1818
1879
  # Limit is supported for the {float, decimal, numeric, varchar, clob, blob, graphic, vargraphic} data types.
1819
1880
  def type_to_sql(type, limit=nil, precision=nil, scale=nil )
1881
+ puts_log "type_to_sql"
1882
+ puts_log "Type = #{type}, Limit = #{limit}"
1883
+
1884
+ if type.to_sym == :binary and limit.class == Hash and limit.has_key?("limit".to_sym)
1885
+ sql_segment = native_database_types[type.to_sym][:name].to_s
1886
+ sql_segment << "(#{limit[:limit]})"
1887
+ return sql_segment
1888
+ end
1889
+
1890
+ if type.to_sym == :datetime and limit.class == Hash and limit.has_key?("precision".to_sym)
1891
+ sql_segment = native_database_types[type.to_sym][:name].to_s
1892
+ if limit[:precision].nil?
1893
+ return sql_segment
1894
+ elsif (0..12) === limit[:precision]
1895
+ sql_segment << "(#{limit[:precision]})"
1896
+ return sql_segment
1897
+ else
1898
+ raise ArgumentError, "No #{sql_segment} type has precision of #{limit[:precision]}. The allowed range of precision is from 0 to 12"
1899
+ end
1900
+ end
1901
+
1902
+ if type.to_sym == :string and limit.class == Hash and limit.has_key?("limit".to_sym)
1903
+ sql_segment = native_database_types[type.to_sym][:name].to_s
1904
+ sql_segment << "(#{limit[:limit]})"
1905
+ return sql_segment
1906
+ end
1907
+
1820
1908
  if type.to_sym == :decimal
1821
1909
  if limit.class == Hash
1822
1910
  if limit.has_key?("precision".to_sym)
@@ -1834,6 +1922,9 @@ module ActiveRecord
1834
1922
  return sql_segment
1835
1923
  elsif scale.nil? && !precision.nil?
1836
1924
  sql_segment << "(#{precision})"
1925
+ return sql_segment
1926
+ elsif precision.nil? && !scale.nil?
1927
+ raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
1837
1928
  else
1838
1929
  return sql_segment
1839
1930
  end
@@ -1845,50 +1936,48 @@ module ActiveRecord
1845
1936
  return sql_segment
1846
1937
  end
1847
1938
 
1848
- if type.to_sym == :vargraphic
1849
- sql_segment = native_database_types[type.to_sym][:name].to_s
1850
- if limit.class == Hash
1851
- if limit.has_key?("limit".to_sym)
1852
- limit1 = limit[:limit]
1853
- sql_segment << "(#{limit1})"
1854
- else
1855
- return "vargraphic(1)"
1856
- end
1857
- else
1858
- if limit != nil
1859
- sql_segment << "(#{limit})"
1860
- else
1861
- return "vargraphic(1)"
1862
- end
1863
- end
1864
- return sql_segment
1865
- end
1866
-
1867
- if type.to_sym == :graphic
1868
- sql_segment = native_database_types[type.to_sym][:name].to_s
1869
- if limit.class == Hash
1870
- if limit.has_key?("limit".to_sym)
1871
- limit1 = limit[:limit]
1872
- sql_segment << "(#{limit1})"
1873
- else
1874
- return "graphic(1)"
1875
- end
1876
- else
1877
- if limit != nil
1878
- sql_segment << "(#{limit})"
1879
- else
1880
- return "graphic(1)"
1881
- end
1882
- end
1883
- return sql_segment
1939
+ if type.to_sym == :vargraphic
1940
+ sql_segment = native_database_types[type.to_sym][:name].to_s
1941
+ if limit.class == Hash
1942
+ if limit.has_key?("limit".to_sym)
1943
+ limit1 = limit[:limit]
1944
+ sql_segment << "(#{limit1})"
1945
+ else
1946
+ return "vargraphic(1)"
1947
+ end
1948
+ else
1949
+ if limit != nil
1950
+ sql_segment << "(#{limit})"
1951
+ else
1952
+ return "vargraphic(1)"
1953
+ end
1954
+ end
1955
+ return sql_segment
1884
1956
  end
1885
1957
 
1886
-
1958
+ if type.to_sym == :graphic
1959
+ sql_segment = native_database_types[type.to_sym][:name].to_s
1960
+ if limit.class == Hash
1961
+ if limit.has_key?("limit".to_sym)
1962
+ limit1 = limit[:limit]
1963
+ sql_segment << "(#{limit1})"
1964
+ else
1965
+ return "graphic(1)"
1966
+ end
1967
+ else
1968
+ if limit != nil
1969
+ sql_segment << "(#{limit})"
1970
+ else
1971
+ return "graphic(1)"
1972
+ end
1973
+ end
1974
+ return sql_segment
1975
+ end
1887
1976
 
1888
1977
  if limit.class == Hash
1889
- return super if limit.has_key?("limit".to_sym).nil?
1978
+ return super(type) if limit.has_key?("limit".to_sym).nil?
1890
1979
  else
1891
- return super if limit.nil?
1980
+ return super(type) if limit.nil?
1892
1981
  end
1893
1982
 
1894
1983
  # strip off limits on data types not supporting them
@@ -1899,23 +1988,17 @@ module ActiveRecord
1899
1988
  else
1900
1989
  return super(type)
1901
1990
  end
1902
-
1903
1991
  end
1904
-
1905
-
1906
-
1907
-
1908
-
1909
-
1992
+
1910
1993
  # Returns the maximum length a table alias identifier can be.
1911
1994
  # IBM data servers (cross-platform) table limit is 128 characters
1912
1995
  def table_alias_length
1913
1996
  128
1914
1997
  end
1915
-
1916
1998
 
1917
1999
  # Retrieves table's metadata for a specified shema name
1918
2000
  def tables(name = nil)
2001
+ puts_log "tables"
1919
2002
  # Initializes the tables array
1920
2003
  tables = []
1921
2004
  # Retrieve table's metadata through IBM_DB driver
@@ -1954,16 +2037,13 @@ module ActiveRecord
1954
2037
  return tables
1955
2038
  end
1956
2039
 
1957
- ###################################
1958
-
1959
-
1960
2040
  # Retrieves views's metadata for a specified shema name
1961
2041
  def views
2042
+ puts_log "views"
1962
2043
  # Initializes the tables array
1963
2044
  tables = []
1964
2045
  # Retrieve view's metadata through IBM_DB driver
1965
- stmt = IBM_DB.tables(@connection, nil,
1966
- @servertype.set_case(@schema))
2046
+ stmt = IBM_DB.tables(@connection, nil, @servertype.set_case(@schema))
1967
2047
  if(stmt)
1968
2048
  begin
1969
2049
  # Fetches all the records available
@@ -1996,18 +2076,19 @@ module ActiveRecord
1996
2076
  # Returns the tables array
1997
2077
  return tables
1998
2078
  end
1999
-
2000
-
2079
+
2001
2080
  # Returns the primary key of the mentioned table
2002
2081
  def primary_key(table_name)
2003
- pk_name = nil
2082
+ puts_log "primary_key"
2083
+ pk_name = []
2004
2084
  stmt = IBM_DB.primary_keys( @connection, nil,
2005
2085
  @servertype.set_case(@schema),
2006
- @servertype.set_case(table_name))
2086
+ @servertype.set_case(table_name.to_s))
2007
2087
  if(stmt)
2008
2088
  begin
2009
- if ( pk_index_row = IBM_DB.fetch_array(stmt) )
2010
- pk_name = pk_index_row[3].downcase
2089
+ while ( pk_index_row = IBM_DB.fetch_array(stmt) )
2090
+ puts_log "Primary_keys = #{pk_index_row}"
2091
+ pk_name << pk_index_row[3].downcase
2011
2092
  end
2012
2093
  rescue StandardError => fetch_error # Handle driver fetch errors
2013
2094
  error_msg = IBM_DB.getErrormsg( stmt, IBM_DB::DB_STMT )
@@ -2029,11 +2110,19 @@ module ActiveRecord
2029
2110
  raise StandardError.new('An unexpected error occurred during primary key retrieval')
2030
2111
  end
2031
2112
  end
2032
- return pk_name
2113
+ if pk_name.length() == 1
2114
+ return pk_name[0]
2115
+ elsif pk_name.empty?
2116
+ return nil
2117
+ else
2118
+ return pk_name
2119
+ end
2033
2120
  end
2034
2121
 
2035
2122
  # Returns an array of non-primary key indexes for a specified table name
2036
2123
  def indexes(table_name, name = nil)
2124
+ puts_log "indexes"
2125
+ puts_log "Table = #{table_name}"
2037
2126
  # to_s required because +table_name+ may be a symbol.
2038
2127
  table_name = table_name.to_s
2039
2128
  # Checks if a blank table name has been given.
@@ -2055,11 +2144,13 @@ module ActiveRecord
2055
2144
  if(stmt)
2056
2145
  begin
2057
2146
  while ( pk_index_row = IBM_DB.fetch_array(stmt) )
2147
+ puts_log "Primary keys = #{pk_index_row}"
2148
+ puts_log "pk_index = #{pk_index}"
2058
2149
  if pk_index_row[5]
2059
2150
  pk_index_name = pk_index_row[5].downcase
2060
2151
  pk_index_columns = [pk_index_row[3].downcase] # COLUMN_NAME
2061
2152
  if pk_index
2062
- pk_index.columns = pk_index.columns + pk_index_columns
2153
+ pk_index.columns << pk_index_columns
2063
2154
  else
2064
2155
  pk_index = IndexDefinition.new(table_name, pk_index_name, true, pk_index_columns)
2065
2156
  end
@@ -2115,7 +2206,10 @@ module ActiveRecord
2115
2206
  end
2116
2207
 
2117
2208
  unless is_composite
2118
- indexes << IndexDefinition.new(table_name, index_name, index_unique, index_columns)
2209
+ sql = "select remarks from syscat.indexes where tabname = #{quote(table_name.upcase)} and indname = #{quote(index_stats[5])}"
2210
+ comment = single_value_from_rows(select_prepared(sql).rows)
2211
+
2212
+ indexes << IndexDefinition.new(table_name, index_name, index_unique, index_columns, comment: comment)
2119
2213
  index_schema << index_qualifier
2120
2214
  end
2121
2215
  end
@@ -2143,6 +2237,7 @@ module ActiveRecord
2143
2237
 
2144
2238
  # remove the primary key index entry.... should not be dumped by the dumper
2145
2239
 
2240
+ puts_log "Indexes 1 = #{pk_index}"
2146
2241
  i = 0
2147
2242
  indexes.each do |index|
2148
2243
  if pk_index && index.columns == pk_index.columns
@@ -2151,12 +2246,13 @@ module ActiveRecord
2151
2246
  i = i+1
2152
2247
  end
2153
2248
  # Returns the indexes array
2249
+ puts_log "Indexes 2 = #{indexes}"
2154
2250
  return indexes
2155
2251
  end
2156
2252
 
2157
-
2158
2253
  # Mapping IBM data servers SQL datatypes to Ruby data types
2159
2254
  def simplified_type2(field_type)
2255
+ puts_log "simplified_type2"
2160
2256
  case field_type
2161
2257
  # if +field_type+ contains 'for bit data' handle it as a binary
2162
2258
  when /for bit data/i
@@ -2193,10 +2289,10 @@ module ActiveRecord
2193
2289
  "rowid"
2194
2290
  end
2195
2291
  end # method simplified_type
2196
-
2197
-
2292
+
2198
2293
  # Mapping IBM data servers SQL datatypes to Ruby data types
2199
2294
  def simplified_type(field_type)
2295
+ puts_log "simplified_type"
2200
2296
  case field_type
2201
2297
  # if +field_type+ contains 'for bit data' handle it as a binary
2202
2298
  when /for bit data/i
@@ -2210,7 +2306,7 @@ module ActiveRecord
2210
2306
  when /float|double|real/i
2211
2307
  :float
2212
2308
  when /timestamp|datetime/i
2213
- :timestamp
2309
+ :datetime
2214
2310
  when /time/i
2215
2311
  :time
2216
2312
  when /date/i
@@ -2233,11 +2329,35 @@ module ActiveRecord
2233
2329
  :rowid
2234
2330
  end
2235
2331
  end # method simplified_type
2236
-
2237
-
2332
+
2333
+ def extract_value_from_default(default)
2334
+ case default
2335
+ when /IDENTITY GENERATED BY DEFAULT/i
2336
+ nil
2337
+ when /^null$/i
2338
+ nil
2339
+ # Quoted types
2340
+ when /^'(.*)'$/m
2341
+ $1.gsub("''", "'")
2342
+ # Quoted types
2343
+ when /^"(.*)"$/m
2344
+ $1.gsub('""', '"')
2345
+ # Numeric types
2346
+ when /\A-?\d+(\.\d*)?\z/
2347
+ $&
2348
+ else
2349
+ # Anything else is blank or some function
2350
+ # and we can't know the value of that, so return nil.
2351
+ nil
2352
+ end
2353
+ end
2354
+
2238
2355
  # Returns an array of Column objects for the table specified by +table_name+
2239
- def columns(table_name)
2356
+ def columns(table_name)
2357
+ default_blob_length = 1048576
2240
2358
  # to_s required because it may be a symbol.
2359
+ puts_log "columns"
2360
+ puts_log caller
2241
2361
  table_name = @servertype.set_case(table_name.to_s)
2242
2362
 
2243
2363
  # Checks if a blank table name has been given.
@@ -2249,74 +2369,96 @@ module ActiveRecord
2249
2369
  stmt = IBM_DB.columns( @connection, nil,
2250
2370
  @servertype.set_case(@schema),
2251
2371
  @servertype.set_case(table_name) )
2372
+ # sql = "select * from sysibm.sqlcolumns where table_name = #{quote(table_name.upcase)}"
2373
+ if @debug == true
2374
+ sql = "select * from syscat.columns where tabname = #{quote(table_name.upcase)}"
2375
+ puts_log "SYSIBM.SQLCOLUMNS = #{select_prepared(sql).rows}"
2376
+ end
2377
+
2252
2378
  if(stmt)
2253
2379
  begin
2254
2380
  # Fetches all the columns and assigns them to col.
2255
2381
  # +col+ is an hash with keys/value pairs for a column
2256
2382
  while col = IBM_DB.fetch_assoc(stmt)
2383
+ puts_log col
2257
2384
  column_name = col["column_name"].downcase
2258
2385
  # Assigns the column default value.
2259
2386
  column_default_value = col["column_def"]
2260
- # If there is no default value, it assigns NIL
2261
- column_default_value = nil if (column_default_value && column_default_value.upcase == 'NULL')
2262
- # If default value is IDENTITY GENERATED BY DEFAULT (this value is retrieved in case of id columns)
2263
- column_default_value = nil if (column_default_value && column_default_value.upcase =~ /IDENTITY GENERATED BY DEFAULT/i)
2264
- # Removes single quotes from the default value
2265
- column_default_value.gsub!(/^'(.*)'$/, '\1') unless column_default_value.nil?
2387
+ default_value = extract_value_from_default(column_default_value)
2266
2388
  # Assigns the column type
2267
2389
  column_type = col["type_name"].downcase
2390
+
2268
2391
  # Assigns the field length (size) for the column
2269
2392
 
2270
- original_column_type = "#{column_type}"
2271
-
2272
- column_length = col["column_size"]
2393
+ if column_type =~ /integer|bigint/i
2394
+ column_length = col["buffer_length"]
2395
+ else
2396
+ column_length = col["column_size"]
2397
+ end
2273
2398
  column_scale = col["decimal_digits"]
2274
2399
  # The initializer of the class Column, requires the +column_length+ to be declared
2275
2400
  # between brackets after the datatype(e.g VARCHAR(50)) for :string and :text types.
2276
2401
  # If it's a "for bit data" field it does a subsitution in place, if not
2277
2402
  # it appends the (column_length) string on the supported data types
2278
- unless column_length.nil? ||
2279
- column_length == '' ||
2280
- column_type.sub!(/ \(\) for bit data/i,"(#{column_length}) FOR BIT DATA") ||
2281
- !column_type =~ /char|lob|graphic/i
2282
- if column_type =~ /decimal/i
2403
+ if column_type.match(/decimal|numeric/)
2404
+ if column_length > 0 and column_scale > 0
2283
2405
  column_type << "(#{column_length},#{column_scale})"
2284
- elsif column_type =~ /smallint|integer|double|date|time|timestamp|xml|bigint/i
2285
- column_type << "" # override native limits incompatible with table create
2286
- else
2406
+ elsif column_length > 0 and column_scale == 0
2287
2407
  column_type << "(#{column_length})"
2288
2408
  end
2409
+ elsif column_type.match(/timestamp/)
2410
+ column_type << "(#{column_scale})"
2411
+ elsif column_type.match(/varchar/) and column_length > 0
2412
+ column_type << "(#{column_length})"
2289
2413
  end
2290
- # col["NULLABLE"] is 1 if the field is nullable, 0 if not.
2414
+
2291
2415
  column_nullable = (col["nullable"] == 1) ? true : false
2292
2416
  # Make sure the hidden column (db2_generated_rowid_for_lobs) in DB2 z/OS isn't added to the list
2293
- if !(column_name =~ /db2_generated_rowid_for_lobs/i)
2294
- # Pushes into the array the *IBM_DBColumn* object, created by passing to the initializer
2295
- # +column_name+, +default_value+, +column_type+ and +column_nullable+.
2296
- #if(@arelVersion >= 6 )
2297
-
2298
- #cast_type = lookup_cast_type(column_type)
2299
-
2300
- ruby_type = simplified_type2(column_type)
2417
+ if !(column_name.match(/db2_generated_rowid_for_lobs/i))
2418
+ puts_log "Column type = #{column_type}"
2419
+ ruby_type = simplified_type(column_type)
2420
+ puts_log "Ruby type after = #{ruby_type}"
2301
2421
  precision = extract_precision(ruby_type)
2302
-
2303
- #type = type_map.lookup(column_type)
2304
- sql_type = type_to_sql(column_type, column_length, precision, column_scale)
2305
-
2422
+
2423
+ if column_type.match(/timestamp|integer|bigint|date|time|blob/i)
2424
+ if column_type.match(/timestamp/i)
2425
+ precision = column_scale
2426
+ if !default_value.nil?
2427
+ default_value[10] = " "
2428
+ default_value[13] = ":"
2429
+ default_value[16] = ":"
2430
+ end
2431
+ elsif column_type.match(/time/i)
2432
+ if !default_value.nil?
2433
+ default_value[2] = ":"
2434
+ default_value[5] = ":"
2435
+ end
2436
+ end
2437
+ column_scale = nil
2438
+ if !(column_type.match(/blob/i) and column_length != default_blob_length) and !column_type.match(/bigint/i)
2439
+ column_length = nil
2440
+ end
2441
+ elsif column_type.match(/decimal|numeric/)
2442
+ precision = column_length
2443
+ column_length = nil
2444
+ end
2445
+
2446
+ if ruby_type.to_s == "boolean"
2447
+ column_type = "boolean"
2448
+ end
2449
+
2450
+ default_function = extract_default_function(default_value, column_default_value)
2451
+
2306
2452
  sqltype_metadata = SqlTypeMetadata.new(
2307
2453
  #sql_type: sql_type,
2308
- sql_type: original_column_type,
2454
+ sql_type: column_type,
2309
2455
  type: ruby_type,
2310
2456
  limit: column_length,
2311
2457
  precision: precision,
2312
2458
  scale: column_scale,
2313
2459
  )
2314
2460
 
2315
- columns << Column.new(column_name, column_default_value, sqltype_metadata, column_nullable, table_name)
2316
-
2317
- #else
2318
- # columns << IBM_DBColumn.new(column_name, column_default_value, column_type, column_nullable)
2319
- #end
2461
+ columns << Column.new(column_name, default_value, sqltype_metadata, column_nullable, default_function, comment: col["remarks"])
2320
2462
  end
2321
2463
  end
2322
2464
  rescue StandardError => fetch_error # Handle driver fetch errors
@@ -2326,7 +2468,7 @@ module ActiveRecord
2326
2468
  else
2327
2469
  error_msg = "An unexpected error occurred during retrieval of column metadata"
2328
2470
  error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
2329
- raise error_msg
2471
+ # raise error_msg
2330
2472
  end
2331
2473
  ensure # Free resources associated with the statement
2332
2474
  IBM_DB.free_stmt(stmt) if stmt
@@ -2340,10 +2482,24 @@ module ActiveRecord
2340
2482
  end
2341
2483
  end
2342
2484
  # Returns the columns array
2485
+ puts_log "Inside def columns() #{columns}"
2343
2486
  return columns
2344
2487
  end
2345
-
2488
+
2489
+ def extract_precision(sql_type)
2490
+ $1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/
2491
+ end
2492
+
2493
+ def extract_default_function(default_value, default)
2494
+ default if has_default_function?(default_value, default)
2495
+ end
2496
+
2497
+ def has_default_function?(default_value, default)
2498
+ !default_value && %r{\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
2499
+ end
2500
+
2346
2501
  def foreign_keys(table_name)
2502
+ puts_log "foreign_keys #{table_name}"
2347
2503
  #fetch the foreign keys of the table using function foreign_keys
2348
2504
  #PKTABLE_NAME:: fk_row[2] Name of the table containing the primary key.
2349
2505
  #PKCOLUMN_NAME:: fk_row[3] Name of the column containing the primary key.
@@ -2359,15 +2515,16 @@ module ActiveRecord
2359
2515
 
2360
2516
  if(stmt)
2361
2517
  begin
2362
- while ( fk_row = IBM_DB.fetch_array(stmt) )
2518
+ while ( fk_row = IBM_DB.fetch_array(stmt) )
2519
+ puts_log "foreign_keys fetch = #{fk_row}"
2363
2520
  options = {
2364
- column: fk_row[7],
2365
- name: fk_row[11],
2366
- primary_key: fk_row[3],
2521
+ column: fk_row[7].downcase,
2522
+ name: fk_row[11].downcase,
2523
+ primary_key: fk_row[3].downcase,
2367
2524
  }
2368
2525
  options[:on_update] = extract_foreign_key_action(fk_row[9])
2369
2526
  options[:on_delete] = extract_foreign_key_action(fk_row[10])
2370
- foreignKeys << ForeignKeyDefinition.new(fk_row[6], table_name, options)
2527
+ foreignKeys << ForeignKeyDefinition.new(fk_row[6].downcase, fk_row[2].downcase, options)
2371
2528
  end
2372
2529
 
2373
2530
  rescue StandardError => fetch_error # Handle driver fetch errors
@@ -2377,7 +2534,7 @@ module ActiveRecord
2377
2534
  else
2378
2535
  error_msg = "An unexpected error occurred during retrieval of foreign key metadata"
2379
2536
  error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
2380
- raise error_msg
2537
+ # raise error_msg
2381
2538
  end
2382
2539
  ensure # Free resources associated with the statement
2383
2540
  IBM_DB.free_stmt(stmt) if stmt
@@ -2390,24 +2547,25 @@ module ActiveRecord
2390
2547
  raise StandardError.new('An unexpected error occurred during foreign key retrieval')
2391
2548
  end
2392
2549
  end
2393
- #Returns the foreignKeys array
2394
- return foreignKeys
2395
- end
2550
+ #Returns the foreignKeys array
2551
+ return foreignKeys
2552
+ end
2396
2553
 
2397
- def extract_foreign_key_action(specifier) # :nodoc:
2398
- case specifier
2399
- when 0; :cascade
2400
- when 1; :restrict
2401
- when 2; :nullify
2402
- when 3; :noaction
2403
- end
2554
+ def extract_foreign_key_action(specifier) # :nodoc:
2555
+ puts_log "extract_foreign_key_action"
2556
+ case specifier
2557
+ when 0; :cascade
2558
+ when 1; :restrict
2559
+ when 2; :nullify
2560
+ end
2404
2561
  end
2405
2562
 
2406
2563
  def supports_disable_referential_integrity? #:nodoc:
2407
- true
2408
- end
2564
+ true
2565
+ end
2409
2566
 
2410
- def disable_referential_integrity #:nodoc:
2567
+ def disable_referential_integrity #:nodoc:
2568
+ puts_log "disable_referential_integrity"
2411
2569
  if supports_disable_referential_integrity?
2412
2570
  alter_foreign_keys(tables, true)
2413
2571
  end
@@ -2416,43 +2574,303 @@ module ActiveRecord
2416
2574
  ensure
2417
2575
  if supports_disable_referential_integrity?
2418
2576
  alter_foreign_keys(tables, false)
2419
- end
2420
-
2421
- end
2577
+ end
2578
+ end
2422
2579
 
2423
2580
  def alter_foreign_keys(tables, not_enforced)
2581
+ puts_log "alter_foreign_keys"
2424
2582
  enforced = not_enforced ? 'NOT ENFORCED' : 'ENFORCED'
2425
2583
  tables.each do |table|
2426
2584
  foreign_keys(table).each do |fk|
2427
- execute("ALTER TABLE #{@servertype.set_case(fk.from_table)} ALTER FOREIGN KEY #{@servertype.set_case(fk.name)} #{enforced}")
2585
+ execute("ALTER TABLE #{@servertype.set_case(fk.from_table)} ALTER FOREIGN KEY #{@servertype.set_case(fk.name)} #{enforced}")
2586
+ end
2587
+ end
2588
+ end
2589
+
2590
+ def primary_keys(table_name) # :nodoc:
2591
+ puts_log "primary_keys"
2592
+ raise ArgumentError unless table_name.present?
2593
+ primary_key(table_name)
2594
+ end
2595
+
2596
+ # Adds comment for given table column or drops it if +comment+ is a +nil+
2597
+ def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
2598
+ puts_log "change_column_comment"
2599
+ clear_cache!
2600
+ comment = extract_new_comment_value(comment_or_changes)
2601
+ if comment.nil?
2602
+ execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS ''"
2603
+ else
2604
+ execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}"
2605
+ end
2606
+ end
2607
+
2608
+ # Adds comment for given table or drops it if +comment+ is a +nil+
2609
+ def change_table_comment(table_name, comment_or_changes) # :nodoc:
2610
+ puts_log "change_table_comment"
2611
+ clear_cache!
2612
+ comment = extract_new_comment_value(comment_or_changes)
2613
+ if comment.nil?
2614
+ execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS ''"
2615
+ else
2616
+ execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}"
2617
+ end
2618
+ end
2619
+
2620
+ def add_column(table_name, column_name, type, **options) # :nodoc:
2621
+ puts_log "add_column"
2622
+ clear_cache!
2623
+ puts_log "add_column info #{table_name}, #{column_name}, #{type}, #{options}"
2624
+ puts_log caller
2625
+ if (!type.nil? && type.to_s == "primary_key") or (options.key?(:primary_key) and options[:primary_key] == true)
2626
+ if !type.nil? and type.to_s != "primary_key"
2627
+ execute "ALTER TABLE #{table_name} ADD COLUMN #{column_name} #{type} NOT NULL DEFAULT 0"
2628
+ else
2629
+ execute "ALTER TABLE #{table_name} ADD COLUMN #{column_name} INTEGER NOT NULL DEFAULT 0"
2428
2630
  end
2631
+ execute "ALTER TABLE #{table_name} alter column #{column_name} drop default"
2632
+ execute "ALTER TABLE #{table_name} alter column #{column_name} set GENERATED BY DEFAULT AS IDENTITY (START WITH 1000)"
2633
+ execute "ALTER TABLE #{table_name} add primary key (#{column_name})"
2634
+ else
2635
+ super
2429
2636
  end
2637
+ change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
2638
+ end
2639
+
2640
+ def table_comment(table_name) # :nodoc:
2641
+ puts_log "table_comment"
2642
+ sql = "select remarks from syscat.tables where tabname = #{quote(table_name.upcase)}"
2643
+ single_value_from_rows(select_prepared(sql).rows)
2644
+ end
2645
+
2646
+ def add_index(table_name, column_name, **options) #:nodoc:
2647
+ puts_log "add_index"
2648
+ index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
2649
+
2650
+ return if if_not_exists && index_exists?(table_name, column_name, name: index.name)
2651
+ if_not_exists = false if if_not_exists
2652
+ create_index = CreateIndexDefinition.new(index, algorithm, if_not_exists)
2653
+ result = execute schema_creation.accept(create_index)
2654
+
2655
+ execute "COMMENT ON INDEX #{quote_column_name(index.name)} IS #{quote(index.comment)}" if index.comment
2656
+ result
2657
+ end
2658
+
2659
+ def query_values(sql, name = nil) # :nodoc:
2660
+ puts_log "query_values"
2661
+ select_prepared(sql).rows.map(&:first)
2662
+ end
2663
+
2664
+ def data_source_sql(name = nil, type: nil)
2665
+ puts_log "data_source_sql"
2666
+ sql = +"SELECT tabname FROM (SELECT tabname, type FROM syscat.tables "
2667
+ sql << " WHERE tabschema = #{quote(@schema.upcase)}) subquery"
2668
+ if type || name
2669
+ conditions = []
2670
+ conditions << "subquery.type = #{quote(type.upcase)}" if type
2671
+ conditions << "subquery.tabname = #{quote(name.upcase)}" if name
2672
+ sql << " WHERE #{conditions.join(" AND ")}"
2673
+ end
2674
+ sql
2430
2675
  end
2431
2676
 
2677
+ # Returns an array of table names defined in the database.
2678
+ def tables
2679
+ puts_log "tables"
2680
+ query_values(data_source_sql(type: "T"), "SCHEMA").map(&:downcase)
2681
+ end
2682
+
2683
+ # Checks to see if the table +table_name+ exists on the database.
2684
+ #
2685
+ # table_exists?(:developers)
2686
+ #
2687
+ def table_exists?(table_name)
2688
+ puts_log "table_exists? = #{table_name}"
2689
+ query_values(data_source_sql(table_name, type: "T"), "SCHEMA").any? if table_name.present?
2690
+ rescue NotImplementedError
2691
+ tables.include?(table_name.to_s)
2692
+ end
2693
+
2694
+ # Returns an array of view names defined in the database.
2695
+ def views
2696
+ puts_log "views"
2697
+ query_values(data_source_sql(type: "V"), "SCHEMA").map(&:downcase)
2698
+ end
2699
+
2700
+ # Checks to see if the view +view_name+ exists on the database.
2701
+ #
2702
+ # view_exists?(:ebooks)
2703
+ #
2704
+ def view_exists?(view_name)
2705
+ puts_log "view_exists?"
2706
+ query_values(data_source_sql(view_name, type: "V"), "SCHEMA").any? if view_name.present?
2707
+ rescue NotImplementedError
2708
+ views.include?(view_name.to_s)
2709
+ end
2710
+
2711
+ # Returns the relation names useable to back Active Record models.
2712
+ # For most adapters this means all #tables and #views.
2713
+ def data_sources
2714
+ puts_log "data_sources"
2715
+ query_values(data_source_sql, "SCHEMA").map(&:downcase)
2716
+ rescue NotImplementedError
2717
+ tables | views
2718
+ end
2719
+
2720
+ def create_schema_dumper(options)
2721
+ puts_log "create_schema_dumper"
2722
+ SchemaDumper.create(self, options)
2723
+ end
2724
+
2725
+ def table_options(table_name) # :nodoc:
2726
+ puts_log "table_options"
2727
+ if comment = table_comment(table_name)
2728
+ { comment: comment }
2729
+ end
2730
+ end
2731
+
2732
+ def remove_columns(table_name, *column_names, type: nil, **options)
2733
+ if column_names.empty?
2734
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)")
2735
+ end
2736
+
2737
+ remove_column_fragments = remove_columns_for_alter(table_name, *column_names, type: type, **options)
2738
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_fragments.join(' ')}"
2739
+ end
2740
+
2432
2741
  # Renames a table.
2433
2742
  # ==== Example
2434
2743
  # rename_table('octopuses', 'octopi')
2435
2744
  # Overriden to satisfy IBM data servers syntax
2436
2745
  def rename_table(name, new_name)
2746
+ puts_log "rename_table"
2747
+ name = quote_column_name(name)
2748
+ new_name = quote_column_name(new_name)
2749
+ puts_log "90 old_table = #{name}, new_table = #{new_name}"
2437
2750
  # SQL rename table statement
2751
+ index_list = indexes(name)
2752
+ puts_log "Index List = #{index_list}"
2753
+ drop_table_indexes(index_list)
2438
2754
  rename_table_sql = "RENAME TABLE #{name} TO #{new_name}"
2439
2755
  stmt = execute(rename_table_sql)
2756
+ create_table_indexes(index_list, new_name)
2440
2757
  # Ensures to free the resources associated with the statement
2441
2758
  ensure
2442
2759
  IBM_DB.free_stmt(stmt) if stmt
2443
2760
  end
2444
2761
 
2445
- # Renames a column.
2446
- # ===== Example
2447
- # rename_column(:suppliers, :description, :name)
2448
- def rename_column(table_name, column_name, new_column_name)
2449
- @servertype.rename_column(table_name, column_name, new_column_name)
2762
+ def drop_table_indexes(index_list)
2763
+ puts_log "drop_table_indexes"
2764
+ index_list.each do |indexs|
2765
+ remove_index(indexs.table, name: indexs.name)
2766
+ end
2767
+ end
2768
+
2769
+ def create_table_indexes(index_list, new_table)
2770
+ puts_log "create_table_indexes"
2771
+ index_list.each do |indexs|
2772
+ generated_index_name = index_name(indexs.table, column: indexs.columns)
2773
+ custom_index_name = indexs.name
2774
+
2775
+ if generated_index_name == custom_index_name
2776
+ add_index(new_table, indexs.columns, unique: indexs.unique)
2777
+ else
2778
+ add_index(new_table, indexs.columns, name: custom_index_name, unique: indexs.unique)
2779
+ end
2780
+ end
2781
+ end
2782
+
2783
+ def drop_column_indexes(index_list, column_name)
2784
+ puts_log "drop_column_indexes"
2785
+ index_list.each do |indexs|
2786
+ if indexs.columns.class == Array
2787
+ next if !indexs.columns.include?(column_name)
2788
+ else
2789
+ next if indexs.columns != column_name
2790
+ end
2791
+ remove_index(indexs.table, name: indexs.name)
2792
+ end
2793
+ end
2794
+
2795
+ def create_column_indexes(index_list, column_name, new_column_name)
2796
+ puts_log "create_column_indexes"
2797
+ index_list.each do |indexs|
2798
+ generated_index_name = index_name(indexs.table, column: indexs.columns)
2799
+ custom_index_name = indexs.name
2800
+ if indexs.columns.class == Array
2801
+ next if !indexs.columns.include?(column_name)
2802
+ indexs.columns[indexs.columns.index(column_name)] = new_column_name
2803
+ else
2804
+ next if indexs.columns != column_name
2805
+ indexs.columns = new_column_name
2806
+ end
2807
+
2808
+ if generated_index_name == custom_index_name
2809
+ add_index(indexs.table, indexs.columns, unique: indexs.unique)
2810
+ else
2811
+ add_index(indexs.table, indexs.columns, name: custom_index_name, unique: indexs.unique)
2812
+ end
2813
+ end
2814
+ end
2815
+
2816
+ # Renames a column in a table.
2817
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
2818
+ puts_log "rename_column"
2819
+ column_name = quote_column_name(column_name)
2820
+ new_column_name = quote_column_name(new_column_name)
2821
+ puts_log "rename_column #{table_name}, #{column_name}, #{new_column_name}"
2822
+ clear_cache!
2823
+ index_list = indexes(table_name)
2824
+ puts_log "Index List = #{index_list}"
2825
+ fkey_list = foreign_keys(table_name)
2826
+ puts_log "ForeignKey = #{fkey_list}"
2827
+ drop_column_indexes(index_list, column_name)
2828
+ fkey_removed = remove_foreign_key_byColumn(fkey_list, table_name, column_name)
2829
+ execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
2830
+ add_foreign_keyList(fkey_list, table_name, column_name, new_column_name) if fkey_removed
2831
+ create_column_indexes(index_list, column_name, new_column_name)
2832
+ end
2833
+
2834
+ def add_foreign_keyList(fkey_list, table_name, column_name, new_column_name)
2835
+ puts_log "add_foreign_keyList = #{table_name}, #{column_name}, #{fkey_list}"
2836
+ fkey_list.each do |fkey|
2837
+ if fkey.options[:column] == column_name
2838
+ add_foreign_key(table_name, strip_table_name_prefix_and_suffix(fkey.to_table), column: new_column_name)
2839
+ end
2840
+ end
2841
+ end
2842
+
2843
+ def remove_foreign_key_byColumn(fkey_list, table_name, column_name)
2844
+ puts_log "remove_foreign_key_byColumn = #{table_name}, #{column_name}, #{fkey_list}"
2845
+ fkey_removed = false
2846
+ fkey_list.each do |fkey|
2847
+ if fkey.options[:column] == column_name
2848
+ remove_foreign_key(table_name, column: column_name)
2849
+ fkey_removed = true
2850
+ end
2851
+ end
2852
+ fkey_removed
2853
+ end
2854
+
2855
+ def rename_index(table_name, old_name, new_name)
2856
+ puts_log "rename_index"
2857
+ old_name = old_name.to_s
2858
+ new_name = new_name.to_s
2859
+ validate_index_length!(table_name, new_name)
2860
+
2861
+ # this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
2862
+ old_index_def = indexes(table_name).detect { |i| i.name == old_name }
2863
+ return unless old_index_def
2864
+ remove_index(table_name, name: old_name)
2865
+ add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
2450
2866
  end
2451
2867
 
2452
2868
  # Removes the column from the table definition.
2453
2869
  # ===== Examples
2454
2870
  # remove_column(:suppliers, :qualification)
2455
- def remove_column(table_name, column_name)
2871
+ def remove_column(table_name, column_name, type = nil, **options)
2872
+ puts_log "remove_column"
2873
+ return if options[:if_exists] == true && !column_exists?(table_name, column_name)
2456
2874
  @servertype.remove_column(table_name, column_name)
2457
2875
  end
2458
2876
 
@@ -2462,28 +2880,20 @@ module ActiveRecord
2462
2880
  # change_column(:suppliers, :name, :string, :limit => 80)
2463
2881
  # change_column(:accounts, :description, :text)
2464
2882
  def change_column(table_name, column_name, type, options = {})
2883
+ puts_log "change_column"
2465
2884
  @servertype.change_column(table_name, column_name, type, options)
2885
+ change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
2466
2886
  end
2467
2887
 
2468
2888
  #Add distinct clause to the sql if there is no order by specified
2469
2889
  def distinct(columns, order_by)
2890
+ puts_log "distinct"
2470
2891
  if order_by.nil?
2471
2892
  "DISTINCT #{columns}"
2472
2893
  else
2473
2894
  "#{columns}"
2474
2895
  end
2475
2896
  end
2476
-
2477
- def columns_for_distinct(columns, orders) #:nodoc:
2478
- order_columns = orders.reject(&:blank?).map{ |s|
2479
- # Convert Arel node to string
2480
- s = s.to_sql unless s.is_a?(String)
2481
- # Remove any ASC/DESC modifiers
2482
- s.gsub(/\s+(?:ASC|DESC)\b/i, '')
2483
- .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '')
2484
- }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
2485
- [super, *order_columns].join(', ')
2486
- end
2487
2897
 
2488
2898
  # Sets a new default value for a column. This does not set the default
2489
2899
  # value to +NULL+, instead, it needs DatabaseStatements#execute which
@@ -2493,43 +2903,42 @@ module ActiveRecord
2493
2903
  # change_column_default(:accounts, :authorized, 1)
2494
2904
  # Method overriden to satisfy IBM data servers syntax.
2495
2905
  def change_column_default(table_name, column_name, default)
2906
+ puts_log "change_column_default"
2496
2907
  @servertype.change_column_default(table_name, column_name, default)
2497
2908
  end
2498
2909
 
2499
2910
  #Changes the nullability value of a column
2500
2911
  def change_column_null(table_name, column_name, null, default = nil)
2912
+ puts_log "change_column_null"
2501
2913
  @servertype.change_column_null(table_name, column_name, null, default)
2502
2914
  end
2503
2915
 
2504
- # Remove the given index from the table.
2505
- #
2506
- # Remove the suppliers_name_index in the suppliers table (legacy support, use the second or third forms).
2507
- # remove_index :suppliers, :name
2508
- # Remove the index named accounts_branch_id in the accounts table.
2509
- # remove_index :accounts, :column => :branch_id
2510
- # Remove the index named by_branch_party in the accounts table.
2511
- # remove_index :accounts, :name => :by_branch_party
2512
- #
2513
- # You can remove an index on multiple columns by specifying the first column.
2514
- # add_index :accounts, [:username, :password]
2515
- # remove_index :accounts, :username
2516
- # Overriden to use the IBM data servers SQL syntax.
2517
- def remove_index(table_name, options = {})
2518
- execute("DROP INDEX #{index_name(table_name, options)}")
2916
+ def remove_index(table_name, column_name = nil, **options)
2917
+ puts_log "remove_index"
2918
+ return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
2919
+ execute("DROP INDEX #{index_name_for_remove(table_name, column_name, options)}")
2920
+ end
2921
+
2922
+ def column_for(table_name, column_name)
2923
+ super
2519
2924
  end
2520
2925
 
2521
2926
  protected
2522
- def initialize_type_map(m) # :nodoc:
2927
+ def initialize_type_map(m = type_map) # :nodoc:
2928
+ puts_log "initialize_type_map"
2523
2929
  register_class_with_limit m, %r(boolean)i, Type::Boolean
2524
2930
  register_class_with_limit m, %r(char)i, Type::String
2525
2931
  register_class_with_limit m, %r(binary)i, Type::Binary
2526
2932
  register_class_with_limit m, %r(text)i, Type::Text
2527
- register_class_with_limit m, %r(date)i, Type::Date
2528
- register_class_with_limit m, %r(time)i, Type::Time
2529
- register_class_with_limit m, %r(datetime)i, Type::DateTime
2933
+ register_class_with_precision m, %r(date)i, Type::Date
2934
+ register_class_with_precision m, %r(time)i, Type::Time
2935
+ register_class_with_precision m, %r(datetime)i, Type::DateTime
2530
2936
  register_class_with_limit m, %r(float)i, Type::Float
2531
- register_class_with_limit m, %r(int)i, Type::Integer
2532
-
2937
+
2938
+ m.register_type %r(^bigint)i, Type::Integer.new(limit: 8)
2939
+ m.register_type %r(^int)i, Type::Integer.new(limit: 4)
2940
+ m.register_type %r(^smallint)i, Type::Integer.new(limit: 2)
2941
+ m.register_type %r(^tinyint)i, Type::Integer.new(limit: 1)
2533
2942
 
2534
2943
  m.alias_type %r(blob)i, 'binary'
2535
2944
  m.alias_type %r(clob)i, 'text'
@@ -2552,13 +2961,21 @@ module ActiveRecord
2552
2961
 
2553
2962
  m.alias_type %r(xml)i, 'text'
2554
2963
  m.alias_type %r(for bit data)i, 'binary'
2555
- m.alias_type %r(smallint)i, 'boolean'
2556
2964
  m.alias_type %r(serial)i, 'int'
2557
2965
  m.alias_type %r(decfloat)i, 'decimal'
2558
2966
  m.alias_type %r(real)i, 'decimal'
2559
2967
  m.alias_type %r(graphic)i, 'binary'
2560
2968
  m.alias_type %r(rowid)i, 'int'
2561
2969
  end
2970
+
2971
+ class SchemaDumper < ConnectionAdapters::SchemaDumper
2972
+ def dump(stream) # Like in abstract class, we no need to call header() & trailer().
2973
+ header(stream)
2974
+ extensions(stream)
2975
+ tables(stream)
2976
+ stream
2977
+ end
2978
+ end
2562
2979
  end # class IBM_DBAdapter
2563
2980
 
2564
2981
  # This class contains common code across DB's (DB2 LUW, zOS, i5 and IDS)
@@ -2581,12 +2998,14 @@ module ActiveRecord
2581
2998
  end
2582
2999
 
2583
3000
  def check_reserved_words(col_name)
3001
+ @adapter.puts_log "check_reserved_words"
2584
3002
  col_name.to_s
2585
3003
  end
2586
3004
 
2587
3005
  # This is supported by the DB2 for Linux, UNIX, Windows data servers
2588
3006
  # and by the DB2 for i5 data servers
2589
3007
  def remove_column(table_name, column_name)
3008
+ @adapter.puts_log "remove_column"
2590
3009
  begin
2591
3010
  @adapter.execute "ALTER TABLE #{table_name} DROP #{column_name}"
2592
3011
  reorg_table(table_name)
@@ -2603,12 +3022,13 @@ To remove the column, the table must be dropped and recreated without the #{colu
2603
3022
  end
2604
3023
 
2605
3024
  def select(stmt)
3025
+ @adapter.puts_log "select"
2606
3026
  results = []
2607
3027
  # Fetches all the results available. IBM_DB.fetch_assoc(stmt) returns
2608
3028
  # an hash for each single record.
2609
3029
  # The loop stops when there aren't any more valid records to fetch
2610
3030
  begin
2611
- if(@isAr3)
3031
+ if(@isAr3)
2612
3032
  while single_hash = IBM_DB.fetch_assoc(stmt)
2613
3033
  # Add the record to the +results+ array
2614
3034
  results << single_hash
@@ -2633,6 +3053,7 @@ To remove the column, the table must be dropped and recreated without the #{colu
2633
3053
  end
2634
3054
 
2635
3055
  def select_rows(sql, name, stmt, results)
3056
+ @adapter.puts_log "select_rows"
2636
3057
  # Fetches all the results available. IBM_DB.fetch_array(stmt) returns
2637
3058
  # an array representing a row in a result set.
2638
3059
  # The loop stops when there aren't any more valid records to fetch
@@ -2656,6 +3077,7 @@ To remove the column, the table must be dropped and recreated without the #{colu
2656
3077
 
2657
3078
  # Praveen
2658
3079
  def prepare(sql,name = nil)
3080
+ @adapter.puts_log "prepare"
2659
3081
  begin
2660
3082
  stmt = IBM_DB.prepare(@adapter.connection, sql)
2661
3083
  if( stmt )
@@ -2667,61 +3089,39 @@ To remove the column, the table must be dropped and recreated without the #{colu
2667
3089
  if prep_err && !prep_err.message.empty?
2668
3090
  raise "Failed to prepare sql #{sql} due to: #{prep_err}"
2669
3091
  else
2670
- raise
3092
+ raise "An unexpected error occurred during SQLprepare"
2671
3093
  end
2672
3094
  end
2673
3095
  end
2674
3096
 
2675
3097
  # Akhil Tcheck for if_exits added so that it will try to drop even if the table does not exit.
2676
3098
  def execute(sql, name = nil)
2677
- if name == nil || name.class == String
3099
+ @adapter.puts_log "execute #{sql}"
3100
+
2678
3101
  begin
2679
- if stmt = IBM_DB.exec(@adapter.connection, sql)
3102
+ if @adapter.connection == nil || @adapter.connection == false
3103
+ raise ActiveRecord::ConnectionNotEstablished, "called on a closed database"
3104
+ elsif stmt = IBM_DB.exec(@adapter.connection, sql)
2680
3105
  stmt # Return the statement object
2681
3106
  else
2682
- raise StatementInvalid, IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN )
3107
+ raise StatementInvalid, IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN ), sql
2683
3108
  end
2684
3109
  rescue StandardError => exec_err
2685
3110
  if exec_err && !exec_err.message.empty?
2686
- raise "Failed to execute statement due to: #{exec_err}"
3111
+ @adapter.puts_log "104 error = #{exec_err.message}"
3112
+ @adapter.puts_log "104 sql = #{sql}"
3113
+ raise StatementInvalid
2687
3114
  else
2688
3115
  raise
2689
3116
  end
2690
3117
  end
2691
- else
2692
- if name[:if_exists]
2693
- IBM_DB.exec(@adapter.connection, sql)
2694
- else
2695
- begin
2696
- if stmt = IBM_DB.exec(@adapter.connection, sql)
2697
- stmt # Return the statement object
2698
- else
2699
- raise StatementInvalid, IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN )
2700
- end
2701
- rescue StandardError => exec_err
2702
- if exec_err && !exec_err.message.empty?
2703
- raise "Failed to execute statement due to: #{exec_err}"
2704
- else
2705
- raise
2706
- end
2707
- end
2708
- end
2709
- end
2710
3118
  end
2711
3119
 
2712
3120
  def set_schema(schema)
3121
+ @adapter.puts_log "set_schema"
2713
3122
  @adapter.execute("SET SCHEMA #{schema}")
2714
3123
  end
2715
3124
 
2716
- def query_offset_limit(sql, offset, limit)
2717
- end
2718
-
2719
- def get_limit_offset_clauses(limit, offset)
2720
- end
2721
-
2722
- def query_offset_limit!(sql, offset, limit, options)
2723
- end
2724
-
2725
3125
  def get_datetime_mapping
2726
3126
  end
2727
3127
 
@@ -2761,10 +3161,12 @@ To remove the column, the table must be dropped and recreated without the #{colu
2761
3161
  end
2762
3162
 
2763
3163
  def rename_column(table_name, column_name, new_column_name)
3164
+ @adapter.puts_log "primary_key_definition"
2764
3165
  raise NotImplementedError, "rename_column is not implemented yet in the IBM_DB Adapter"
2765
3166
  end
2766
3167
 
2767
3168
  def primary_key_definition(start_id)
3169
+ @adapter.puts_log "108"
2768
3170
  return "INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH #{start_id}) PRIMARY KEY NOT NULL"
2769
3171
  end
2770
3172
 
@@ -2773,6 +3175,7 @@ To remove the column, the table must be dropped and recreated without the #{colu
2773
3175
  # The "stmt" parameter is ignored for DB2 but used for IDS
2774
3176
  def last_generated_id(stmt)
2775
3177
  # Queries the db to obtain the last ID that was automatically generated
3178
+ @adapter.puts_log "last_generated_id"
2776
3179
  sql = "SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1"
2777
3180
  stmt = IBM_DB.prepare(@adapter.connection, sql)
2778
3181
  if(stmt)
@@ -2781,7 +3184,8 @@ To remove the column, the table must be dropped and recreated without the #{colu
2781
3184
  # Fetches the only record available (containing the last id)
2782
3185
  IBM_DB.fetch_row(stmt)
2783
3186
  # Retrieves and returns the result of the query with the last id.
2784
- IBM_DB.result(stmt,0)
3187
+ id_value = IBM_DB.result(stmt,0)
3188
+ return id_value.to_i
2785
3189
  rescue StandardError => fetch_error # Handle driver fetch errors
2786
3190
  error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
2787
3191
  if error_msg && !error_msg.empty?
@@ -2815,33 +3219,54 @@ To remove the column, the table must be dropped and recreated without the #{colu
2815
3219
  end
2816
3220
 
2817
3221
  def change_column(table_name, column_name, type, options)
2818
- if !options[:default].nil?
2819
- change_column_default(table_name, column_name, options[:default])
2820
- else
3222
+ @adapter.puts_log "change_column #{table_name.to_s}, #{column_name.to_s}, #{type.to_s}"
3223
+ column = @adapter.column_for(table_name, column_name)
2821
3224
  data_type = @adapter.type_to_sql(type, options[:limit], options[:precision], options[:scale])
2822
- begin
2823
- execute "ALTER TABLE #{table_name} ALTER #{column_name} SET DATA TYPE #{data_type}"
2824
- rescue StandardError => exec_err
2825
- if exec_err.message.include?('SQLCODE=-190')
2826
- raise StatementInvalid,
2827
- "Please consult documentation for compatible data types while changing column datatype. \
2828
- The column datatype change to [#{data_type}] is not supported by this data server: #{exec_err}"
2829
- else
2830
- raise "#{exec_err}"
3225
+
3226
+ if column.sql_type != data_type
3227
+ begin
3228
+ execute "ALTER TABLE #{table_name} ALTER #{column_name} SET DATA TYPE #{data_type}"
3229
+ rescue StandardError => exec_err
3230
+ if exec_err.message.include?('SQLCODE=-190')
3231
+ raise StatementInvalid,
3232
+ "Please consult documentation for compatible data types while changing column datatype. \
3233
+ The column datatype change to [#{data_type}] is not supported by this data server: #{exec_err}"
3234
+ else
3235
+ raise "#{exec_err}"
3236
+ end
2831
3237
  end
3238
+ reorg_table(table_name)
3239
+ end
3240
+
3241
+ if options.key?(:default)
3242
+ change_column_default(table_name, column_name, options[:default])
3243
+ end
3244
+ if options.key?(:null)
3245
+ change_column_null(table_name, column_name, options[:null], nil)
3246
+ end
3247
+ end
3248
+
3249
+ def extract_new_default_value(default_or_changes)
3250
+ @adapter.puts_log "extract_new_default_value"
3251
+ if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
3252
+ default_or_changes[:to]
3253
+ else
3254
+ default_or_changes
2832
3255
  end
2833
- reorg_table(table_name)
2834
- change_column_null(table_name,column_name,options[:null],nil)
2835
- change_column_default(table_name, column_name, options[:default])
2836
- reorg_table(table_name)
2837
3256
  end
2838
- end
2839
3257
 
2840
3258
  # DB2 specific ALTER TABLE statement to add a default clause
2841
3259
  def change_column_default(table_name, column_name, default)
3260
+ @adapter.puts_log "change_column_default #{table_name.to_s} #{column_name.to_s}"
3261
+ @adapter.puts_log "Default: #{default}"
3262
+
3263
+ default = extract_new_default_value(default)
2842
3264
  # SQL statement which alters column's default value
2843
- change_column_sql = "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} \
2844
- SET WITH DEFAULT #{@adapter.quote(default)}"
3265
+ if default.nil?
3266
+ change_column_sql = "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET WITH DEFAULT NULL"
3267
+ else
3268
+ change_column_sql = "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET WITH DEFAULT #{@adapter.quote(default)}"
3269
+ end
2845
3270
 
2846
3271
  stmt = execute(change_column_sql)
2847
3272
  reorg_table(table_name)
@@ -2851,6 +3276,7 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
2851
3276
 
2852
3277
  #DB2 specific ALTER TABLE statement to change the nullability of a column
2853
3278
  def change_column_null(table_name, column_name, null, default)
3279
+ @adapter.puts_log "change_column_null #{table_name.to_s} #{column_name.to_s}"
2854
3280
  if !default.nil?
2855
3281
  change_column_default(table_name, column_name, default)
2856
3282
  end
@@ -2872,131 +3298,47 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
2872
3298
  # This method returns the DB2 SQL type corresponding to the Rails
2873
3299
  # datetime/timestamp type
2874
3300
  def get_datetime_mapping
3301
+ @adapter.puts_log "get_datetime_mapping"
2875
3302
  return "timestamp"
2876
3303
  end
2877
3304
 
2878
3305
  # This method returns the DB2 SQL type corresponding to the Rails
2879
3306
  # time type
2880
3307
  def get_time_mapping
3308
+ @adapter.puts_log "get_time_mapping"
2881
3309
  return "time"
2882
3310
  end
2883
3311
 
2884
3312
  #This method returns the DB2 SQL type corresponding to Rails double type
2885
3313
  def get_double_mapping
3314
+ @adapter.puts_log "get_double_mapping"
2886
3315
  return "double"
2887
3316
  end
2888
3317
 
2889
- def get_limit_offset_clauses(limit, offset)
2890
- retHash = {"endSegment"=> "", "startSegment" => ""}
2891
- if(offset.nil? && limit.nil?)
2892
- return retHash
2893
- end
2894
-
2895
-
2896
- if (offset.nil?)
2897
- retHash["endSegment"] = " FETCH FIRST #{limit} ROWS ONLY"
2898
- return retHash
2899
- end
2900
-
2901
- #if(limit.nil?)
2902
- if(limit.nil?)
2903
- #retHash["startSegment"] = "SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM ( SELECT "
2904
- retHash["startSegment"] = "SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM ( "
2905
- retHash["endSegment"] = " ) AS I) AS O WHERE sys_row_num > #{offset}"
2906
- return retHash
2907
- end
2908
-
2909
- # Defines what will be the last record
2910
- last_record = offset.to_i + limit.to_i
2911
- #retHash["startSegment"] = "SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM ( SELECT "
2912
- retHash["startSegment"] = "SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM ( "
2913
-
2914
- if last_record < offset+1
2915
- retHash["endSegment"] = " ) AS I) AS O WHERE sys_row_num BETWEEN #{last_record} AND #{offset+1}"
2916
- else
2917
- retHash["endSegment"] = " ) AS I) AS O WHERE sys_row_num BETWEEN #{offset+1} AND #{last_record}"
2918
- end
2919
-
2920
- return retHash
2921
- end
2922
-
2923
- def query_offset_limit(sql, offset, limit)
2924
- if(offset.nil? && limit.nil?)
2925
- return sql
2926
- end
2927
-
2928
- if (offset.nil?)
2929
- return sql << " FETCH FIRST #{limit} ROWS ONLY"
2930
- end
2931
-
2932
- if(limit.nil?)
2933
- sql.sub!(/SELECT/i,"SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM (SELECT")
2934
- return sql << ") AS I) AS O WHERE sys_row_num > #{offset}"
2935
- end
2936
-
2937
- # Defines what will be the last record
2938
- last_record = offset + limit
2939
- # Transforms the SELECT query in order to retrieve/fetch only
2940
- # a number of records after the specified offset.
2941
- # 'select' or 'SELECT' is replaced with the partial query below that adds the sys_row_num column
2942
- # to select with the condition of this column being between offset+1 and the offset+limit
2943
- sql.sub!(/SELECT/i,"SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM (SELECT")
2944
- # The final part of the query is appended to include a WHERE...BETWEEN...AND condition,
2945
- # and retrieve only a LIMIT number of records starting from the OFFSET+1
2946
- sql << ") AS I) AS O WHERE sys_row_num BETWEEN #{offset+1} AND #{last_record}"
2947
- end
2948
-
2949
- def query_offset_limit!(sql, offset, limit, options)
2950
- if(offset.nil? && limit.nil?)
2951
- options[:paramArray] = []
2952
- return sql
2953
- end
2954
-
2955
- if (offset.nil?)
2956
- options[:paramArray] = []
2957
- return sql << " FETCH FIRST #{limit} ROWS ONLY"
2958
- end
2959
-
2960
- if(limit.nil?)
2961
- sql.sub!(/SELECT/i,"SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM (SELECT")
2962
- sql << ") AS I) AS O WHERE sys_row_num > ?"
2963
- options[:paramArray] = [offset]
2964
- return
2965
- end
2966
-
2967
- # Defines what will be the last record
2968
- last_record = offset + limit
2969
- # Transforms the SELECT query in order to retrieve/fetch only
2970
- # a number of records after the specified offset.
2971
- # 'select' or 'SELECT' is replaced with the partial query below that adds the sys_row_num column
2972
- # to select with the condition of this column being between offset+1 and the offset+limit
2973
- sql.sub!(/SELECT/i,"SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM (SELECT")
2974
- # The final part of the query is appended to include a WHERE...BETWEEN...AND condition,
2975
- # and retrieve only a LIMIT number of records starting from the OFFSET+1
2976
- sql << ") AS I) AS O WHERE sys_row_num BETWEEN ? AND ?"
2977
- options[:paramArray] = [offset+1, last_record]
2978
- end
2979
-
2980
3318
  # This method generates the default blob value specified for
2981
3319
  # DB2 Dataservers
2982
3320
  def set_binary_default(value)
3321
+ @adapter.puts_log "set_binary_default"
2983
3322
  "BLOB('#{value}')"
2984
3323
  end
2985
3324
 
2986
3325
  # This method generates the blob value specified for DB2 Dataservers
2987
3326
  def set_binary_value
3327
+ @adapter.puts_log "set_binary_value"
2988
3328
  "BLOB('?')"
2989
3329
  end
2990
3330
 
2991
3331
  # This method generates the default clob value specified for
2992
3332
  # DB2 Dataservers
2993
3333
  def set_text_default(value)
3334
+ @adapter.puts_log "set_text_default"
2994
3335
  "'#{value}'"
2995
3336
  end
2996
3337
 
2997
3338
  # For DB2 Dataservers , the arguments to the meta-data functions
2998
3339
  # need to be in upper-case
2999
3340
  def set_case(value)
3341
+ @adapter.puts_log "set_case"
3000
3342
  value.upcase
3001
3343
  end
3002
3344
  end # class IBM_DB2
@@ -3059,6 +3401,7 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
3059
3401
  if rfile
3060
3402
  RESERVED_WORDS = open(rfile.to_s) {|f| YAML.load(f) }
3061
3403
  def check_reserved_words(col_name)
3404
+ puts_log "164"
3062
3405
  if RESERVED_WORDS[col_name]
3063
3406
  '"' + RESERVED_WORDS[col_name] + '"'
3064
3407
  else
@@ -3140,28 +3483,6 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
3140
3483
  class IBM_DB2_ZOS_8 < IBM_DB2_ZOS
3141
3484
  include HostedDataServer
3142
3485
 
3143
- def get_limit_offset_clauses(limit, offset)
3144
- retHash = {"startSegment" => "", "endSegment" => ""}
3145
- if (!limit.nil?)
3146
- retHash["endSegment"] = " FETCH FIRST #{limit} ROWS ONLY"
3147
- end
3148
- return retHash
3149
- end
3150
-
3151
- def query_offset_limit(sql, offset, limit)
3152
- if (!limit.nil?)
3153
- sql << " FETCH FIRST #{limit} ROWS ONLY"
3154
- end
3155
- return sql
3156
- end
3157
-
3158
- def query_offset_limit!(sql, offset, limit, options)
3159
- if (!limit.nil?)
3160
- sql << " FETCH FIRST #{limit} ROWS ONLY"
3161
- end
3162
- options[:paramArray] = []
3163
- end
3164
-
3165
3486
  # This call is needed on DB2 z/OS v8 for the creation of tables
3166
3487
  # with LOBs. When issued, this call does the following:
3167
3488
  # DB2 creates LOB table spaces, auxiliary tables, and indexes on auxiliary
@@ -3310,56 +3631,6 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
3310
3631
  return "double precision"
3311
3632
  end
3312
3633
 
3313
- def get_limit_offset_clauses(limit, offset)
3314
- retHash = {"startSegment" => "", "endSegment" => ""}
3315
- if limit != 0
3316
- if !offset.nil?
3317
- # Modifying the SQL to utilize the skip and limit amounts
3318
- retHash["startSegment"] = " SELECT SKIP #{offset} LIMIT #{limit} "
3319
- else
3320
- # Modifying the SQL to retrieve only the first #{limit} rows
3321
- retHash["startSegment"] = " SELECT FIRST #{limit} "
3322
- end
3323
- else
3324
- retHash["startSegment"] = " SELECT * FROM (SELECT "
3325
- retHash["endSegment"] = " ) WHERE 0 = 1 "
3326
- end
3327
- end
3328
-
3329
- # Handling offset/limit as per Informix requirements
3330
- def query_offset_limit(sql, offset, limit)
3331
- if limit != 0
3332
- if !offset.nil?
3333
- # Modifying the SQL to utilize the skip and limit amounts
3334
- sql.gsub!(/SELECT/i,"SELECT SKIP #{offset} LIMIT #{limit}")
3335
- else
3336
- # Modifying the SQL to retrieve only the first #{limit} rows
3337
- sql = sql.gsub!("SELECT","SELECT FIRST #{limit}")
3338
- end
3339
- else
3340
- # Modifying the SQL to ensure that no rows will be returned
3341
- sql.gsub!(/SELECT/i,"SELECT * FROM (SELECT")
3342
- sql << ") WHERE 0 = 1"
3343
- end
3344
- end
3345
-
3346
- # Handling offset/limit as per Informix requirements
3347
- def query_offset_limit!(sql, offset, limit, options)
3348
- if limit != 0
3349
- if !offset.nil?
3350
- # Modifying the SQL to utilize the skip and limit amounts
3351
- sql.gsub!(/SELECT/i,"SELECT SKIP #{offset} LIMIT #{limit}")
3352
- else
3353
- # Modifying the SQL to retrieve only the first #{limit} rows
3354
- sql = sql.gsub!("SELECT","SELECT FIRST #{limit}")
3355
- end
3356
- else
3357
- # Modifying the SQL to ensure that no rows will be returned
3358
- sql.gsub!(/SELECT/i,"SELECT * FROM (SELECT")
3359
- sql << ") WHERE 0 = 1"
3360
- end
3361
- end
3362
-
3363
3634
  # Method that returns the last automatically generated ID
3364
3635
  # on the given +@connection+. This method is required by the +insert+
3365
3636
  # method. IDS returns the last generated serial value in the SQLCA unlike
@@ -3406,72 +3677,73 @@ end # module ActiveRecord
3406
3677
 
3407
3678
  module Arel
3408
3679
  #Check Arel version
3409
- begin
3410
- arelVersion = Arel::VERSION.to_i
3411
- rescue
3412
- arelVersion = 0
3413
- end
3414
- if(arelVersion >= 6)
3415
- module Collectors
3416
- class Bind
3417
- def changeFirstSegment(segment)
3418
- @parts[0] = segment
3419
- end
3680
+ begin
3681
+ arelVersion = Arel::VERSION.to_i
3682
+ rescue
3683
+ arelVersion = 0
3684
+ end
3685
+ if(arelVersion >= 6)
3686
+ module Collectors
3687
+ class Bind
3688
+ def changeFirstSegment(segment)
3689
+ @parts[0] = segment
3690
+ end
3420
3691
 
3421
- def changeEndSegment(segment)
3422
- len = @parts.length
3423
- @parts[len] = segment
3692
+ def changeEndSegment(segment)
3693
+ len = @parts.length
3694
+ @parts[len] = segment
3695
+ end
3424
3696
  end
3425
3697
  end
3426
3698
  end
3427
- end
3428
3699
 
3429
3700
  module Visitors
3430
3701
  class Visitor #opening and closing the class to ensure backward compatibility
3431
3702
  end
3432
3703
 
3433
- #Check Arel version
3434
- begin
3435
- arelVersion = Arel::VERSION.to_i
3436
- rescue
3437
- arelVersion = 0
3438
- end
3439
- if(arelVersion >= 6 && arelVersion <= 9)
3440
- class ToSql < Arel::Visitors::Reduce #opening and closing the class to ensure backward compatibility
3441
- # In case when using Rails-2.3.x there is no arel used due to which the constructor has to be defined explicitly
3442
- # to ensure the same code works on any version of Rails
3704
+ #Check Arel version
3705
+ begin
3706
+ arelVersion = Arel::VERSION.to_i
3707
+ rescue
3708
+ arelVersion = 0
3709
+ end
3710
+
3711
+ if(arelVersion >= 6 && arelVersion <= 9)
3712
+ class ToSql < Arel::Visitors::Reduce #opening and closing the class to ensure backward compatibility
3713
+ # In case when using Rails-2.3.x there is no arel used due to which the constructor has to be defined explicitly
3714
+ # to ensure the same code works on any version of Rails
3443
3715
 
3444
- #Check Arel version
3445
- begin
3446
- @arelVersion = Arel::VERSION.to_i
3447
- rescue
3448
- @arelVersion = 0
3449
- end
3716
+ #Check Arel version
3717
+ begin
3718
+ @arelVersion = Arel::VERSION.to_i
3719
+ rescue
3720
+ @arelVersion = 0
3721
+ end
3450
3722
 
3451
- if(@arelVersion >= 3)
3452
- def initialize connection
3453
- super()
3454
- @connection = connection
3455
- @schema_cache = connection.schema_cache if(connection.respond_to?(:schema_cache))
3456
- @quoted_tables = {}
3457
- @quoted_columns = {}
3458
- @last_column = nil
3723
+ if(@arelVersion >= 3)
3724
+ def initialize connection
3725
+ super()
3726
+ @connection = connection
3727
+ @schema_cache = connection.schema_cache if(connection.respond_to?(:schema_cache))
3728
+ @quoted_tables = {}
3729
+ @quoted_columns = {}
3730
+ @last_column = nil
3731
+ end
3459
3732
  end
3460
3733
  end
3461
- end
3462
- else
3463
- class ToSql < Arel::Visitors::Visitor #opening and closing the class to ensure backward compatibility
3464
- # In case when using Rails-2.3.x there is no arel used due to which the constructor has to be defined explicitly
3465
- # to ensure the same code works on any version of Rails
3734
+ else
3735
+ class ToSql < Arel::Visitors::Visitor #opening and closing the class to ensure backward compatibility
3736
+ # In case when using Rails-2.3.x there is no arel used due to which the constructor has to be defined explicitly
3737
+ # to ensure the same code works on any version of Rails
3466
3738
 
3467
3739
  #Check Arel version
3468
- begin
3469
- @arelVersion = Arel::VERSION.to_i
3740
+ begin
3741
+ @arelVersion = Arel::VERSION.to_i
3470
3742
  rescue
3471
3743
  @arelVersion = 0
3472
3744
  end
3473
- if(@arelVersion >= 3)
3474
- def initialize connection
3745
+ if(@arelVersion >= 3)
3746
+ def initialize connection
3475
3747
  super()
3476
3748
  @connection = connection
3477
3749
  @schema_cache = connection.schema_cache if(connection.respond_to?(:schema_cache))
@@ -3482,42 +3754,42 @@ else
3482
3754
 
3483
3755
  end
3484
3756
  end
3485
-
3486
- end
3487
-
3488
-
3489
- class IBM_DB < Arel::Visitors::ToSql
3490
- private
3757
+ end
3491
3758
 
3492
-
3493
- def visit_Arel_Nodes_Limit o,collector
3494
- collector << " FETCH FIRST "
3759
+ class IBM_DB < Arel::Visitors::ToSql
3760
+ private
3761
+ def visit_Arel_Nodes_Limit(o, collector)
3762
+ collector << " FETCH FIRST "
3495
3763
  visit o.expr, collector
3496
- collector << " ROWS ONLY "
3764
+ collector << " ROWS ONLY "
3497
3765
  end
3498
3766
 
3499
- def visit_Arel_Nodes_Offset o,collector
3500
- visit o.expr,collector
3767
+ def visit_Arel_Nodes_Offset(o, collector)
3768
+ collector << " OFFSET "
3769
+ visit o.expr, collector
3770
+ collector << " ROWS"
3501
3771
  end
3772
+
3502
3773
  def visit_Arel_Nodes_ValuesList(o, collector)
3503
- collector << "VALUES "
3504
- o.rows.each_with_index do |row, i|
3505
- collector << ", " unless i == 0
3506
- collector << "("
3507
- row.each_with_index do |value, k|
3508
- collector << ", " unless k == 0
3509
- case value
3510
- when Nodes::SqlLiteral, Nodes::BindParam
3511
- collector = visit(value, collector)
3512
- #collector << quote(value).to_s
3513
- else
3514
- collector << value.to_s
3515
- end
3774
+ collector << "VALUES "
3775
+ o.rows.each_with_index do |row, i|
3776
+ collector << ", " unless i == 0
3777
+ collector << "("
3778
+ row.each_with_index do |value, k|
3779
+ collector << ", " unless k == 0
3780
+ case value
3781
+ when Nodes::SqlLiteral, Nodes::BindParam, ActiveModel::Attribute
3782
+ collector = visit(value, collector)
3783
+ #collector << quote(value).to_s
3784
+ else
3785
+ collector << quote(value).to_s
3516
3786
  end
3517
- collector << ")"
3518
3787
  end
3519
- collector
3788
+ collector << ")"
3789
+ end
3790
+ collector
3520
3791
  end
3792
+
3521
3793
  def visit_Arel_Nodes_SelectStatement o, collector
3522
3794
  if o.with
3523
3795
  collector = visit o.with, collector
@@ -3533,61 +3805,27 @@ end
3533
3805
  len = o.orders.length - 1
3534
3806
  o.orders.each_with_index { |x, i|
3535
3807
  collector = visit(x, collector)
3536
- collector << "," unless len == i
3808
+ collector << ", " unless len == i
3537
3809
  }
3538
3810
  end
3539
3811
 
3540
-
3541
- # if o.limit
3542
- # limcoll = Arel::Collectors::SQLString.new
3543
- # visit(o.limit,limcoll)
3544
- # limit = limcoll.value.to_i
3545
- # else
3546
- # limit = nil
3547
- # end
3548
- #
3549
- # if o.offset
3550
- # offcoll = Arel::Collectors::SQLString.new
3551
- # visit(o.offset,offcoll)
3552
- # offset = offcoll.value.to_i
3553
- # else
3554
- # offset = nil
3555
- # end
3556
- #
3557
- # limOffClause = @connection.get_limit_offset_clauses(limit,offset)
3558
- #
3559
- # if( !limOffClause["startSegment"].empty? )
3560
- # #collector.changeFirstSegment(limOffClause["startSegment"])
3561
- # collector.value.prepend(limOffClause["startSegment"])
3562
- # end
3563
- #
3564
- # if( !limOffClause["endSegment"].empty? )
3565
- # #collector.changeEndSegment(limOffClause["endSegment"])
3566
- # collector << " "
3567
- # collector << limOffClause["endSegment"]
3568
- # end
3569
-
3570
- #Initialize a new Collector and set its value to the sql string built so far with any limit and ofset modifications
3571
- #collector.reset(sql)
3572
- if (o.limit && o.offset.nil?)
3573
- visit(o.limit, collector)
3574
- end
3575
- if (o.offset && o.limit.nil?)
3576
- collector.value.prepend(" SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM ( ")
3577
- collector << (" ) AS I) AS O WHERE sys_row_num > ")
3578
- visit(o.offset, collector)
3579
- end
3580
-
3581
- if (o.offset && o.limit)
3582
- collector.value.prepend(" SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM ( ")
3583
- collector << (" ) AS I) AS O WHERE sys_row_num > ")
3584
- visit(o.offset, collector)
3585
- visit(o.limit, collector)
3586
- end
3587
- collector = maybe_visit o.lock, collector
3588
- return collector
3812
+ if (o.offset && o.limit)
3813
+ visit_Arel_Nodes_Offset(o.offset, collector)
3814
+ visit_Arel_Nodes_Limit(o.limit, collector)
3815
+ elsif (o.offset && o.limit.nil?)
3816
+ collector << " OFFSET "
3817
+ visit o.offset.expr, collector
3818
+ collector << " ROWS "
3819
+ maybe_visit o.lock, collector
3820
+ else
3821
+ visit_Arel_Nodes_SelectOptions(o, collector)
3822
+ end
3823
+ end
3824
+
3825
+ # Locks are not supported in SQLite
3826
+ def visit_Arel_Nodes_Lock(o, collector)
3827
+ collector
3589
3828
  end
3590
-
3591
3829
  end
3592
3830
  end
3593
3831
  end