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