ibm_db 5.6.1-arm64-darwin-24

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 (753) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +299 -0
  3. data/LICENSE +55 -0
  4. data/MANIFEST +14 -0
  5. data/ParameterizedQueries README +39 -0
  6. data/README +210 -0
  7. data/ext/Makefile +270 -0
  8. data/ext/Makefile.nt32 +181 -0
  9. data/ext/Makefile.nt32.191 +212 -0
  10. data/ext/extconf.rb +320 -0
  11. data/ext/gil_release_version.h +3 -0
  12. data/ext/ibm_db.bundle +0 -0
  13. data/ext/ibm_db.c +11865 -0
  14. data/ext/ibm_db.o +0 -0
  15. data/ext/mkmf.log +98 -0
  16. data/ext/ruby_ibm_db.h +241 -0
  17. data/ext/ruby_ibm_db_cli.c +867 -0
  18. data/ext/ruby_ibm_db_cli.h +508 -0
  19. data/ext/ruby_ibm_db_cli.o +0 -0
  20. data/ext/unicode_support_version.h +3 -0
  21. data/init.rb +42 -0
  22. data/lib/IBM_DB.rb +27 -0
  23. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +4407 -0
  24. data/lib/active_record/connection_adapters/ibm_db_pstmt.rb +1965 -0
  25. data/lib/active_record/connection_adapters/ibmdb_adapter.rb +5 -0
  26. data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -0
  27. data/lib/ibm_db.bundle +0 -0
  28. data/test/active_record/connection_adapters/fake_adapter.rb +52 -0
  29. data/test/activejob/destroy_association_async_test.rb +305 -0
  30. data/test/activejob/destroy_async_job_not_present_test.rb +31 -0
  31. data/test/activejob/helper.rb +15 -0
  32. data/test/assets/example.log +1 -0
  33. data/test/assets/flowers.jpg +0 -0
  34. data/test/assets/schema_dump_5_1.yml +345 -0
  35. data/test/assets/test.txt +1 -0
  36. data/test/cases/adapter_prevent_writes_test.rb +334 -0
  37. data/test/cases/adapter_test.rb +565 -0
  38. data/test/cases/adapters/mysql2/active_schema_test.rb +203 -0
  39. data/test/cases/adapters/mysql2/auto_increment_test.rb +34 -0
  40. data/test/cases/adapters/mysql2/bind_parameter_test.rb +52 -0
  41. data/test/cases/adapters/mysql2/boolean_test.rb +102 -0
  42. data/test/cases/adapters/mysql2/case_sensitivity_test.rb +65 -0
  43. data/test/cases/adapters/mysql2/charset_collation_test.rb +57 -0
  44. data/test/cases/adapters/mysql2/connection_test.rb +208 -0
  45. data/test/cases/adapters/mysql2/count_deleted_rows_with_lock_test.rb +28 -0
  46. data/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb +49 -0
  47. data/test/cases/adapters/mysql2/enum_test.rb +47 -0
  48. data/test/cases/adapters/mysql2/explain_test.rb +23 -0
  49. data/test/cases/adapters/mysql2/json_test.rb +24 -0
  50. data/test/cases/adapters/mysql2/mysql2_adapter_prevent_writes_test.rb +208 -0
  51. data/test/cases/adapters/mysql2/mysql2_adapter_test.rb +238 -0
  52. data/test/cases/adapters/mysql2/nested_deadlock_test.rb +75 -0
  53. data/test/cases/adapters/mysql2/optimizer_hints_test.rb +69 -0
  54. data/test/cases/adapters/mysql2/reserved_word_test.rb +152 -0
  55. data/test/cases/adapters/mysql2/schema_migrations_test.rb +64 -0
  56. data/test/cases/adapters/mysql2/schema_test.rb +128 -0
  57. data/test/cases/adapters/mysql2/set_test.rb +32 -0
  58. data/test/cases/adapters/mysql2/sp_test.rb +38 -0
  59. data/test/cases/adapters/mysql2/sql_types_test.rb +16 -0
  60. data/test/cases/adapters/mysql2/table_options_test.rb +125 -0
  61. data/test/cases/adapters/mysql2/transaction_test.rb +151 -0
  62. data/test/cases/adapters/mysql2/unsigned_type_test.rb +68 -0
  63. data/test/cases/adapters/mysql2/virtual_column_test.rb +66 -0
  64. data/test/cases/adapters/postgresql/active_schema_test.rb +113 -0
  65. data/test/cases/adapters/postgresql/array_test.rb +394 -0
  66. data/test/cases/adapters/postgresql/bit_string_test.rb +84 -0
  67. data/test/cases/adapters/postgresql/bytea_test.rb +135 -0
  68. data/test/cases/adapters/postgresql/case_insensitive_test.rb +27 -0
  69. data/test/cases/adapters/postgresql/change_schema_test.rb +40 -0
  70. data/test/cases/adapters/postgresql/cidr_test.rb +27 -0
  71. data/test/cases/adapters/postgresql/citext_test.rb +78 -0
  72. data/test/cases/adapters/postgresql/collation_test.rb +55 -0
  73. data/test/cases/adapters/postgresql/composite_test.rb +134 -0
  74. data/test/cases/adapters/postgresql/connection_test.rb +245 -0
  75. data/test/cases/adapters/postgresql/create_unlogged_tables_test.rb +74 -0
  76. data/test/cases/adapters/postgresql/datatype_test.rb +89 -0
  77. data/test/cases/adapters/postgresql/date_test.rb +42 -0
  78. data/test/cases/adapters/postgresql/domain_test.rb +49 -0
  79. data/test/cases/adapters/postgresql/enum_test.rb +93 -0
  80. data/test/cases/adapters/postgresql/explain_test.rb +22 -0
  81. data/test/cases/adapters/postgresql/extension_migration_test.rb +64 -0
  82. data/test/cases/adapters/postgresql/foreign_table_test.rb +109 -0
  83. data/test/cases/adapters/postgresql/full_text_test.rb +46 -0
  84. data/test/cases/adapters/postgresql/geometric_test.rb +372 -0
  85. data/test/cases/adapters/postgresql/hstore_test.rb +390 -0
  86. data/test/cases/adapters/postgresql/infinity_test.rb +108 -0
  87. data/test/cases/adapters/postgresql/integer_test.rb +27 -0
  88. data/test/cases/adapters/postgresql/interval_test.rb +99 -0
  89. data/test/cases/adapters/postgresql/json_test.rb +52 -0
  90. data/test/cases/adapters/postgresql/ltree_test.rb +51 -0
  91. data/test/cases/adapters/postgresql/money_test.rb +127 -0
  92. data/test/cases/adapters/postgresql/network_test.rb +102 -0
  93. data/test/cases/adapters/postgresql/numbers_test.rb +51 -0
  94. data/test/cases/adapters/postgresql/optimizer_hints_test.rb +71 -0
  95. data/test/cases/adapters/postgresql/partitions_test.rb +22 -0
  96. data/test/cases/adapters/postgresql/postgresql_adapter_prevent_writes_test.rb +205 -0
  97. data/test/cases/adapters/postgresql/postgresql_adapter_test.rb +447 -0
  98. data/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb +27 -0
  99. data/test/cases/adapters/postgresql/prepared_statements_test.rb +22 -0
  100. data/test/cases/adapters/postgresql/quoting_test.rb +50 -0
  101. data/test/cases/adapters/postgresql/range_test.rb +457 -0
  102. data/test/cases/adapters/postgresql/referential_integrity_test.rb +112 -0
  103. data/test/cases/adapters/postgresql/rename_table_test.rb +35 -0
  104. data/test/cases/adapters/postgresql/schema_authorization_test.rb +110 -0
  105. data/test/cases/adapters/postgresql/schema_test.rb +713 -0
  106. data/test/cases/adapters/postgresql/serial_test.rb +156 -0
  107. data/test/cases/adapters/postgresql/statement_pool_test.rb +61 -0
  108. data/test/cases/adapters/postgresql/timestamp_test.rb +92 -0
  109. data/test/cases/adapters/postgresql/transaction_nested_test.rb +114 -0
  110. data/test/cases/adapters/postgresql/transaction_test.rb +189 -0
  111. data/test/cases/adapters/postgresql/type_lookup_test.rb +35 -0
  112. data/test/cases/adapters/postgresql/utils_test.rb +64 -0
  113. data/test/cases/adapters/postgresql/uuid_test.rb +411 -0
  114. data/test/cases/adapters/postgresql/xml_test.rb +50 -0
  115. data/test/cases/adapters/sqlite3/collation_test.rb +64 -0
  116. data/test/cases/adapters/sqlite3/copy_table_test.rb +101 -0
  117. data/test/cases/adapters/sqlite3/explain_test.rb +23 -0
  118. data/test/cases/adapters/sqlite3/json_test.rb +29 -0
  119. data/test/cases/adapters/sqlite3/quoting_test.rb +79 -0
  120. data/test/cases/adapters/sqlite3/sqlite3_adapter_prevent_writes_test.rb +186 -0
  121. data/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +628 -0
  122. data/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb +24 -0
  123. data/test/cases/adapters/sqlite3/statement_pool_test.rb +21 -0
  124. data/test/cases/adapters/sqlite3/transaction_test.rb +123 -0
  125. data/test/cases/aggregations_test.rb +170 -0
  126. data/test/cases/annotate_test.rb +46 -0
  127. data/test/cases/ar_schema_test.rb +213 -0
  128. data/test/cases/arel/attributes/attribute_test.rb +1145 -0
  129. data/test/cases/arel/attributes/math_test.rb +83 -0
  130. data/test/cases/arel/attributes_test.rb +27 -0
  131. data/test/cases/arel/collectors/bind_test.rb +40 -0
  132. data/test/cases/arel/collectors/composite_test.rb +47 -0
  133. data/test/cases/arel/collectors/sql_string_test.rb +41 -0
  134. data/test/cases/arel/collectors/substitute_bind_collector_test.rb +48 -0
  135. data/test/cases/arel/crud_test.rb +65 -0
  136. data/test/cases/arel/delete_manager_test.rb +53 -0
  137. data/test/cases/arel/factory_methods_test.rb +46 -0
  138. data/test/cases/arel/helper.rb +45 -0
  139. data/test/cases/arel/insert_manager_test.rb +241 -0
  140. data/test/cases/arel/nodes/and_test.rb +30 -0
  141. data/test/cases/arel/nodes/as_test.rb +36 -0
  142. data/test/cases/arel/nodes/ascending_test.rb +46 -0
  143. data/test/cases/arel/nodes/bin_test.rb +35 -0
  144. data/test/cases/arel/nodes/binary_test.rb +29 -0
  145. data/test/cases/arel/nodes/bind_param_test.rb +22 -0
  146. data/test/cases/arel/nodes/case_test.rb +96 -0
  147. data/test/cases/arel/nodes/casted_test.rb +18 -0
  148. data/test/cases/arel/nodes/comment_test.rb +22 -0
  149. data/test/cases/arel/nodes/count_test.rb +35 -0
  150. data/test/cases/arel/nodes/delete_statement_test.rb +36 -0
  151. data/test/cases/arel/nodes/descending_test.rb +46 -0
  152. data/test/cases/arel/nodes/distinct_test.rb +21 -0
  153. data/test/cases/arel/nodes/equality_test.rb +62 -0
  154. data/test/cases/arel/nodes/extract_test.rb +43 -0
  155. data/test/cases/arel/nodes/false_test.rb +21 -0
  156. data/test/cases/arel/nodes/grouping_test.rb +26 -0
  157. data/test/cases/arel/nodes/infix_operation_test.rb +42 -0
  158. data/test/cases/arel/nodes/insert_statement_test.rb +44 -0
  159. data/test/cases/arel/nodes/named_function_test.rb +48 -0
  160. data/test/cases/arel/nodes/node_test.rb +22 -0
  161. data/test/cases/arel/nodes/not_test.rb +31 -0
  162. data/test/cases/arel/nodes/or_test.rb +36 -0
  163. data/test/cases/arel/nodes/over_test.rb +69 -0
  164. data/test/cases/arel/nodes/select_core_test.rb +79 -0
  165. data/test/cases/arel/nodes/select_statement_test.rb +51 -0
  166. data/test/cases/arel/nodes/sql_literal_test.rb +75 -0
  167. data/test/cases/arel/nodes/sum_test.rb +35 -0
  168. data/test/cases/arel/nodes/table_alias_test.rb +29 -0
  169. data/test/cases/arel/nodes/true_test.rb +21 -0
  170. data/test/cases/arel/nodes/unary_operation_test.rb +41 -0
  171. data/test/cases/arel/nodes/update_statement_test.rb +60 -0
  172. data/test/cases/arel/nodes/window_test.rb +81 -0
  173. data/test/cases/arel/nodes_test.rb +34 -0
  174. data/test/cases/arel/select_manager_test.rb +1238 -0
  175. data/test/cases/arel/support/fake_record.rb +135 -0
  176. data/test/cases/arel/table_test.rb +216 -0
  177. data/test/cases/arel/update_manager_test.rb +126 -0
  178. data/test/cases/arel/visitors/dispatch_contamination_test.rb +78 -0
  179. data/test/cases/arel/visitors/dot_test.rb +90 -0
  180. data/test/cases/arel/visitors/mysql_test.rb +157 -0
  181. data/test/cases/arel/visitors/postgres_test.rb +366 -0
  182. data/test/cases/arel/visitors/sqlite_test.rb +75 -0
  183. data/test/cases/arel/visitors/to_sql_test.rb +750 -0
  184. data/test/cases/associations/association_scope_test.rb +16 -0
  185. data/test/cases/associations/belongs_to_associations_test.rb +1493 -0
  186. data/test/cases/associations/bidirectional_destroy_dependencies_test.rb +43 -0
  187. data/test/cases/associations/callbacks_test.rb +208 -0
  188. data/test/cases/associations/cascaded_eager_loading_test.rb +245 -0
  189. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +156 -0
  190. data/test/cases/associations/eager_load_nested_include_test.rb +127 -0
  191. data/test/cases/associations/eager_singularization_test.rb +148 -0
  192. data/test/cases/associations/eager_test.rb +1658 -0
  193. data/test/cases/associations/extension_test.rb +93 -0
  194. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +1025 -0
  195. data/test/cases/associations/has_many_associations_test.rb +3074 -0
  196. data/test/cases/associations/has_many_through_associations_test.rb +1580 -0
  197. data/test/cases/associations/has_one_associations_test.rb +872 -0
  198. data/test/cases/associations/has_one_through_associations_test.rb +429 -0
  199. data/test/cases/associations/inner_join_association_test.rb +215 -0
  200. data/test/cases/associations/inverse_associations_test.rb +941 -0
  201. data/test/cases/associations/join_model_test.rb +787 -0
  202. data/test/cases/associations/left_outer_join_association_test.rb +123 -0
  203. data/test/cases/associations/nested_through_associations_test.rb +636 -0
  204. data/test/cases/associations/required_test.rb +127 -0
  205. data/test/cases/associations_test.rb +516 -0
  206. data/test/cases/attribute_decorators_test.rb +126 -0
  207. data/test/cases/attribute_methods/read_test.rb +60 -0
  208. data/test/cases/attribute_methods_test.rb +1124 -0
  209. data/test/cases/attribute_set_test.rb +270 -0
  210. data/test/cases/attribute_test.rb +246 -0
  211. data/test/cases/attributes_test.rb +371 -0
  212. data/test/cases/autosave_association_test.rb +1953 -0
  213. data/test/cases/base_prevent_writes_test.rb +229 -0
  214. data/test/cases/base_test.rb +1770 -0
  215. data/test/cases/batches_test.rb +695 -0
  216. data/test/cases/binary_test.rb +39 -0
  217. data/test/cases/bind_parameter_test.rb +283 -0
  218. data/test/cases/boolean_test.rb +52 -0
  219. data/test/cases/cache_key_test.rb +131 -0
  220. data/test/cases/calculations_test.rb +1361 -0
  221. data/test/cases/callbacks_test.rb +503 -0
  222. data/test/cases/clone_test.rb +45 -0
  223. data/test/cases/coders/json_test.rb +17 -0
  224. data/test/cases/coders/yaml_column_test.rb +66 -0
  225. data/test/cases/collection_cache_key_test.rb +272 -0
  226. data/test/cases/column_alias_test.rb +19 -0
  227. data/test/cases/column_definition_test.rb +34 -0
  228. data/test/cases/comment_test.rb +204 -0
  229. data/test/cases/connection_adapters/adapter_leasing_test.rb +60 -0
  230. data/test/cases/connection_adapters/connection_handler_test.rb +467 -0
  231. data/test/cases/connection_adapters/connection_handlers_multi_db_test.rb +400 -0
  232. data/test/cases/connection_adapters/connection_handlers_multi_pool_config_test.rb +103 -0
  233. data/test/cases/connection_adapters/connection_handlers_sharding_db_test.rb +499 -0
  234. data/test/cases/connection_adapters/connection_specification_test.rb +12 -0
  235. data/test/cases/connection_adapters/connection_swapping_nested_test.rb +457 -0
  236. data/test/cases/connection_adapters/legacy_connection_handlers_multi_db_test.rb +486 -0
  237. data/test/cases/connection_adapters/legacy_connection_handlers_sharding_db_test.rb +586 -0
  238. data/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +436 -0
  239. data/test/cases/connection_adapters/mysql_type_lookup_test.rb +81 -0
  240. data/test/cases/connection_adapters/quoting_test.rb +13 -0
  241. data/test/cases/connection_adapters/schema_cache_test.rb +294 -0
  242. data/test/cases/connection_adapters/type_lookup_test.rb +119 -0
  243. data/test/cases/connection_management_test.rb +114 -0
  244. data/test/cases/connection_pool_test.rb +754 -0
  245. data/test/cases/connection_specification/resolver_test.rb +131 -0
  246. data/test/cases/core_test.rb +136 -0
  247. data/test/cases/counter_cache_test.rb +368 -0
  248. data/test/cases/custom_locking_test.rb +19 -0
  249. data/test/cases/database_configurations/hash_config_test.rb +74 -0
  250. data/test/cases/database_configurations/resolver_test.rb +150 -0
  251. data/test/cases/database_configurations_test.rb +145 -0
  252. data/test/cases/database_selector_test.rb +296 -0
  253. data/test/cases/database_statements_test.rb +36 -0
  254. data/test/cases/date_test.rb +36 -0
  255. data/test/cases/date_time_precision_test.rb +129 -0
  256. data/test/cases/date_time_test.rb +76 -0
  257. data/test/cases/defaults_test.rb +254 -0
  258. data/test/cases/delegated_type_test.rb +57 -0
  259. data/test/cases/dirty_test.rb +959 -0
  260. data/test/cases/disconnected_test.rb +30 -0
  261. data/test/cases/dup_test.rb +184 -0
  262. data/test/cases/enum_test.rb +823 -0
  263. data/test/cases/errors_test.rb +16 -0
  264. data/test/cases/explain_subscriber_test.rb +66 -0
  265. data/test/cases/explain_test.rb +79 -0
  266. data/test/cases/filter_attributes_test.rb +153 -0
  267. data/test/cases/finder_respond_to_test.rb +60 -0
  268. data/test/cases/finder_test.rb +1676 -0
  269. data/test/cases/fixture_set/file_test.rb +152 -0
  270. data/test/cases/fixtures_test.rb +1645 -0
  271. data/test/cases/forbidden_attributes_protection_test.rb +130 -0
  272. data/test/cases/habtm_destroy_order_test.rb +61 -0
  273. data/test/cases/helper.rb +233 -0
  274. data/test/cases/hot_compatibility_test.rb +143 -0
  275. data/test/cases/i18n_test.rb +46 -0
  276. data/test/cases/inheritance_test.rb +671 -0
  277. data/test/cases/insert_all_test.rb +489 -0
  278. data/test/cases/instrumentation_test.rb +101 -0
  279. data/test/cases/integration_test.rb +243 -0
  280. data/test/cases/invalid_connection_test.rb +26 -0
  281. data/test/cases/invertible_migration_test.rb +527 -0
  282. data/test/cases/json_attribute_test.rb +35 -0
  283. data/test/cases/json_serialization_test.rb +310 -0
  284. data/test/cases/json_shared_test_cases.rb +290 -0
  285. data/test/cases/locking_test.rb +787 -0
  286. data/test/cases/log_subscriber_test.rb +267 -0
  287. data/test/cases/marshal_serialization_test.rb +39 -0
  288. data/test/cases/migration/change_schema_test.rb +504 -0
  289. data/test/cases/migration/change_table_test.rb +364 -0
  290. data/test/cases/migration/check_constraint_test.rb +162 -0
  291. data/test/cases/migration/column_attributes_test.rb +186 -0
  292. data/test/cases/migration/column_positioning_test.rb +68 -0
  293. data/test/cases/migration/columns_test.rb +326 -0
  294. data/test/cases/migration/command_recorder_test.rb +437 -0
  295. data/test/cases/migration/compatibility_test.rb +673 -0
  296. data/test/cases/migration/create_join_table_test.rb +167 -0
  297. data/test/cases/migration/foreign_key_test.rb +581 -0
  298. data/test/cases/migration/helper.rb +40 -0
  299. data/test/cases/migration/index_test.rb +267 -0
  300. data/test/cases/migration/logger_test.rb +39 -0
  301. data/test/cases/migration/pending_migrations_test.rb +106 -0
  302. data/test/cases/migration/references_foreign_key_test.rb +235 -0
  303. data/test/cases/migration/references_index_test.rb +120 -0
  304. data/test/cases/migration/references_statements_test.rb +137 -0
  305. data/test/cases/migration/rename_table_test.rb +116 -0
  306. data/test/cases/migration_test.rb +1525 -0
  307. data/test/cases/migrator_test.rb +527 -0
  308. data/test/cases/mixin_test.rb +64 -0
  309. data/test/cases/modules_test.rb +174 -0
  310. data/test/cases/multi_db_migrator_test.rb +223 -0
  311. data/test/cases/multiparameter_attributes_test.rb +399 -0
  312. data/test/cases/multiple_db_test.rb +116 -0
  313. data/test/cases/nested_attributes_test.rb +1119 -0
  314. data/test/cases/nested_attributes_with_callbacks_test.rb +146 -0
  315. data/test/cases/null_relation_test.rb +84 -0
  316. data/test/cases/numeric_data_test.rb +93 -0
  317. data/test/cases/persistence_test.rb +1093 -0
  318. data/test/cases/pooled_connections_test.rb +73 -0
  319. data/test/cases/prepared_statement_status_test.rb +48 -0
  320. data/test/cases/primary_keys_test.rb +482 -0
  321. data/test/cases/query_cache_test.rb +915 -0
  322. data/test/cases/quoting_test.rb +303 -0
  323. data/test/cases/readonly_test.rb +120 -0
  324. data/test/cases/reaper_test.rb +199 -0
  325. data/test/cases/reflection_test.rb +520 -0
  326. data/test/cases/relation/delegation_test.rb +76 -0
  327. data/test/cases/relation/delete_all_test.rb +117 -0
  328. data/test/cases/relation/merging_test.rb +434 -0
  329. data/test/cases/relation/mutation_test.rb +145 -0
  330. data/test/cases/relation/or_test.rb +192 -0
  331. data/test/cases/relation/predicate_builder_test.rb +31 -0
  332. data/test/cases/relation/record_fetch_warning_test.rb +42 -0
  333. data/test/cases/relation/select_test.rb +67 -0
  334. data/test/cases/relation/update_all_test.rb +317 -0
  335. data/test/cases/relation/where_chain_test.rb +141 -0
  336. data/test/cases/relation/where_clause_test.rb +257 -0
  337. data/test/cases/relation/where_test.rb +429 -0
  338. data/test/cases/relation_test.rb +482 -0
  339. data/test/cases/relations_test.rb +2251 -0
  340. data/test/cases/reload_models_test.rb +26 -0
  341. data/test/cases/reserved_word_test.rb +141 -0
  342. data/test/cases/result_test.rb +141 -0
  343. data/test/cases/sanitize_test.rb +192 -0
  344. data/test/cases/schema_dumper_test.rb +550 -0
  345. data/test/cases/schema_loading_test.rb +53 -0
  346. data/test/cases/scoping/default_scoping_test.rb +569 -0
  347. data/test/cases/scoping/named_scoping_test.rb +649 -0
  348. data/test/cases/scoping/relation_scoping_test.rb +522 -0
  349. data/test/cases/secure_token_test.rb +47 -0
  350. data/test/cases/serialization_test.rb +106 -0
  351. data/test/cases/serialized_attribute_test.rb +455 -0
  352. data/test/cases/signed_id_test.rb +168 -0
  353. data/test/cases/statement_cache_test.rb +153 -0
  354. data/test/cases/statement_invalid_test.rb +42 -0
  355. data/test/cases/store_test.rb +320 -0
  356. data/test/cases/strict_loading_test.rb +473 -0
  357. data/test/cases/suppressor_test.rb +77 -0
  358. data/test/cases/tasks/database_tasks_test.rb +1526 -0
  359. data/test/cases/tasks/mysql_rake_test.rb +417 -0
  360. data/test/cases/tasks/postgresql_rake_test.rb +534 -0
  361. data/test/cases/tasks/sqlite_rake_test.rb +267 -0
  362. data/test/cases/test_case.rb +142 -0
  363. data/test/cases/test_databases_test.rb +79 -0
  364. data/test/cases/test_fixtures_test.rb +96 -0
  365. data/test/cases/time_precision_test.rb +125 -0
  366. data/test/cases/timestamp_test.rb +504 -0
  367. data/test/cases/touch_later_test.rb +123 -0
  368. data/test/cases/transaction_callbacks_test.rb +772 -0
  369. data/test/cases/transaction_isolation_test.rb +106 -0
  370. data/test/cases/transactions_test.rb +1285 -0
  371. data/test/cases/type/adapter_specific_registry_test.rb +145 -0
  372. data/test/cases/type/date_time_test.rb +16 -0
  373. data/test/cases/type/integer_test.rb +29 -0
  374. data/test/cases/type/string_test.rb +24 -0
  375. data/test/cases/type/time_test.rb +28 -0
  376. data/test/cases/type/type_map_test.rb +178 -0
  377. data/test/cases/type/unsigned_integer_test.rb +19 -0
  378. data/test/cases/type_test.rb +41 -0
  379. data/test/cases/types_test.rb +26 -0
  380. data/test/cases/unconnected_test.rb +46 -0
  381. data/test/cases/unsafe_raw_sql_test.rb +274 -0
  382. data/test/cases/validations/absence_validation_test.rb +75 -0
  383. data/test/cases/validations/association_validation_test.rb +99 -0
  384. data/test/cases/validations/i18n_generate_message_validation_test.rb +102 -0
  385. data/test/cases/validations/i18n_validation_test.rb +87 -0
  386. data/test/cases/validations/length_validation_test.rb +80 -0
  387. data/test/cases/validations/numericality_validation_test.rb +181 -0
  388. data/test/cases/validations/presence_validation_test.rb +105 -0
  389. data/test/cases/validations/uniqueness_validation_test.rb +618 -0
  390. data/test/cases/validations_repair_helper.rb +21 -0
  391. data/test/cases/validations_test.rb +229 -0
  392. data/test/cases/view_test.rb +222 -0
  393. data/test/cases/yaml_serialization_test.rb +166 -0
  394. data/test/config.example.yml +97 -0
  395. data/test/config.rb +7 -0
  396. data/test/config.yml +220 -0
  397. data/test/connections/native_ibm_db/connection.rb +44 -0
  398. data/test/fixtures/accounts.yml +29 -0
  399. data/test/fixtures/admin/accounts.yml +2 -0
  400. data/test/fixtures/admin/randomly_named_a9.yml +7 -0
  401. data/test/fixtures/admin/randomly_named_b0.yml +7 -0
  402. data/test/fixtures/admin/users.yml +10 -0
  403. data/test/fixtures/all/admin +1 -0
  404. data/test/fixtures/all/developers.yml +0 -0
  405. data/test/fixtures/all/namespaced/accounts.yml +2 -0
  406. data/test/fixtures/all/people.yml +0 -0
  407. data/test/fixtures/all/tasks.yml +0 -0
  408. data/test/fixtures/author_addresses.yml +11 -0
  409. data/test/fixtures/author_favorites.yml +4 -0
  410. data/test/fixtures/authors.yml +17 -0
  411. data/test/fixtures/bad_posts.yml +9 -0
  412. data/test/fixtures/binaries.yml +137 -0
  413. data/test/fixtures/books.yml +38 -0
  414. data/test/fixtures/bulbs.yml +5 -0
  415. data/test/fixtures/cars.yml +9 -0
  416. data/test/fixtures/categories/special_categories.yml +9 -0
  417. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  418. data/test/fixtures/categories.yml +19 -0
  419. data/test/fixtures/categories_ordered.yml +7 -0
  420. data/test/fixtures/categories_posts.yml +34 -0
  421. data/test/fixtures/categorizations.yml +23 -0
  422. data/test/fixtures/citations.yml +5 -0
  423. data/test/fixtures/clubs.yml +8 -0
  424. data/test/fixtures/collections.yml +3 -0
  425. data/test/fixtures/colleges.yml +3 -0
  426. data/test/fixtures/comments.yml +72 -0
  427. data/test/fixtures/companies.yml +72 -0
  428. data/test/fixtures/computers.yml +12 -0
  429. data/test/fixtures/content.yml +3 -0
  430. data/test/fixtures/content_positions.yml +3 -0
  431. data/test/fixtures/courses.yml +8 -0
  432. data/test/fixtures/customers.yml +35 -0
  433. data/test/fixtures/dashboards.yml +6 -0
  434. data/test/fixtures/dead_parrots.yml +5 -0
  435. data/test/fixtures/developers.yml +22 -0
  436. data/test/fixtures/developers_projects.yml +17 -0
  437. data/test/fixtures/dog_lovers.yml +7 -0
  438. data/test/fixtures/dogs.yml +4 -0
  439. data/test/fixtures/doubloons.yml +3 -0
  440. data/test/fixtures/edges.yml +5 -0
  441. data/test/fixtures/entrants.yml +14 -0
  442. data/test/fixtures/essays.yml +16 -0
  443. data/test/fixtures/faces.yml +11 -0
  444. data/test/fixtures/fk_test_has_fk.yml +3 -0
  445. data/test/fixtures/fk_test_has_pk.yml +2 -0
  446. data/test/fixtures/friendships.yml +4 -0
  447. data/test/fixtures/funny_jokes.yml +10 -0
  448. data/test/fixtures/humans.yml +5 -0
  449. data/test/fixtures/interests.yml +33 -0
  450. data/test/fixtures/items.yml +3 -0
  451. data/test/fixtures/jobs.yml +7 -0
  452. data/test/fixtures/legacy_things.yml +3 -0
  453. data/test/fixtures/live_parrots.yml +4 -0
  454. data/test/fixtures/mateys.yml +4 -0
  455. data/test/fixtures/member_details.yml +8 -0
  456. data/test/fixtures/member_types.yml +6 -0
  457. data/test/fixtures/members.yml +11 -0
  458. data/test/fixtures/memberships.yml +41 -0
  459. data/test/fixtures/men.yml +5 -0
  460. data/test/fixtures/minimalistics.yml +5 -0
  461. data/test/fixtures/minivans.yml +5 -0
  462. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  463. data/test/fixtures/mixins.yml +29 -0
  464. data/test/fixtures/movies.yml +7 -0
  465. data/test/fixtures/naked/yml/accounts.yml +1 -0
  466. data/test/fixtures/naked/yml/companies.yml +1 -0
  467. data/test/fixtures/naked/yml/courses.yml +1 -0
  468. data/test/fixtures/naked/yml/courses_with_invalid_key.yml +3 -0
  469. data/test/fixtures/naked/yml/parrots.yml +3 -0
  470. data/test/fixtures/naked/yml/trees.yml +3 -0
  471. data/test/fixtures/nodes.yml +29 -0
  472. data/test/fixtures/organizations.yml +5 -0
  473. data/test/fixtures/other_books.yml +26 -0
  474. data/test/fixtures/other_comments.yml +6 -0
  475. data/test/fixtures/other_dogs.yml +2 -0
  476. data/test/fixtures/other_posts.yml +8 -0
  477. data/test/fixtures/other_topics.yml +42 -0
  478. data/test/fixtures/owners.yml +9 -0
  479. data/test/fixtures/parrots.yml +33 -0
  480. data/test/fixtures/parrots_pirates.yml +7 -0
  481. data/test/fixtures/people.yml +24 -0
  482. data/test/fixtures/peoples_treasures.yml +3 -0
  483. data/test/fixtures/pets.yml +19 -0
  484. data/test/fixtures/pirates.yml +15 -0
  485. data/test/fixtures/posts.yml +88 -0
  486. data/test/fixtures/price_estimates.yml +16 -0
  487. data/test/fixtures/products.yml +4 -0
  488. data/test/fixtures/projects.yml +7 -0
  489. data/test/fixtures/randomly_named_a9.yml +7 -0
  490. data/test/fixtures/ratings.yml +14 -0
  491. data/test/fixtures/readers.yml +17 -0
  492. data/test/fixtures/references.yml +17 -0
  493. data/test/fixtures/reserved_words/distinct.yml +5 -0
  494. data/test/fixtures/reserved_words/distinct_select.yml +11 -0
  495. data/test/fixtures/reserved_words/group.yml +14 -0
  496. data/test/fixtures/reserved_words/select.yml +8 -0
  497. data/test/fixtures/reserved_words/values.yml +7 -0
  498. data/test/fixtures/ships.yml +6 -0
  499. data/test/fixtures/speedometers.yml +8 -0
  500. data/test/fixtures/sponsors.yml +15 -0
  501. data/test/fixtures/strict_zines.yml +2 -0
  502. data/test/fixtures/string_key_objects.yml +7 -0
  503. data/test/fixtures/subscribers.yml +11 -0
  504. data/test/fixtures/subscriptions.yml +12 -0
  505. data/test/fixtures/taggings.yml +78 -0
  506. data/test/fixtures/tags.yml +11 -0
  507. data/test/fixtures/tasks.yml +7 -0
  508. data/test/fixtures/teapots.yml +3 -0
  509. data/test/fixtures/to_be_linked/accounts.yml +2 -0
  510. data/test/fixtures/to_be_linked/users.yml +10 -0
  511. data/test/fixtures/topics.yml +49 -0
  512. data/test/fixtures/toys.yml +14 -0
  513. data/test/fixtures/traffic_lights.yml +10 -0
  514. data/test/fixtures/treasures.yml +10 -0
  515. data/test/fixtures/trees.yml +3 -0
  516. data/test/fixtures/uuid_children.yml +3 -0
  517. data/test/fixtures/uuid_parents.yml +2 -0
  518. data/test/fixtures/variants.yml +4 -0
  519. data/test/fixtures/vegetables.yml +20 -0
  520. data/test/fixtures/vertices.yml +4 -0
  521. data/test/fixtures/warehouse-things.yml +3 -0
  522. data/test/fixtures/warehouse_things.yml +3 -0
  523. data/test/fixtures/zines.yml +5 -0
  524. data/test/ibm_db_test.rb +25 -0
  525. data/test/migrations/10_urban/9_add_expressions.rb +13 -0
  526. data/test/migrations/decimal/1_give_me_big_numbers.rb +17 -0
  527. data/test/migrations/magic/1_currencies_have_symbols.rb +13 -0
  528. data/test/migrations/missing/1000_people_have_middle_names.rb +11 -0
  529. data/test/migrations/missing/1_people_have_last_names.rb +11 -0
  530. data/test/migrations/missing/3_we_need_reminders.rb +14 -0
  531. data/test/migrations/missing/4_innocent_jointable.rb +14 -0
  532. data/test/migrations/rename/1_we_need_things.rb +13 -0
  533. data/test/migrations/rename/2_rename_things.rb +11 -0
  534. data/test/migrations/to_copy/1_people_have_hobbies.rb +11 -0
  535. data/test/migrations/to_copy/2_people_have_descriptions.rb +11 -0
  536. data/test/migrations/to_copy2/1_create_articles.rb +9 -0
  537. data/test/migrations/to_copy2/2_create_comments.rb +9 -0
  538. data/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb +11 -0
  539. data/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb +11 -0
  540. data/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb +11 -0
  541. data/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb +9 -0
  542. data/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb +9 -0
  543. data/test/migrations/valid/1_valid_people_have_last_names.rb +11 -0
  544. data/test/migrations/valid/2_we_need_reminders.rb +14 -0
  545. data/test/migrations/valid/3_innocent_jointable.rb +14 -0
  546. data/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb +11 -0
  547. data/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb +14 -0
  548. data/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb +14 -0
  549. data/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb +11 -0
  550. data/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb +14 -0
  551. data/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb +14 -0
  552. data/test/migrations/version_check/20131219224947_migration_version_check.rb +10 -0
  553. data/test/models/account.rb +46 -0
  554. data/test/models/admin/account.rb +5 -0
  555. data/test/models/admin/randomly_named_c1.rb +9 -0
  556. data/test/models/admin/user.rb +48 -0
  557. data/test/models/admin.rb +7 -0
  558. data/test/models/aircraft.rb +7 -0
  559. data/test/models/arunit2_model.rb +5 -0
  560. data/test/models/author.rb +260 -0
  561. data/test/models/auto_id.rb +6 -0
  562. data/test/models/autoloadable/extra_firm.rb +4 -0
  563. data/test/models/binary.rb +4 -0
  564. data/test/models/binary_field.rb +6 -0
  565. data/test/models/bird.rb +24 -0
  566. data/test/models/book.rb +33 -0
  567. data/test/models/book_destroy_async.rb +24 -0
  568. data/test/models/boolean.rb +7 -0
  569. data/test/models/bulb.rb +61 -0
  570. data/test/models/cake_designer.rb +5 -0
  571. data/test/models/car.rb +36 -0
  572. data/test/models/carrier.rb +4 -0
  573. data/test/models/cart.rb +5 -0
  574. data/test/models/cat.rb +12 -0
  575. data/test/models/categorization.rb +21 -0
  576. data/test/models/category.rb +47 -0
  577. data/test/models/chef.rb +10 -0
  578. data/test/models/citation.rb +7 -0
  579. data/test/models/club.rb +28 -0
  580. data/test/models/college.rb +12 -0
  581. data/test/models/column.rb +5 -0
  582. data/test/models/column_name.rb +5 -0
  583. data/test/models/comment.rb +98 -0
  584. data/test/models/company.rb +226 -0
  585. data/test/models/company_in_module.rb +99 -0
  586. data/test/models/computer.rb +5 -0
  587. data/test/models/contact.rb +43 -0
  588. data/test/models/content.rb +42 -0
  589. data/test/models/contract.rb +38 -0
  590. data/test/models/country.rb +5 -0
  591. data/test/models/course.rb +8 -0
  592. data/test/models/customer.rb +85 -0
  593. data/test/models/customer_carrier.rb +16 -0
  594. data/test/models/dashboard.rb +5 -0
  595. data/test/models/default.rb +4 -0
  596. data/test/models/department.rb +6 -0
  597. data/test/models/destroy_async_parent.rb +15 -0
  598. data/test/models/destroy_async_parent_soft_delete.rb +20 -0
  599. data/test/models/developer.rb +341 -0
  600. data/test/models/dl_keyed_belongs_to.rb +13 -0
  601. data/test/models/dl_keyed_belongs_to_soft_delete.rb +19 -0
  602. data/test/models/dl_keyed_has_many.rb +5 -0
  603. data/test/models/dl_keyed_has_many_through.rb +5 -0
  604. data/test/models/dl_keyed_has_one.rb +5 -0
  605. data/test/models/dl_keyed_join.rb +10 -0
  606. data/test/models/dog.rb +7 -0
  607. data/test/models/dog_lover.rb +7 -0
  608. data/test/models/doubloon.rb +14 -0
  609. data/test/models/drink_designer.rb +20 -0
  610. data/test/models/edge.rb +7 -0
  611. data/test/models/electron.rb +7 -0
  612. data/test/models/engine.rb +5 -0
  613. data/test/models/entrant.rb +5 -0
  614. data/test/models/entry.rb +5 -0
  615. data/test/models/essay.rb +8 -0
  616. data/test/models/essay_destroy_async.rb +12 -0
  617. data/test/models/event.rb +5 -0
  618. data/test/models/eye.rb +39 -0
  619. data/test/models/face.rb +17 -0
  620. data/test/models/family.rb +6 -0
  621. data/test/models/family_tree.rb +6 -0
  622. data/test/models/friendship.rb +8 -0
  623. data/test/models/frog.rb +8 -0
  624. data/test/models/guid.rb +4 -0
  625. data/test/models/guitar.rb +6 -0
  626. data/test/models/hotel.rb +13 -0
  627. data/test/models/human.rb +39 -0
  628. data/test/models/image.rb +5 -0
  629. data/test/models/interest.rb +16 -0
  630. data/test/models/invoice.rb +6 -0
  631. data/test/models/item.rb +9 -0
  632. data/test/models/job.rb +9 -0
  633. data/test/models/joke.rb +9 -0
  634. data/test/models/keyboard.rb +5 -0
  635. data/test/models/legacy_thing.rb +5 -0
  636. data/test/models/lesson.rb +13 -0
  637. data/test/models/line_item.rb +5 -0
  638. data/test/models/liquid.rb +6 -0
  639. data/test/models/man.rb +11 -0
  640. data/test/models/matey.rb +6 -0
  641. data/test/models/measurement.rb +4 -0
  642. data/test/models/member.rb +45 -0
  643. data/test/models/member_detail.rb +11 -0
  644. data/test/models/member_type.rb +5 -0
  645. data/test/models/membership.rb +38 -0
  646. data/test/models/mentor.rb +5 -0
  647. data/test/models/message.rb +5 -0
  648. data/test/models/minimalistic.rb +4 -0
  649. data/test/models/minivan.rb +10 -0
  650. data/test/models/mixed_case_monkey.rb +5 -0
  651. data/test/models/mocktail_designer.rb +2 -0
  652. data/test/models/molecule.rb +8 -0
  653. data/test/models/mouse.rb +6 -0
  654. data/test/models/movie.rb +7 -0
  655. data/test/models/node.rb +7 -0
  656. data/test/models/non_primary_key.rb +4 -0
  657. data/test/models/notification.rb +5 -0
  658. data/test/models/numeric_data.rb +12 -0
  659. data/test/models/order.rb +6 -0
  660. data/test/models/organization.rb +16 -0
  661. data/test/models/other_dog.rb +7 -0
  662. data/test/models/owner.rb +39 -0
  663. data/test/models/parrot.rb +36 -0
  664. data/test/models/person.rb +147 -0
  665. data/test/models/personal_legacy_thing.rb +6 -0
  666. data/test/models/pet.rb +20 -0
  667. data/test/models/pet_treasure.rb +8 -0
  668. data/test/models/pirate.rb +116 -0
  669. data/test/models/possession.rb +5 -0
  670. data/test/models/post.rb +371 -0
  671. data/test/models/price_estimate.rb +14 -0
  672. data/test/models/professor.rb +7 -0
  673. data/test/models/project.rb +42 -0
  674. data/test/models/publisher/article.rb +6 -0
  675. data/test/models/publisher/magazine.rb +5 -0
  676. data/test/models/publisher.rb +4 -0
  677. data/test/models/randomly_named_c1.rb +5 -0
  678. data/test/models/rating.rb +8 -0
  679. data/test/models/reader.rb +25 -0
  680. data/test/models/recipe.rb +5 -0
  681. data/test/models/record.rb +4 -0
  682. data/test/models/reference.rb +25 -0
  683. data/test/models/reply.rb +79 -0
  684. data/test/models/room.rb +6 -0
  685. data/test/models/section.rb +6 -0
  686. data/test/models/seminar.rb +6 -0
  687. data/test/models/session.rb +6 -0
  688. data/test/models/ship.rb +42 -0
  689. data/test/models/ship_part.rb +10 -0
  690. data/test/models/shop.rb +19 -0
  691. data/test/models/shop_account.rb +8 -0
  692. data/test/models/speedometer.rb +8 -0
  693. data/test/models/sponsor.rb +10 -0
  694. data/test/models/squeak.rb +6 -0
  695. data/test/models/strict_zine.rb +7 -0
  696. data/test/models/string_key_object.rb +5 -0
  697. data/test/models/student.rb +6 -0
  698. data/test/models/subject.rb +16 -0
  699. data/test/models/subscriber.rb +10 -0
  700. data/test/models/subscription.rb +8 -0
  701. data/test/models/tag.rb +16 -0
  702. data/test/models/tagging.rb +20 -0
  703. data/test/models/task.rb +7 -0
  704. data/test/models/topic.rb +153 -0
  705. data/test/models/toy.rb +10 -0
  706. data/test/models/traffic_light.rb +6 -0
  707. data/test/models/treasure.rb +16 -0
  708. data/test/models/treaty.rb +5 -0
  709. data/test/models/tree.rb +5 -0
  710. data/test/models/tuning_peg.rb +6 -0
  711. data/test/models/tyre.rb +13 -0
  712. data/test/models/user.rb +22 -0
  713. data/test/models/uuid_child.rb +5 -0
  714. data/test/models/uuid_item.rb +8 -0
  715. data/test/models/uuid_parent.rb +5 -0
  716. data/test/models/vegetables.rb +33 -0
  717. data/test/models/vehicle.rb +7 -0
  718. data/test/models/vertex.rb +11 -0
  719. data/test/models/warehouse_thing.rb +7 -0
  720. data/test/models/wheel.rb +5 -0
  721. data/test/models/without_table.rb +5 -0
  722. data/test/models/zine.rb +5 -0
  723. data/test/schema/i5/ibm_db_specific_schema.rb +137 -0
  724. data/test/schema/ids/ibm_db_specific_schema.rb +140 -0
  725. data/test/schema/luw/ibm_db_specific_schema.rb +137 -0
  726. data/test/schema/mysql2_specific_schema.rb +82 -0
  727. data/test/schema/oracle_specific_schema.rb +38 -0
  728. data/test/schema/postgresql_specific_schema.rb +125 -0
  729. data/test/schema/schema.rb +1237 -0
  730. data/test/schema/schema.rb.original +1057 -0
  731. data/test/schema/sqlite_specific_schema.rb +11 -0
  732. data/test/schema/zOS/ibm_db_specific_schema.rb +208 -0
  733. data/test/support/config.rb +43 -0
  734. data/test/support/connection.rb +29 -0
  735. data/test/support/connection_helper.rb +16 -0
  736. data/test/support/ddl_helper.rb +10 -0
  737. data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_6_0_topic.dump +0 -0
  738. data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_6_0_topic_associations.dump +0 -0
  739. data/test/support/marshal_compatibility_fixtures/Mysql2/rails_6_0_topic.dump +0 -0
  740. data/test/support/marshal_compatibility_fixtures/Mysql2/rails_6_0_topic_associations.dump +0 -0
  741. data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_6_0_topic.dump +0 -0
  742. data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_6_0_topic_associations.dump +0 -0
  743. data/test/support/marshal_compatibility_fixtures/SQLite/rails_6_0_topic.dump +0 -0
  744. data/test/support/marshal_compatibility_fixtures/SQLite/rails_6_0_topic_associations.dump +0 -0
  745. data/test/support/marshal_compatibility_fixtures/legacy_6_0_record_mysql.dump +0 -0
  746. data/test/support/marshal_compatibility_fixtures/legacy_relation.dump +0 -0
  747. data/test/support/schema_dumping_helper.rb +22 -0
  748. data/test/support/stubs/strong_parameters.rb +40 -0
  749. data/test/support/yaml_compatibility_fixtures/rails_4_1.yml +22 -0
  750. data/test/support/yaml_compatibility_fixtures/rails_4_2_0.yml +182 -0
  751. data/test/support/yaml_compatibility_fixtures/rails_v1_mysql.yml +206 -0
  752. data/test/support/yaml_compatibility_fixtures/rails_v2.yml +55 -0
  753. metadata +876 -0
@@ -0,0 +1,4407 @@
1
+ # +----------------------------------------------------------------------+
2
+ # | Licensed Materials - Property of IBM |
3
+ # | |
4
+ # | (C) Copyright IBM Corporation 2006 - 2025 |
5
+ # +----------------------------------------------------------------------+
6
+ # | Authors: Antonio Cangiano <cangiano@ca.ibm.com> |
7
+ # | : Mario Ds Briggs <mario.briggs@in.ibm.com> |
8
+ # | : Praveen Devarao <praveendrl@in.ibm.com> |
9
+ # | : Arvind Gupta <arvindgu@in.ibm.com> |
10
+ # +----------------------------------------------------------------------+
11
+
12
+ require 'active_record/connection_adapters/abstract_adapter'
13
+ require 'arel/visitors/visitor'
14
+ require 'active_support/core_ext/string/strip'
15
+ require 'active_record/type'
16
+ require 'active_record/connection_adapters/sql_type_metadata'
17
+ require 'active_record/connection_adapters/statement_pool'
18
+ require 'active_record/connection_adapters'
19
+
20
+ # Ensure ActiveRecord and Rails Generators are loaded
21
+ require "active_record"
22
+ ActiveRecord::ConnectionAdapters.register(
23
+ "ibm_db",
24
+ "ActiveRecord::ConnectionAdapters::IBM_DBAdapter",
25
+ "active_record/connection_adapters/ibm_db_adapter"
26
+ )
27
+ require "rails/generators/database"
28
+
29
+ module Rails
30
+ module Generators
31
+ class Database
32
+ DATABASES << "ibm_db" unless DATABASES.include?("ibm_db")
33
+
34
+ class << self
35
+ alias_method :original_build, :build
36
+
37
+ def build(database_name)
38
+ return IBMDB.new if database_name == "ibm_db"
39
+ original_build(database_name)
40
+ end
41
+
42
+ alias_method :original_all, :all
43
+
44
+ def all
45
+ original_all + [IBMDB.new]
46
+ end
47
+ end
48
+ end
49
+
50
+ class IBMDB < Database
51
+ def name
52
+ "ibm_db"
53
+ end
54
+
55
+ def service
56
+ {
57
+ "image" => "ibm_db:latest",
58
+ "restart" => "unless-stopped",
59
+ "networks" => ["default"],
60
+ "volumes" => ["ibm-db-data:/var/lib/ibmdb"],
61
+ "environment" => {
62
+ "IBM_DB_ALLOW_EMPTY_PASSWORD" => "true",
63
+ }
64
+ }
65
+ end
66
+
67
+ def port
68
+ nil # Default DB2 port
69
+ end
70
+
71
+ def gem
72
+ ["ibm_db", [">= 5.5"]]
73
+ end
74
+
75
+ def base_package
76
+ nil
77
+ end
78
+
79
+ def build_package
80
+ nil
81
+ end
82
+
83
+ def feature_name
84
+ nil
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ module CallChain
91
+ def self.caller_method(depth = 1)
92
+ parse_caller(caller(depth + 1).first).last
93
+ end
94
+
95
+ # Copied from ActionMailer
96
+ def self.parse_caller(at)
97
+ return unless /^(.+?):(\d+)(?::in `(.*)')?/ =~ at
98
+
99
+ file = Regexp.last_match[1]
100
+ line = Regexp.last_match[2].to_i
101
+ method = Regexp.last_match[3]
102
+ [file, line, method]
103
+ end
104
+ end
105
+
106
+ module ActiveRecord
107
+ class SchemaMigration
108
+ class << self
109
+ def create_table
110
+ return if connection.table_exists?(table_name)
111
+
112
+ connection.create_table(table_name, id: false) do |t|
113
+ t.string :version, **connection.internal_string_options_for_primary_key
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ module Persistence
120
+ module ClassMethods
121
+ def _insert_record(connection, values, returning) # :nodoc:
122
+ primary_key = self.primary_key
123
+ primary_key_value = nil
124
+
125
+ if prefetch_primary_key? && primary_key
126
+ values[primary_key] ||= begin
127
+ primary_key_value = next_sequence_value
128
+ _default_attributes[primary_key].with_cast_value(primary_key_value)
129
+ end
130
+ end
131
+
132
+ im = Arel::InsertManager.new(arel_table)
133
+
134
+ with_connection do |c|
135
+ if values.empty?
136
+ im.insert(connection.empty_insert_statement_value(primary_key, arel_table[name].relation.name))
137
+ else
138
+ im.insert(values.transform_keys { |name| arel_table[name] })
139
+ end
140
+
141
+ connection.insert(
142
+ im, "#{self} Create", primary_key || false, primary_key_value,
143
+ returning: returning
144
+ )
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ module ConnectionAdapters
151
+ class SchemaDumper
152
+ private
153
+
154
+ def header(stream)
155
+ stream.puts <<~HEADER
156
+ ActiveRecord::Schema[#{ActiveRecord::Migration.current_version}].define(#{define_params}) do
157
+ HEADER
158
+ end
159
+
160
+ def default_primary_key?(column)
161
+ schema_type(column) == :integer
162
+ end
163
+
164
+ def explicit_primary_key_default?(column)
165
+ column.bigint? and column.name == 'id'
166
+ end
167
+
168
+ def unique_constraints_in_create(table, stream)
169
+ if (unique_constraints = @connection.unique_constraints(table)).any?
170
+ add_unique_constraint_statements = unique_constraints.map do |unique_constraint|
171
+ parts = [
172
+ "t.unique_constraint #{unique_constraint.column.inspect}"
173
+ ]
174
+
175
+ parts << "deferrable: #{unique_constraint.deferrable.inspect}" if unique_constraint.deferrable
176
+
177
+ if unique_constraint.export_name_on_schema_dump?
178
+ parts << "name: #{unique_constraint.name.inspect}"
179
+ end
180
+
181
+ " #{parts.join(', ')}"
182
+ end
183
+
184
+ stream.puts add_unique_constraint_statements.sort.join("\n")
185
+ end
186
+ end
187
+ end
188
+
189
+ class SchemaCreation
190
+ private
191
+
192
+ def visit_TableDefinition(o)
193
+ create_sql = +"CREATE#{table_modifier_in_create(o)} TABLE "
194
+ create_sql << 'IF NOT EXISTS ' if o.if_not_exists
195
+ create_sql << "#{quote_table_name(o.name)} "
196
+
197
+ statements = o.columns.map { |c| accept c }
198
+ statements << accept(o.primary_keys) if o.primary_keys
199
+
200
+ if supports_indexes_in_create?
201
+ statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
202
+ end
203
+
204
+ statements.concat(o.foreign_keys.map { |fk| accept fk }) if use_foreign_keys?
205
+
206
+ statements.concat(o.check_constraints.map { |chk| accept chk }) if supports_check_constraints?
207
+
208
+ @conn.puts_log "visit_TableDefinition #{@conn.servertype}"
209
+ if !@conn.servertype.instance_of? IBM_IDS
210
+ statements.concat(o.unique_constraints.map { |exc| accept exc }) if supports_unique_constraints?
211
+ end
212
+
213
+ create_sql << "(#{statements.join(', ')})" if statements.present?
214
+ add_table_options!(create_sql, o)
215
+ create_sql << " AS (#{to_sql(o.as)}) WITH DATA" if o.as
216
+ create_sql
217
+ end
218
+
219
+ def visit_ColumnDefinition(o)
220
+ if @conn.instance_of? IBM_DBAdapter
221
+ @conn.puts_log "visit_ColumnDefinition #{o.name} #{o} #{@conn} #{@conn.servertype}"
222
+ end
223
+ o.sql_type = type_to_sql(o.type, **o.options)
224
+ column_sql = +"#{quote_column_name(o.name)} #{o.sql_type}"
225
+ add_column_options!(column_sql, column_options(o))
226
+ column_sql
227
+ end
228
+
229
+ def add_column_options!(sql, options)
230
+ if options_include_default?(options)
231
+ sql << " DEFAULT #{quote_default_expression(options[:default],
232
+ options[:column])}"
233
+ end
234
+ sql << ' GENERATED BY DEFAULT AS IDENTITY (START WITH 1000)' if options[:auto_increment] == true
235
+ sql << ' PRIMARY KEY' if options[:primary_key] == true
236
+ # must explicitly check for :null to allow change_column to work on migrations
237
+ sql << ' NOT NULL' if options[:null] == false
238
+ sql
239
+ end
240
+
241
+ def visit_AlterTable(o)
242
+ sql = +"ALTER TABLE #{quote_table_name(o.name)} "
243
+ sql << o.adds.map { |col| accept col }.join(" ")
244
+ sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(" ")
245
+ sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(" ")
246
+ sql << o.check_constraint_adds.map { |con| visit_AddCheckConstraint con }.join(" ")
247
+ sql << o.check_constraint_drops.map { |con| visit_DropCheckConstraint con }.join(" ")
248
+ sql << o.constraint_validations.map { |fk| visit_ValidateConstraint fk }.join(" ")
249
+ sql << o.exclusion_constraint_adds.map { |con| visit_AddExclusionConstraint con }.join(" ")
250
+ sql << o.exclusion_constraint_drops.map { |con| visit_DropExclusionConstraint con }.join(" ")
251
+ sql << o.unique_constraint_adds.map { |con| visit_AddUniqueConstraint con }.join(" ")
252
+ sql << o.unique_constraint_drops.map { |con| visit_DropUniqueConstraint con }.join(" ")
253
+ end
254
+
255
+ def visit_ValidateConstraint(name)
256
+ "VALIDATE CONSTRAINT #{quote_column_name(name)}"
257
+ end
258
+
259
+ def visit_UniqueConstraintDefinition(o)
260
+ column_name = Array(o.column).map { |column| quote_column_name(column) }.join(", ")
261
+
262
+ sql = ["CONSTRAINT"]
263
+ sql << quote_column_name(o.name)
264
+ sql << "UNIQUE"
265
+
266
+ if o.using_index
267
+ sql << "USING INDEX #{quote_column_name(o.using_index)}"
268
+ else
269
+ sql << "(#{column_name})"
270
+ end
271
+
272
+ # if o.deferrable
273
+ # sql << "DEFERRABLE INITIALLY #{o.deferrable.to_s.upcase}"
274
+ # end
275
+
276
+ sql.join(" ")
277
+ end
278
+
279
+ def visit_AddExclusionConstraint(o)
280
+ "ADD #{accept(o)}"
281
+ end
282
+
283
+ def visit_DropExclusionConstraint(name)
284
+ "DROP CONSTRAINT #{quote_column_name(name)}"
285
+ end
286
+
287
+ def visit_AddUniqueConstraint(o)
288
+ "ADD #{accept(o)}"
289
+ end
290
+
291
+ def visit_DropUniqueConstraint(name)
292
+ "DROP CONSTRAINT #{quote_column_name(name)}"
293
+ end
294
+
295
+ end
296
+ end
297
+
298
+ class Base
299
+ # Method required to handle LOBs and XML fields.
300
+ # An after save callback checks if a marker has been inserted through
301
+ # the insert or update, and then proceeds to update that record with
302
+ # the actual large object through a prepared statement (param binding).
303
+ after_save :handle_lobs
304
+ def handle_lobs
305
+ # return unless self.class.with_connection.is_a?(ConnectionAdapters::IBM_DBAdapter)
306
+
307
+ self.class.with_connection do |conn|
308
+ if conn.is_a?(ConnectionAdapters::IBM_DBAdapter)
309
+ # Checks that the insert or update had at least a BLOB, CLOB or XML field
310
+ conn.sql.each do |clob_sql|
311
+ next unless clob_sql =~ /BLOB\('(.*)'\)/i ||
312
+ clob_sql =~ /@@@IBMTEXT@@@/i ||
313
+ clob_sql =~ /@@@IBMXML@@@/i ||
314
+ clob_sql =~ /@@@IBMBINARY@@@/i
315
+
316
+ update_query = "UPDATE #{self.class.table_name} SET ("
317
+ counter = 0
318
+ values = []
319
+ params = []
320
+ # Selects only binary, text and xml columns
321
+ self.class.columns.select { |col| col.sql_type.to_s =~ /blob|binary|clob|text|xml/i }.each do |col|
322
+ update_query << if counter.zero?
323
+ "#{col.name}".to_s
324
+ else
325
+ ",#{col.name}".to_s
326
+ end
327
+
328
+ # Add a '?' for the parameter or a NULL if the value is nil or empty
329
+ # (except for a CLOB field where '' can be a value)
330
+ if self[col.name].nil? ||
331
+ self[col.name] == {} ||
332
+ self[col.name] == [] ||
333
+ (self[col.name] == '' && !(col.sql_type.to_s =~ /text|clob/i))
334
+ params << 'NULL'
335
+ else
336
+ values << if col.cast_type.is_a?(::ActiveRecord::Type::Serialized)
337
+ YAML.dump(self[col.name])
338
+ else
339
+ self[col.name]
340
+ end
341
+ params << '?'
342
+ end
343
+ counter += 1
344
+ end
345
+
346
+ # no subsequent update is required if no relevant columns are found
347
+ next if counter.zero?
348
+
349
+ update_query << ') = '
350
+ # IBM_DB accepts 'SET (column) = NULL' but not (NULL),
351
+ # therefore the sql needs to be changed for a single NULL field.
352
+ update_query << if params.size == 1 && params[0] == 'NULL'
353
+ 'NULL'
354
+ else
355
+ '(' + params.join(',') + ')'
356
+ end
357
+
358
+ update_query << " WHERE #{self.class.primary_key} = ?"
359
+ values << self[self.class.primary_key.downcase]
360
+
361
+ begin
362
+ unless (stmt = IBM_DB.prepare(conn.connection, update_query))
363
+ error_msg = IBM_DB.getErrormsg(conn.connection, IBM_DB::DB_CONN)
364
+ if error_msg && !error_msg.empty?
365
+ raise "Statement prepare for updating LOB/XML column failed : #{error_msg}"
366
+ end
367
+ raise StandardError.new('An unexpected error occurred during update of LOB/XML column')
368
+ end
369
+
370
+ conn.log_query(update_query, 'update of LOB/XML field(s)in handle_lobs')
371
+
372
+ # rollback any failed LOB/XML field updates (and remove associated marker)
373
+ unless IBM_DB.execute(stmt, values)
374
+ error_msg = "Failed to insert/update LOB/XML field(s) due to: #{IBM_DB.getErrormsg(stmt,
375
+ IBM_DB::DB_STMT)}"
376
+ conn.execute('ROLLBACK')
377
+ raise error_msg
378
+ end
379
+ rescue StandardError => e
380
+ raise e
381
+ ensure
382
+ IBM_DB.free_stmt(stmt) if stmt
383
+ end
384
+ # if clob_sql
385
+ # connection.sql.each
386
+ end
387
+ conn.handle_lobs_triggered = true
388
+ end # if conn.is_a?
389
+ end # with_connection
390
+ # if connection.kind_of?
391
+ # handle_lobs
392
+ end
393
+
394
+ private :handle_lobs
395
+
396
+ # Establishes a connection to a specified database using the credentials provided
397
+ # with the +config+ argument. All the ActiveRecord objects will use this connection
398
+ def self.ibm_db_connection(config)
399
+ # Attempts to load the Ruby driver IBM databases
400
+ # while not already loaded or raises LoadError in case of failure.
401
+ begin
402
+ require 'ibm_db' unless defined? IBM_DB
403
+ rescue LoadError
404
+ raise LoadError, 'Failed to load IBM_DB Ruby driver.'
405
+ end
406
+
407
+ # Check if class TableDefinition responds to indexes method to determine if we are on AR 3 or AR 4.
408
+ # 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.
409
+ checkClass = ActiveRecord::ConnectionAdapters::TableDefinition.new(self, nil)
410
+ isAr3 = if checkClass.respond_to?(:indexes)
411
+ false
412
+ else
413
+ true
414
+ end
415
+ # Converts all +config+ keys to symbols
416
+ config = config.symbolize_keys
417
+
418
+ # Flag to decide if quoted literal replcement should take place. By default it is ON. Set it to OFF if using Pstmt
419
+ set_quoted_literal_replacement = IBM_DB::QUOTED_LITERAL_REPLACEMENT_ON
420
+
421
+ # Retrieves database user credentials from the +config+ hash
422
+ # or raises ArgumentError in case of failure.
423
+ if !config.has_key?(:username) || !config.has_key?(:password)
424
+ raise ArgumentError, "Missing argument(s): Username/Password for #{config[:database]} is not specified"
425
+ end
426
+
427
+ if config[:username].to_s.nil? || config[:password].to_s.nil?
428
+ raise ArgumentError, 'Username/Password cannot be nil'
429
+ end
430
+
431
+ username = config[:username].to_s
432
+ password = config[:password].to_s
433
+
434
+ # Retrieves the database alias (local catalog name) or remote name
435
+ # (for remote TCP/IP connections) from the +config+ hash
436
+ # or raises ArgumentError in case of failure.
437
+ raise ArgumentError, 'Missing argument: a database name needs to be specified.' unless config.has_key?(:database)
438
+
439
+ database = config[:database].to_s
440
+
441
+ # Providing default schema (username) when not specified
442
+ config[:schema] = config.has_key?(:schema) ? config[:schema].to_s : config[:username].to_s
443
+
444
+ if config.has_key?(:parameterized) && config[:parameterized] == true
445
+ set_quoted_literal_replacement = IBM_DB::QUOTED_LITERAL_REPLACEMENT_OFF
446
+ end
447
+
448
+ # Extract connection options from the database configuration
449
+ # (in support to formatting, audit and billing purposes):
450
+ # Retrieve database objects fields in lowercase
451
+ conn_options = { IBM_DB::ATTR_CASE => IBM_DB::CASE_LOWER }
452
+ config.each do |key, value|
453
+ next if value.nil?
454
+
455
+ case key
456
+ when :app_user # Set connection's user info
457
+ conn_options[IBM_DB::SQL_ATTR_INFO_USERID] = value
458
+ when :account # Set connection's account info
459
+ conn_options[IBM_DB::SQL_ATTR_INFO_ACCTSTR] = value
460
+ when :application # Set connection's application info
461
+ conn_options[IBM_DB::SQL_ATTR_INFO_APPLNAME] = value
462
+ when :workstation # Set connection's workstation info
463
+ conn_options[IBM_DB::SQL_ATTR_INFO_WRKSTNNAME] = value
464
+ end
465
+ end
466
+
467
+ begin
468
+ # Checks if a host name or address has been specified. If so, this implies a TCP/IP connection
469
+ # Returns IBM_DB.Connection object upon succesful DB connection to the database
470
+ # If otherwise the connection fails, +false+ is returned
471
+ if config.has_key?(:host)
472
+ # Retrieves the host address/name
473
+ host = config[:host]
474
+ # A net address connection requires a port. If no port has been specified, 50000 is used by default
475
+ port = config[:port] || 50000
476
+ # Connects to the database specified using the hostname, port, authentication type, username and password info
477
+ # Starting with DB2 9.1FP5 secure connections using SSL are supported.
478
+ # On the client side using CLI this is supported from CLI version V95FP2 and onwards.
479
+ # This feature is set by specifying SECURITY=SSL in the connection string.
480
+ # Below connection string is constructed and SECURITY parameter is appended if the user has specified the :security option
481
+ conn_string = "DRIVER={IBM DB2 ODBC DRIVER};\
482
+ DATABASE=#{database};\
483
+ HOSTNAME=#{host};\
484
+ PORT=#{port};\
485
+ PROTOCOL=TCPIP;\
486
+ UID=#{username};\
487
+ PWD=#{password};"
488
+ conn_string << "SECURITY=#{config[:security]};" if config.has_key?(:security)
489
+ conn_string << "AUTHENTICATION=#{config[:authentication]};" if config.has_key?(:authentication)
490
+ conn_string << "CONNECTTIMEOUT=#{config[:timeout]};" if config.has_key?(:timeout)
491
+ connection = IBM_DB.connect(conn_string, '', '', conn_options, set_quoted_literal_replacement)
492
+ else
493
+ # No host implies a local catalog-based connection: +database+ represents catalog alias
494
+ connection = IBM_DB.connect(database, username, password, conn_options, set_quoted_literal_replacement)
495
+ end
496
+ return connection, isAr3, config, conn_options
497
+ rescue StandardError => e
498
+ raise "Failed to connect to [#{database}] due to: #{e}"
499
+ end
500
+ # Verifies that the connection was successful
501
+ raise "An unexpected error occured during connect attempt to [#{database}]" unless connection
502
+
503
+ # method self.ibm_db_connection
504
+ end
505
+
506
+ def self.ibmdb_connection(config)
507
+ # Method to support alising of adapter name as ibmdb [without underscore]
508
+ ibm_db_connection(config)
509
+ end
510
+ # class Base
511
+ end
512
+
513
+ module ConnectionAdapters
514
+ module Quoting
515
+ def lookup_cast_type_from_column(column) # :nodoc:
516
+ lookup_cast_type(column.sql_type_metadata.sql_type)
517
+ end
518
+
519
+ module ClassMethods
520
+ def quote_table_name(name)
521
+ if name.start_with? '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
522
+ name = "\"#{name}\""
523
+ else
524
+ name = name.to_s
525
+ end
526
+ name
527
+ # @servertype.check_reserved_words(name).gsub('"', '').gsub("'",'')
528
+ end
529
+
530
+ def quote_column_name(name)
531
+ name = name.to_s
532
+ name.gsub('"', '').gsub("'", '')
533
+ end
534
+ end
535
+ end
536
+
537
+ module ColumnDumper
538
+ def prepare_column_options(column)
539
+ puts_log 'prepare_column_options'
540
+ spec = {}
541
+
542
+ if limit = schema_limit(column)
543
+ spec[:limit] = limit
544
+ end
545
+
546
+ if precision = schema_precision(column)
547
+ spec[:precision] = precision
548
+ end
549
+
550
+ if scale = schema_scale(column)
551
+ spec[:scale] = scale
552
+ end
553
+
554
+ default = schema_default(column) if column.has_default?
555
+ spec[:default] = default unless default.nil?
556
+ spec[:null] = 'false' unless column.null
557
+
558
+ if collation = schema_collation(column)
559
+ spec[:collation] = collation
560
+ end
561
+
562
+ spec[:comment] = column.comment.inspect if column.comment.present?
563
+
564
+ spec
565
+ end
566
+
567
+ def schema_limit(column)
568
+ puts_log 'schema_limit'
569
+ limit = column.limit unless column.bigint?
570
+ limit.inspect if limit && limit != native_database_types[column.type.to_sym][:limit]
571
+ end
572
+ # end of module ColumnDumper
573
+ end
574
+
575
+ module SchemaStatements
576
+ def internal_string_options_for_primary_key # :nodoc:
577
+ { primary_key: true, null: false }
578
+ end
579
+
580
+ def valid_primary_key_options # :nodoc:
581
+ [:limit, :default, :precision, :auto_increment]
582
+ end
583
+
584
+ def valid_column_definition_options # :nodoc:
585
+ ColumnDefinition::OPTION_NAMES + [:auto_increment]
586
+ end
587
+
588
+ def drop_table(table_name, options = {})
589
+ if options[:if_exists]
590
+ execute("DROP TABLE IF EXISTS #{quote_table_name(table_name)}")
591
+ else
592
+ execute("DROP TABLE #{quote_table_name(table_name)}", options)
593
+ end
594
+ end
595
+
596
+ def create_table_definition(*args, **options)
597
+ puts_log 'create_table_definition SchemaStatements'
598
+ TableDefinition.new(self, *args, **options)
599
+ end
600
+
601
+ def unique_constraint_name(table_name, **options)
602
+ options.fetch(:name) do
603
+ column_or_index = Array(options[:column] || options[:using_index]).map(&:to_s)
604
+ identifier = "#{table_name}_#{column_or_index * '_and_'}_unique"
605
+ hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
606
+
607
+ "uniq_rails_#{hashed_identifier}"
608
+ end
609
+ end
610
+ # end of Module SchemaStatements
611
+ end
612
+
613
+ class IBM_DBColumn < ConnectionAdapters::Column # :nodoc:
614
+ def initialize(*)
615
+ puts_log '15'
616
+ super
617
+ end
618
+
619
+ # Used to convert from BLOBs to Strings
620
+ def self.binary_to_string(value)
621
+ # Returns a string removing the eventual BLOB scalar function
622
+ value.to_s.gsub(/"SYSIBM"."BLOB"\('(.*)'\)/i, '\1')
623
+ end
624
+ # class IBM_DBColumn
625
+ end
626
+
627
+ # The IBM_DB Adapter requires the native Ruby driver (ibm_db)
628
+ # for IBM data servers (ibm_db.so).
629
+ # +config+ the hash passed as an initializer argument content:
630
+ # == mandatory parameters
631
+ # adapter: 'ibm_db' // IBM_DB Adapter name
632
+ # username: 'db2user' // data server (database) user
633
+ # password: 'secret' // data server (database) password
634
+ # database: 'ARUNIT' // remote database name (or catalog entry alias)
635
+ # == optional (highly recommended for data server auditing and monitoring purposes)
636
+ # schema: 'rails123' // name space qualifier
637
+ # account: 'tester' // OS account (client workstation)
638
+ # app_user: 'test11' // authenticated application user
639
+ # application: 'rtests' // application name
640
+ # workstation: 'plato' // client workstation name
641
+ # == remote TCP/IP connection (required when no local database catalog entry available)
642
+ # host: 'socrates' // fully qualified hostname or IP address
643
+ # port: '50000' // data server TCP/IP port number
644
+ # security: 'SSL' // optional parameter enabling SSL encryption -
645
+ # // - Available only from CLI version V95fp2 and above
646
+ # authentication: 'SERVER' // AUTHENTICATION type which the client uses -
647
+ # // - to connect to the database server. By default value is SERVER
648
+ # timeout: 10 // Specifies the time in seconds (0 - 32767) to wait for a reply from server -
649
+ # //- when trying to establish a connection before generating a timeout
650
+ # == Parameterized Queries Support
651
+ # parameterized: false // Specifies if the prepared statement support of
652
+ # //- the IBM_DB Adapter is to be turned on or off
653
+ #
654
+ # When schema is not specified, the username value is used instead.
655
+ # The default setting of parameterized is false.
656
+ #
657
+ class IBM_DBAdapter < AbstractAdapter
658
+ attr_reader :connection, :servertype, :schema, :app_user, :account, :application, :workstation, :pstmt_support_on,
659
+ :set_quoted_literal_replacement
660
+ attr_accessor :sql, :handle_lobs_triggered, :sql_parameter_values
661
+
662
+ # Name of the adapter
663
+ def adapter_name
664
+ 'IBM_DB'
665
+ end
666
+
667
+ include Savepoints
668
+
669
+ def create_savepoint(name = current_savepoint_name)
670
+ puts_log 'create_savepoint'
671
+ # Turns off auto-committing
672
+ auto_commit_off
673
+ # Create savepoint
674
+ internal_execute("SAVEPOINT #{name} ON ROLLBACK RETAIN CURSORS", 'TRANSACTION')
675
+ end
676
+
677
+ class Column < ActiveRecord::ConnectionAdapters::Column
678
+ attr_reader :rowid
679
+
680
+ def initialize(*, auto_increment: nil, rowid: false, generated_type: nil, **)
681
+ super
682
+ @auto_increment = auto_increment
683
+ @rowid = rowid
684
+ @generated_type = generated_type
685
+ end
686
+
687
+ def self.binary_to_string(value)
688
+ # Returns a string removing the eventual BLOB scalar function
689
+ value.to_s.gsub(/"SYSIBM"."BLOB"\('(.*)'\)/i, '\1')
690
+ end
691
+
692
+ # whether the column is auto-populated by the database using a sequence
693
+ def auto_increment?
694
+ @auto_increment
695
+ end
696
+
697
+ def auto_incremented_by_db?
698
+ auto_increment? || rowid
699
+ end
700
+ alias_method :auto_incremented_by_db?, :auto_increment?
701
+ end
702
+
703
+ class AlterTable < ActiveRecord::ConnectionAdapters::AlterTable
704
+ attr_reader :constraint_validations, :exclusion_constraint_adds, :exclusion_constraint_drops, :unique_constraint_adds, :unique_constraint_drops
705
+ def initialize(td)
706
+ super
707
+ @constraint_validations = []
708
+ @exclusion_constraint_adds = []
709
+ @exclusion_constraint_drops = []
710
+ @unique_constraint_adds = []
711
+ @unique_constraint_drops = []
712
+ end
713
+
714
+ def add_unique_constraint(column_name, options)
715
+ @unique_constraint_adds << @td.new_unique_constraint_definition(column_name, options)
716
+ end
717
+
718
+ def drop_unique_constraint(unique_constraint_name)
719
+ @unique_constraint_drops << unique_constraint_name
720
+ end
721
+ end
722
+
723
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
724
+ include ColumnMethods
725
+
726
+ attr_reader :exclusion_constraints, :unique_constraints
727
+
728
+ def initialize(*, **)
729
+ super
730
+ @exclusion_constraints = []
731
+ @unique_constraints = []
732
+ end
733
+
734
+ def exclusion_constraint(expression, **options)
735
+ exclusion_constraints << new_exclusion_constraint_definition(expression, options)
736
+ end
737
+
738
+ def unique_constraint(column_name, **options)
739
+ @conn.puts_log "TD unique_constraint column_name = #{column_name}, options = #{options}"
740
+ unique_constraints << new_unique_constraint_definition(column_name, options)
741
+ @conn.puts_log "unique_constraints = #{unique_constraints}"
742
+ end
743
+
744
+ def new_unique_constraint_definition(column_name, options) # :nodoc:
745
+ @conn.puts_log "TD new_unique_constraint_definition column_name = #{column_name}, options = #{options}"
746
+ @conn.puts_log caller
747
+ options = @conn.unique_constraint_options(name, column_name, options)
748
+ UniqueConstraintDefinition.new(name, column_name, options)
749
+ end
750
+
751
+ def references(*args, **options)
752
+ super(*args, type: :integer, **options)
753
+ end
754
+ alias :belongs_to :references
755
+ end # end of class TableDefinition
756
+
757
+ UniqueConstraintDefinition = Struct.new(:table_name, :column, :options) do
758
+ def name
759
+ options[:name]
760
+ end
761
+
762
+ def deferrable
763
+ options[:deferrable]
764
+ end
765
+
766
+ def using_index
767
+ options[:using_index]
768
+ end
769
+
770
+ def export_name_on_schema_dump?
771
+ !ActiveRecord::SchemaDumper.unique_ignore_pattern.match?(name) if name
772
+ end
773
+
774
+ def defined_for?(name: nil, column: nil, **options)
775
+ (name.nil? || self.name == name.to_s) &&
776
+ (column.nil? || Array(self.column) == Array(column).map(&:to_s)) &&
777
+ options.all? { |k, v| self.options[k].to_s == v.to_s }
778
+ end
779
+ end
780
+
781
+ class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
782
+ private
783
+
784
+ def dealloc(stmt)
785
+ # stmt.close unless stmt.closed?
786
+ end
787
+ end
788
+
789
+ def build_statement_pool
790
+ StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
791
+ end
792
+
793
+ def initialize(args)
794
+ # Caching database connection configuration (+connect+ or +reconnect+ support)\
795
+ connection, ar3, config, conn_options = ActiveRecord::Base.ibm_db_connection(args)
796
+ @config = config
797
+ @connection = connection
798
+ @isAr3 = ar3
799
+ @conn_options = conn_options
800
+ @database = config[:database]
801
+ @username = config[:username]
802
+ @password = config[:password]
803
+ @debug = config[:debug]
804
+ if config.has_key?(:host)
805
+ @host = config[:host]
806
+ @port = config[:port] || 50000 # default port
807
+ end
808
+ @schema = if config.has_key?(:schema)
809
+ config[:schema]
810
+ else
811
+ config[:username]
812
+ end
813
+ @security = config[:security] || nil
814
+ @authentication = config[:authentication] || nil
815
+ @timeout = config[:timeout] || 0 # default timeout value is 0
816
+
817
+ @app_user = @account = @application = @workstation = nil
818
+ # Caching database connection options (auditing and billing support)
819
+ @app_user = conn_options[:app_user] if conn_options.has_key?(:app_user)
820
+ @account = conn_options[:account] if conn_options.has_key?(:account)
821
+ @application = conn_options[:application] if conn_options.has_key?(:application)
822
+ @workstation = conn_options[:workstation] if conn_options.has_key?(:workstation)
823
+
824
+ @sql = []
825
+ @sql_parameter_values = [] # Used only if pstmt support is turned on
826
+
827
+ @handle_lobs_triggered = false
828
+
829
+ # Calls the parent class +ConnectionAdapters+' initializer
830
+ super(@config)
831
+
832
+ if @connection
833
+ server_info = IBM_DB.server_info(@connection)
834
+ if server_info
835
+ case server_info.DBMS_NAME
836
+ when %r{DB2/}i # DB2 for Linux, Unix and Windows (LUW)
837
+ @servertype = case server_info.DBMS_VER
838
+ when /09.07/i # DB2 Version 9.7 (Cobra)
839
+ IBM_DB2_LUW_COBRA.new(self, @isAr3)
840
+ when /10./i # DB2 version 10.1 and above
841
+ IBM_DB2_LUW_COBRA.new(self, @isAr3)
842
+ else # DB2 Version 9.5 or below
843
+ IBM_DB2_LUW.new(self, @isAr3)
844
+ end
845
+ when /DB2/i # DB2 for zOS
846
+ case server_info.DBMS_VER
847
+ when /09/ # DB2 for zOS version 9 and version 10
848
+ @servertype = IBM_DB2_ZOS.new(self, @isAr3)
849
+ when /10/
850
+ @servertype = IBM_DB2_ZOS.new(self, @isAr3)
851
+ when /11/
852
+ @servertype = IBM_DB2_ZOS.new(self, @isAr3)
853
+ when /12/
854
+ @servertype = IBM_DB2_ZOS.new(self, @isAr3)
855
+ when /08/ # DB2 for zOS version 8
856
+ @servertype = IBM_DB2_ZOS_8.new(self, @isAr3)
857
+ else # DB2 for zOS version 7
858
+ raise 'Only DB2 z/OS version 8 and above are currently supported'
859
+ end
860
+ when /AS/i # DB2 for i5 (iSeries)
861
+ @servertype = IBM_DB2_I5.new(self, @isAr3)
862
+ when /IDS/i # Informix Dynamic Server
863
+ @servertype = IBM_IDS.new(self, @isAr3)
864
+ else
865
+ log('server_info',
866
+ 'Forcing servertype to LUW: DBMS name could not be retrieved. Check if your client version is of the right level')
867
+ warn 'Forcing servertype to LUW: DBMS name could not be retrieved. Check if your client version is of the right level'
868
+ @servertype = IBM_DB2_LUW.new(self, @isAr3)
869
+ end
870
+ @database_version = server_info.DBMS_VER
871
+ else
872
+ error_msg = IBM_DB.getErrormsg(@connection, IBM_DB::DB_CONN)
873
+ IBM_DB.close(@connection)
874
+ raise "Cannot retrieve server information: #{error_msg}"
875
+ end
876
+ end
877
+
878
+ # Executes the +set schema+ statement using the schema identifier provided
879
+ @servertype.set_schema(@schema) if @schema && @schema != @username
880
+
881
+ # Check for the start value for id (primary key column). By default it is 1
882
+ @start_id = if config.has_key?(:start_id)
883
+ config[:start_id]
884
+ else
885
+ 1
886
+ end
887
+
888
+ # Check Arel version
889
+ begin
890
+ @arelVersion = Arel::VERSION.to_i
891
+ rescue StandardError
892
+ @arelVersion = 0
893
+ end
894
+
895
+ @visitor = Arel::Visitors::IBM_DB.new self if @arelVersion >= 3
896
+
897
+ if config.has_key?(:parameterized) && config[:parameterized] == true
898
+ @pstmt_support_on = true
899
+ @prepared_statements = true
900
+ @set_quoted_literal_replacement = IBM_DB::QUOTED_LITERAL_REPLACEMENT_OFF
901
+ else
902
+ @pstmt_support_on = false
903
+ @prepared_statements = false
904
+ @set_quoted_literal_replacement = IBM_DB::QUOTED_LITERAL_REPLACEMENT_ON
905
+ end
906
+ end
907
+
908
+ def get_database_version
909
+ @database_version
910
+ end
911
+
912
+ def prepared_statements?
913
+ puts_log 'prepared_statements?'
914
+ prepare = @prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
915
+ puts_log "prepare = #{prepare}"
916
+ prepare
917
+ end
918
+ alias prepared_statements prepared_statements?
919
+
920
+ def bind_params_length
921
+ 999
922
+ end
923
+
924
+ # Optional connection attribute: database name space qualifier
925
+ def schema=(name)
926
+ puts_log 'schema='
927
+ return if name == @schema
928
+
929
+ @schema = name
930
+ @servertype.set_schema(@schema)
931
+ end
932
+
933
+ # Optional connection attribute: authenticated application user
934
+ def app_user=(name)
935
+ puts_log 'app_user='
936
+ return if name == @app_user
937
+
938
+ option = { IBM_DB::SQL_ATTR_INFO_USERID => "#{name}" }
939
+ return unless IBM_DB.set_option(@connection, option, 1)
940
+
941
+ @app_user = IBM_DB.get_option(@connection, IBM_DB::SQL_ATTR_INFO_USERID, 1)
942
+ end
943
+
944
+ # Optional connection attribute: OS account (client workstation)
945
+ def account=(name)
946
+ puts_log 'account='
947
+ return if name == @account
948
+
949
+ option = { IBM_DB::SQL_ATTR_INFO_ACCTSTR => "#{name}" }
950
+ return unless IBM_DB.set_option(@connection, option, 1)
951
+
952
+ @account = IBM_DB.get_option(@connection, IBM_DB::SQL_ATTR_INFO_ACCTSTR, 1)
953
+ end
954
+
955
+ # Optional connection attribute: application name
956
+ def application=(name)
957
+ puts_log 'application='
958
+ return if name == @application
959
+
960
+ option = { IBM_DB::SQL_ATTR_INFO_APPLNAME => "#{name}" }
961
+ return unless IBM_DB.set_option(@connection, option, 1)
962
+
963
+ @application = IBM_DB.get_option(@connection, IBM_DB::SQL_ATTR_INFO_APPLNAME, 1)
964
+ end
965
+
966
+ # Optional connection attribute: client workstation name
967
+ def workstation=(name)
968
+ puts_log 'workstation='
969
+ return if name == @workstation
970
+
971
+ option = { IBM_DB::SQL_ATTR_INFO_WRKSTNNAME => "#{name}" }
972
+ return unless IBM_DB.set_option(@connection, option, 1)
973
+
974
+ @workstation = IBM_DB.get_option(@connection, IBM_DB::SQL_ATTR_INFO_WRKSTNNAME, 1)
975
+ end
976
+
977
+ def self.visitor_for(pool)
978
+ puts_log 'visitor_for'
979
+ Arel::Visitors::IBM_DB.new(pool)
980
+ end
981
+
982
+ # Check Arel version
983
+ begin
984
+ @arelVersion = Arel::VERSION.to_i
985
+ rescue StandardError
986
+ @arelVersion = 0
987
+ end
988
+ if @arelVersion < 6
989
+ def to_sql(arel, binds = [])
990
+ if arel.respond_to?(:ast)
991
+ visitor.accept(arel.ast) do
992
+ quote(*binds.shift.reverse)
993
+ end
994
+ else
995
+ arel
996
+ end
997
+ end
998
+ end
999
+
1000
+ def supports_common_table_expressions?
1001
+ true
1002
+ end
1003
+
1004
+ # Does this adapter support creating unique constraints?
1005
+ def supports_unique_constraints?
1006
+ true
1007
+ end
1008
+
1009
+ #IBM Db2 does not natively support skipping rows on insert when there's a duplicate key
1010
+ def supports_insert_on_duplicate_skip?
1011
+ false
1012
+ end
1013
+
1014
+ def supports_insert_on_duplicate_update?
1015
+ false
1016
+ end
1017
+
1018
+ # This adapter supports migrations.
1019
+ # Current limitations:
1020
+ # +rename_column+ is not currently supported by the IBM data servers
1021
+ # +remove_column+ is not currently supported by the DB2 for zOS data server
1022
+ # Tables containing columns of XML data type do not support +remove_column+
1023
+ def supports_migrations?
1024
+ puts_log 'supports_migrations?'
1025
+ true
1026
+ end
1027
+
1028
+ def use_foreign_keys?
1029
+ puts_log 'use_foreign_keys?'
1030
+ true
1031
+ end
1032
+
1033
+ def supports_datetime_with_precision?
1034
+ puts_log 'supports_datetime_with_precision?'
1035
+ true
1036
+ end
1037
+
1038
+ # This Adapter supports DDL transactions.
1039
+ # This means CREATE TABLE and other DDL statements can be carried out as a transaction.
1040
+ # That is the statements executed can be ROLLED BACK in case of any error during the process.
1041
+ def supports_ddl_transactions?
1042
+ puts_log 'supports_ddl_transactions?'
1043
+ true
1044
+ end
1045
+
1046
+ def supports_explain?
1047
+ puts_log 'supports_explain?'
1048
+ true
1049
+ end
1050
+
1051
+ def supports_lazy_transactions?
1052
+ puts_log 'supports_lazy_transactions?'
1053
+ true
1054
+ end
1055
+
1056
+ def supports_comments?
1057
+ true
1058
+ end
1059
+
1060
+ def supports_views?
1061
+ true
1062
+ end
1063
+
1064
+ def log_query(sql, name) # :nodoc:
1065
+ puts_log 'log_query'
1066
+ # Used by handle_lobs
1067
+ log(sql, name) {}
1068
+ end
1069
+
1070
+ def supports_partitioned_indexes?
1071
+ true
1072
+ end
1073
+
1074
+ def supports_foreign_keys?
1075
+ true
1076
+ end
1077
+
1078
+ def puts_log(val)
1079
+ begin
1080
+ # puts val
1081
+ rescue StandardError
1082
+ end
1083
+ return unless @debug == true
1084
+
1085
+ log(" IBM_DB = #{val}", 'TRANSACTION') {}
1086
+ end
1087
+
1088
+ #==============================================
1089
+ # CONNECTION MANAGEMENT
1090
+ #==============================================
1091
+
1092
+ # Tests the connection status
1093
+ def active?
1094
+ isActive = false
1095
+ puts_log "active? #{caller} #{Thread.current}"
1096
+ @lock.synchronize do
1097
+ puts_log "active? #{@connection}, #{caller}, #{Thread.current}"
1098
+ isActive = IBM_DB.active @connection
1099
+ puts_log "active? isActive = #{isActive}"
1100
+ end
1101
+ isActive
1102
+ rescue StandardError => e
1103
+ puts_log "active? check failure #{e.message}, #{caller}, #{Thread.current}"
1104
+ false
1105
+ end
1106
+
1107
+ # Private method used by +reconnect!+.
1108
+ # It connects to the database with the initially provided credentials
1109
+ def connect
1110
+ puts_log "connect = #{@connection}, #{caller}, #{Thread.current}"
1111
+ # If the type of connection is net based
1112
+ raise ArgumentError, 'Username/Password cannot be nil' if @username.nil? || @password.nil?
1113
+
1114
+ begin
1115
+ puts_log "Begin connection #{Thread.current}"
1116
+ if @host
1117
+ @conn_string = "DRIVER={IBM DB2 ODBC DRIVER};\
1118
+ DATABASE=#{@database};\
1119
+ HOSTNAME=#{@host};\
1120
+ PORT=#{@port};\
1121
+ PROTOCOL=TCPIP;\
1122
+ UID=#{@username};\
1123
+ PWD=#{@password};"
1124
+ @conn_string << "SECURITY=#{@security};" if @security
1125
+ @conn_string << "AUTHENTICATION=#{@authentication};" if @authentication
1126
+ @conn_string << "CONNECTTIMEOUT=#{@timeout};"
1127
+ # Connects and assigns the resulting IBM_DB.Connection to the +@connection+ instance variable
1128
+ @connection = IBM_DB.connect(@conn_string, '', '', @conn_options, @set_quoted_literal_replacement)
1129
+ puts_log "Connection Established A = #{@connection}"
1130
+ else
1131
+ # Connects to the database using the local alias (@database)
1132
+ # and assigns the connection object (IBM_DB.Connection) to @connection
1133
+ @connection = IBM_DB.connect(@database, @username, @password, @conn_options,
1134
+ @set_quoted_literal_replacement)
1135
+ puts_log "Connection Established B = #{@connection}"
1136
+ end
1137
+ @raw_connection = @connection
1138
+ rescue StandardError => e
1139
+ warn "Connection to database #{@database} failed: #{e}"
1140
+ puts_log "Connection to database #{@database} failed: #{e}"
1141
+ @connection = false
1142
+ end
1143
+ # Sets the schema if different from default (username)
1144
+ return unless @schema && @schema != @username
1145
+
1146
+ puts_log "Connection Established = #{@connection}"
1147
+ @servertype.set_schema(@schema)
1148
+ end
1149
+ private :connect
1150
+
1151
+ def reset!
1152
+ puts_log "reset! #{caller} #{Thread.current}"
1153
+ @lock.synchronize do
1154
+ return connect! unless @connection
1155
+
1156
+ rollback_db_transaction
1157
+
1158
+ super
1159
+ end
1160
+ end
1161
+
1162
+ # Closes the current connection and opens a new one
1163
+ def reconnect
1164
+ puts_log "reconnect #{caller} #{Thread.current}"
1165
+ #disconnect!
1166
+ @lock.synchronize do
1167
+ puts_log "Before reconnection = #{@connection}, #{Thread.current}"
1168
+ connect unless @connection
1169
+ end
1170
+ end
1171
+
1172
+ # def reconnect!(restore_transactions: false)
1173
+ # super
1174
+ # end
1175
+
1176
+ # Closes the current connection
1177
+ def disconnect!
1178
+ # Attempts to close the connection. The methods will return:
1179
+ # * true if succesfull
1180
+ # * false if the connection is already closed
1181
+ # * nil if an error is raised
1182
+ @lock.synchronize do
1183
+ puts_log "disconnect! #{caller}, #{Thread.current}"
1184
+ if @connection.nil? || @connection == false
1185
+ puts_log "disconnect! return #{caller}, #{Thread.current}"
1186
+ return nil
1187
+ end
1188
+
1189
+ begin
1190
+ super
1191
+ IBM_DB.close(@connection)
1192
+ puts_log "Connection closed #{Thread.current}"
1193
+ @connection = nil
1194
+ @raw_connection = nil
1195
+ rescue StandardError => e
1196
+ puts_log "Connection close failure #{e.message}, #{Thread.current}"
1197
+ end
1198
+ #reset_transaction
1199
+ end
1200
+ end
1201
+
1202
+ # Check the connection back in to the connection pool
1203
+ def close
1204
+ pool.checkin self
1205
+ disconnect!
1206
+ end
1207
+
1208
+ def connected?
1209
+ puts_log "connected? #{@connection}"
1210
+ !(@connection.nil?)
1211
+ end
1212
+
1213
+ #==============================================
1214
+ # DATABASE STATEMENTS
1215
+ #==============================================
1216
+
1217
+ def create_table(name, id: :primary_key, primary_key: nil, force: nil, **options)
1218
+ puts_log "create_table name=#{name}, id=#{id}, primary_key=#{primary_key}, force=#{force}"
1219
+ puts_log "create_table Options 1 = #{options}"
1220
+ puts_log "primary_key_prefix_type = #{ActiveRecord::Base.primary_key_prefix_type}"
1221
+ puts_log caller
1222
+ @servertype.setup_for_lob_table
1223
+ # Table definition is complete only when a unique index is created on the primarykey column for DB2 V8 on zOS
1224
+
1225
+ # create index on id column if options[:id] is nil or id ==true
1226
+ # else check if options[:primary_key]is not nil then create an unique index on that column
1227
+ if !id.nil? || !primary_key.nil?
1228
+ if !id.nil? && id == true
1229
+ @servertype.create_index_after_table(name, 'id')
1230
+ elsif !primary_key.nil?
1231
+ @servertype.create_index_after_table(name, primary_key.to_s)
1232
+ end
1233
+ else
1234
+ @servertype.create_index_after_table(name, 'id')
1235
+ end
1236
+
1237
+ # Just incase if id holds any other data type other than primary_key we override it,
1238
+ # otherwise it misses "GENERATED BY DEFAULT AS IDENTITY (START WITH 1000)"
1239
+ if !id.nil? && id != false && primary_key.nil? && ActiveRecord::Base.primary_key_prefix_type.nil?
1240
+ primary_key = :id
1241
+ options[:auto_increment] = true if options[:auto_increment].nil? and %i[integer bigint].include?(id)
1242
+ end
1243
+
1244
+ puts_log "create_table Options 2 = #{options}"
1245
+ super(name, id: id, primary_key: primary_key, force: force, **options)
1246
+ end
1247
+
1248
+ # Calls the servertype select method to fetch the data
1249
+ def fetch_data(stmt)
1250
+ puts_log 'fetch_data'
1251
+ return unless stmt
1252
+
1253
+ begin
1254
+ @servertype.select(stmt)
1255
+ rescue StandardError => e # Handle driver fetch errors
1256
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT)
1257
+ raise StatementInvalid, "Failed to retrieve data: #{error_msg}" if error_msg && !error_msg.empty?
1258
+
1259
+ error_msg += ": #{e.message}" unless e.message.empty?
1260
+ #raise error_msg
1261
+ ensure
1262
+ # Ensures to free the resources associated with the statement
1263
+ if stmt
1264
+ puts_log "Free Statement #{stmt}"
1265
+ IBM_DB.free_stmt(stmt)
1266
+ end
1267
+ end
1268
+ end
1269
+
1270
+ def select(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false)
1271
+ puts_log "select sql = #{sql}"
1272
+ puts_log "binds = #{binds}"
1273
+ puts_log "prepare = #{prepare}"
1274
+
1275
+ # Replaces {"= NULL" with " IS NULL"} OR {"IN (NULL)" with " IS NULL"
1276
+ begin
1277
+ sql.gsub(/(=\s*NULL|IN\s*\(NULL\))/i, ' IS NULL')
1278
+ rescue StandardError
1279
+ # ...
1280
+ end
1281
+
1282
+ if async && async_enabled?
1283
+ if current_transaction.joinable?
1284
+ raise AsynchronousQueryInsideTransactionError, 'Asynchronous queries are not allowed inside transactions'
1285
+ end
1286
+
1287
+ future_result = async.new(
1288
+ pool,
1289
+ sql,
1290
+ name,
1291
+ binds,
1292
+ prepare: prepare
1293
+ )
1294
+ if supports_concurrent_connections? && current_transaction.closed?
1295
+ future_result.schedule!(ActiveRecord::Base.asynchronous_queries_session)
1296
+ else
1297
+ future_result.execute!(self)
1298
+ end
1299
+ return future_result
1300
+ end
1301
+
1302
+ results = []
1303
+ cols = []
1304
+
1305
+ stmt = if binds.nil? || binds.empty?
1306
+ internal_execute(sql, name, allow_retry: allow_retry)
1307
+ else
1308
+ exec_query_ret_stmt(sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry)
1309
+ end
1310
+
1311
+ if stmt
1312
+ cols = IBM_DB.resultCols(stmt)
1313
+ results = fetch_data(stmt)
1314
+ end
1315
+
1316
+ puts_log "select cols = #{cols}, results = #{results}"
1317
+
1318
+ if @isAr3
1319
+ results
1320
+ else
1321
+ results = ActiveRecord::Result.new(cols, results)
1322
+ if async
1323
+ results = ActiveRecord::FutureResult::Complete.new(results)
1324
+ end
1325
+ end
1326
+
1327
+ puts_log "select final results = #{results} #{caller}"
1328
+ results
1329
+ end
1330
+
1331
+ def translate_exception(exception, message:, sql:, binds:)
1332
+ puts_log "translate_exception - exception = #{exception}, message = #{message}"
1333
+ puts_log "translate_exception #{caller}"
1334
+ 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/
1335
+ error_msg2 = /SQL0204N .* is an undefined name/
1336
+ error_msg3 = /SQL0413N Overflow occurred during numeric data type conversion/
1337
+ error_msg4 = /SQL0407N Assignment of a NULL value to a NOT NULL column .* is not allowed/
1338
+ 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/
1339
+ error_msg6 = /SQL0532N A parent row cannot be deleted because the relationship .* restricts the deletion/
1340
+ error_msg7 = /SQL0433N Value .* is too long/
1341
+ error_msg8 = /CLI0109E String data right truncation/
1342
+ if !error_msg1.match(message).nil?
1343
+ puts_log 'RecordNotUnique exception'
1344
+ RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
1345
+ elsif !error_msg2.match(message).nil?
1346
+ puts_log 'ArgumentError exception'
1347
+ ArgumentError.new(message)
1348
+ elsif !error_msg3.match(message).nil?
1349
+ puts_log 'RangeError exception'
1350
+ RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
1351
+ elsif !error_msg4.match(message).nil?
1352
+ puts_log 'NotNullViolation exception'
1353
+ NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
1354
+ elsif !error_msg5.match(message).nil? or !error_msg6.match(message).nil?
1355
+ puts_log 'InvalidForeignKey exception'
1356
+ InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
1357
+ elsif !error_msg7.match(message).nil? or !error_msg8.match(message).nil?
1358
+ puts_log 'ValueTooLong exception'
1359
+ ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
1360
+ elsif exception.message.match?(/called on a closed database/i)
1361
+ puts_log 'ConnectionNotEstablished exception'
1362
+ ConnectionNotEstablished.new(exception, connection_pool: @pool)
1363
+ elsif message.strip.start_with?("FrozenError") or
1364
+ message.strip.start_with?("ActiveRecord::Encryption::Errors::Encoding:") or
1365
+ message.strip.start_with?("ActiveRecord::Encryption::Errors::Encryption") or
1366
+ message.strip.start_with?("ActiveRecord::ConnectionFailed")
1367
+ exception
1368
+ else
1369
+ super(message, message: exception, sql: sql, binds: binds)
1370
+ end
1371
+ end
1372
+
1373
+ def build_truncate_statement(table_name)
1374
+ puts_log 'build_truncate_statement'
1375
+ "DELETE FROM #{quote_table_name(table_name)}"
1376
+ end
1377
+
1378
+ def build_fixture_sql(fixtures, table_name)
1379
+ columns = schema_cache.columns_hash(table_name).reject { |_, column| supports_virtual_columns? && column.virtual? }
1380
+ puts_log "build_fixture_sql - Table = #{table_name}"
1381
+ puts_log "build_fixture_sql - Fixtures = #{fixtures}"
1382
+ puts_log "build_fixture_sql - Columns = #{columns}"
1383
+
1384
+ values_list = fixtures.map do |fixture|
1385
+ fixture = fixture.stringify_keys
1386
+ fixture = fixture.transform_keys(&:downcase)
1387
+
1388
+ unknown_columns = fixture.keys - columns.keys
1389
+ if unknown_columns.any?
1390
+ raise Fixture::FixtureError, %(table "#{table_name}" has no columns named #{unknown_columns.map(&:inspect).join(', ')}.)
1391
+ end
1392
+
1393
+ columns.map do |name, column|
1394
+ if fixture.key?(name)
1395
+ type = lookup_cast_type_from_column(column)
1396
+ with_yaml_fallback(type.serialize(fixture[name]))
1397
+ else
1398
+ default_insert_value(column)
1399
+ end
1400
+ end
1401
+ end
1402
+
1403
+ table = Arel::Table.new(table_name)
1404
+ manager = Arel::InsertManager.new(table)
1405
+
1406
+ if values_list.size == 1
1407
+ values = values_list.shift
1408
+ new_values = []
1409
+ columns.each_key.with_index { |column, i|
1410
+ unless values[i].equal?(DEFAULT_INSERT_VALUE)
1411
+ new_values << values[i]
1412
+ manager.columns << table[column]
1413
+ end
1414
+ }
1415
+ values_list << new_values
1416
+ else
1417
+ columns.each_key { |column| manager.columns << table[column] }
1418
+ end
1419
+
1420
+ manager.values = manager.create_values_list(values_list)
1421
+ visitor.compile(manager.ast)
1422
+ end
1423
+
1424
+ def build_fixture_statements(fixture_set)
1425
+ puts_log "build_fixture_statements - fixture_set = #{fixture_set}"
1426
+ fixture_set.filter_map do |table_name, fixtures|
1427
+ next if fixtures.empty?
1428
+ build_fixture_sql(fixtures, table_name)
1429
+ end
1430
+ end
1431
+
1432
+ # inserts values from fixtures
1433
+ # overridden to handle LOB's fixture insertion, as, in normal inserts callbacks are triggered but during fixture insertion callbacks are not triggered
1434
+ # hence only markers like @@@IBMBINARY@@@ will be inserted and are not updated to actual data
1435
+ def insert_fixture(fixture, table_name)
1436
+ puts_log "insert_fixture = #{fixture}"
1437
+ insert_query = if fixture.respond_to?(:keys)
1438
+ "INSERT INTO #{quote_table_name(table_name)} ( #{fixture.keys.join(', ')})"
1439
+ else
1440
+ "INSERT INTO #{quote_table_name(table_name)} ( #{fixture.key_list})"
1441
+ end
1442
+
1443
+ insert_values = []
1444
+ params = []
1445
+ if @servertype.instance_of? IBM_IDS
1446
+ super
1447
+ return
1448
+ end
1449
+ column_list = columns(table_name)
1450
+ fixture.each do |item|
1451
+ col = nil
1452
+ column_list.each do |column|
1453
+ if column.name.downcase == item.at(0).downcase
1454
+ col = column
1455
+ break
1456
+ end
1457
+ end
1458
+
1459
+ if item.at(1).nil? ||
1460
+ item.at(1) == {} ||
1461
+ (item.at(1) == '' && !(col.sql_type.to_s =~ /text|clob/i))
1462
+ params << 'NULL'
1463
+
1464
+ elsif !col.nil? && (col.sql_type.to_s =~ /blob|binary|clob|text|xml/i)
1465
+ # Add a '?' for the parameter or a NULL if the value is nil or empty
1466
+ # (except for a CLOB field where '' can be a value)
1467
+ insert_values << quote_value_for_pstmt(item.at(1))
1468
+ params << '?'
1469
+ else
1470
+ insert_values << quote_value_for_pstmt(item.at(1), col)
1471
+ params << '?'
1472
+ end
1473
+ end
1474
+
1475
+ insert_query << ' VALUES (' + params.join(',') + ')'
1476
+ unless stmt = IBM_DB.prepare(@connection, insert_query)
1477
+ error_msg = IBM_DB.getErrormsg(@connection, IBM_DB::DB_CONN)
1478
+ if error_msg && !error_msg.empty?
1479
+ raise "Failed to prepare statement for fixtures insert due to : #{error_msg}"
1480
+ end
1481
+
1482
+ raise StandardError.new('An unexpected error occurred during preparing SQL for fixture insert')
1483
+
1484
+ end
1485
+
1486
+ log(insert_query, 'fixture insert') do
1487
+ if IBM_DB.execute(stmt, insert_values)
1488
+ IBM_DB.free_stmt(stmt) if stmt
1489
+ else
1490
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT)
1491
+ IBM_DB.free_stmt(stmt) if stmt
1492
+ raise "Failed to insert due to: #{error_msg}"
1493
+ end
1494
+ end
1495
+ end
1496
+
1497
+ def empty_insert_statement_value(pkey, table_name)
1498
+ puts_log "empty_insert_statement_value pkey = #{pkey}, table_name = #{table_name}"
1499
+ puts_log caller
1500
+
1501
+ colCount = columns(table_name).count()
1502
+ puts_log "empty_insert_statement_value colCount = #{colCount}"
1503
+ val = "DEFAULT, " * (colCount - 1)
1504
+ val = val + "DEFAULT"
1505
+ " VALUES (#{val})"
1506
+ end
1507
+
1508
+ def getTableIdentityColumn(table_name)
1509
+ query = "SELECT COLNAME FROM SYSCAT.COLUMNS WHERE TABNAME = #{quote(table_name.upcase)} AND IDENTITY = 'Y'"
1510
+ puts_log "getTableIdentityColumn table_name = #{table_name}, query = #{query}"
1511
+ rows = execute_without_logging(query).rows
1512
+ puts_log "getTableIdentityColumn rows = #{rows}"
1513
+ if rows.any?
1514
+ return rows.first
1515
+ end
1516
+ end
1517
+
1518
+ def return_insert (stmt, sql, binds, pk, id_value = nil, returning: nil)
1519
+ puts_log "return_insert sql = #{sql}, pk = #{pk}, returning = #{returning}"
1520
+ @sql << sql
1521
+
1522
+ table_name = sql[/\AINSERT\s+INTO\s+([^\s\(]+)/i, 1]
1523
+ rowID = getTableIdentityColumn(table_name)
1524
+ #Identity column exist.
1525
+ if Array(rowID).any?
1526
+ val = @servertype.last_generated_id(stmt)
1527
+ #returning required is just an ID, or nothing is expected to return
1528
+ only_returning_id = Array(returning).empty? ||
1529
+ (Array(returning).size == 1 && Array(rowID).first == Array(returning).first)
1530
+ unless only_returning_id
1531
+ cols = Array(returning).join(', ')
1532
+ query = "SELECT #{cols} FROM #{table_name} WHERE #{Array(rowID).first} = #{val}"
1533
+ puts_log "return_insert val = #{val}, cols = #{cols}, table_name = #{table_name}"
1534
+ puts_log "return_insert query = #{query}"
1535
+ rows = execute_without_logging(query).rows
1536
+ puts_log "return_insert rows = #{rows}"
1537
+ return rows.first
1538
+ end
1539
+ end
1540
+
1541
+ puts_log "return_insert id_value = #{id_value}, val = #{val}"
1542
+ if !returning.nil?
1543
+ [id_value || val]
1544
+ else
1545
+ id_value || val
1546
+ end
1547
+ end
1548
+
1549
+ # Perform an insert and returns the last ID generated.
1550
+ # This can be the ID passed to the method or the one auto-generated by the database,
1551
+ # and retrieved by the +last_generated_id+ method.
1552
+ def insert_direct(sql, name = nil, _pk = nil, id_value = nil, returning: nil)
1553
+ puts_log "insert_direct sql = #{sql}, name = #{name}, _pk = #{_pk}, returning = #{returning}"
1554
+ if @handle_lobs_triggered # Ensure the array of sql is cleared if they have been handled in the callback
1555
+ @sql = []
1556
+ @handle_lobs_triggered = false
1557
+ end
1558
+
1559
+ return unless stmt = execute(sql, name)
1560
+
1561
+ begin
1562
+ return_insert(stmt, sql, nil, _pk, id_value, returning: returning)
1563
+ # Ensures to free the resources associated with the statement
1564
+ ensure
1565
+ IBM_DB.free_stmt(stmt) if stmt
1566
+ end
1567
+ end
1568
+
1569
+ def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil)
1570
+ puts_log "insert Binds P = #{binds}, name = #{name}, pk = #{pk}, id_value = #{id_value}, returning = #{returning}"
1571
+ puts_log caller
1572
+ if @arelVersion < 6
1573
+ sql = to_sql(arel)
1574
+ binds = binds
1575
+ else
1576
+ sql, binds = to_sql_and_binds(arel, binds)
1577
+ end
1578
+
1579
+ puts_log "insert Binds A = #{binds}"
1580
+ puts_log "insert SQL = #{sql}"
1581
+ # unless IBM_DBAdapter.respond_to?(:exec_insert)
1582
+ return insert_direct(sql, name, pk, id_value, returning: returning) if binds.nil? || binds.empty?
1583
+
1584
+ ActiveRecord::Base.clear_query_caches_for_current_thread
1585
+
1586
+ return unless stmt = exec_insert_db2(sql, name, binds, pk, sequence_name, returning)
1587
+
1588
+ begin
1589
+ return_insert(stmt, sql, binds, pk, id_value, returning: returning)
1590
+ ensure
1591
+ IBM_DB.free_stmt(stmt) if stmt
1592
+ end
1593
+ end
1594
+
1595
+ def exec_insert_db2(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning = nil)
1596
+ puts_log "exec_insert_db2 sql = #{sql}, name = #{name}, binds = #{binds}, pk = #{pk}, returning = #{returning}"
1597
+ sql, binds = sql_for_insert(sql, pk, binds, returning)
1598
+ exec_query_ret_stmt(sql, name, binds, prepare: false)
1599
+ end
1600
+
1601
+ def build_insert_sql(insert) # :nodoc:
1602
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
1603
+ sql
1604
+ end
1605
+
1606
+ def last_inserted_id(result)
1607
+ puts_log 'last_inserted_id'
1608
+ result
1609
+ end
1610
+
1611
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) # :nodoc:
1612
+ puts_log 'exec_insert'
1613
+ insert(sql)
1614
+ end
1615
+
1616
+ # Praveen
1617
+ # Performs an insert using the prepared statement and returns the last ID generated.
1618
+ # This can be the ID passed to the method or the one auto-generated by the database,
1619
+ # and retrieved by the +last_generated_id+ method.
1620
+ def prepared_insert(pstmt, param_array = nil, id_value = nil)
1621
+ puts_log 'prepared_insert'
1622
+ if @handle_lobs_triggered # Ensure the array of sql is cleared if they have been handled in the callback
1623
+ @sql = []
1624
+ @sql_parameter_values = []
1625
+ @handle_lobs_triggered = false
1626
+ end
1627
+
1628
+ ActiveRecord::Base.clear_query_caches_for_current_thread
1629
+
1630
+ begin
1631
+ if execute_prepared_stmt(pstmt, param_array)
1632
+ @sql << @prepared_sql
1633
+ @sql_parameter_values << param_array
1634
+ id_value || @servertype.last_generated_id(pstmt)
1635
+ end
1636
+ rescue StandardError => e
1637
+ raise e
1638
+ ensure
1639
+ IBM_DB.free_stmt(pstmt) if pstmt
1640
+ end
1641
+ end
1642
+
1643
+ # Praveen
1644
+ # Prepares and logs +sql+ commands and
1645
+ # returns a +IBM_DB.Statement+ object.
1646
+ def prepare(sql, name = nil)
1647
+ puts_log 'prepare'
1648
+ # The +log+ method is defined in the parent class +AbstractAdapter+
1649
+ @prepared_sql = sql
1650
+ log(sql, name) do
1651
+ @servertype.prepare(sql, name)
1652
+ end
1653
+ end
1654
+
1655
+ # Praveen
1656
+ # Executes the prepared statement
1657
+ # ReturnsTrue on success and False on Failure
1658
+ def execute_prepared_stmt(pstmt, param_array = nil)
1659
+ puts_log 'execute_prepared_stmt'
1660
+ puts_log "Param array = #{param_array}"
1661
+ param_array = nil if !param_array.nil? && param_array.size < 1
1662
+
1663
+ if !IBM_DB.execute(pstmt, param_array)
1664
+ error_msg = IBM_DB.getErrormsg(pstmt, IBM_DB::DB_STMT)
1665
+ puts_log "Error = #{error_msg}"
1666
+ IBM_DB.free_stmt(pstmt) if pstmt
1667
+ raise StatementInvalid, error_msg
1668
+ else
1669
+ true
1670
+ end
1671
+ end
1672
+
1673
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
1674
+ :desc, :describe
1675
+ ) # :nodoc:
1676
+ private_constant :READ_QUERY
1677
+
1678
+ def write_query?(sql) # :nodoc:
1679
+ !READ_QUERY.match?(sql)
1680
+ rescue ArgumentError # Invalid encoding
1681
+ !READ_QUERY.match?(sql.b)
1682
+ end
1683
+
1684
+ def explain(arel, binds = [], options = [])
1685
+ sql = "EXPLAIN ALL SET QUERYNO = 1 FOR #{to_sql(arel, binds)}"
1686
+ stmt = execute(sql, 'EXPLAIN')
1687
+ result = select("select * from explain_statement where explain_level = 'P' and queryno = 1", 'EXPLAIN')
1688
+ result[0]['total_cost'].to_s
1689
+ # Ensures to free the resources associated with the statement
1690
+ ensure
1691
+ IBM_DB.free_stmt(stmt) if stmt
1692
+ end
1693
+
1694
+ def execute_without_logging(sql, name = nil, binds = [], prepare: true, async: false)
1695
+ puts_log "execute_without_logging sql = #{sql}, name = #{name}, binds = #{binds}"
1696
+
1697
+ sql = transform_query(sql)
1698
+ check_if_write_query(sql)
1699
+ mark_transaction_written_if_write(sql)
1700
+ cols = nil
1701
+ results = nil
1702
+ begin
1703
+ param_array = type_casted_binds(binds)
1704
+ puts_log "execute_without_logging Param array = #{param_array}"
1705
+ puts_log "execute_without_logging #{caller}"
1706
+
1707
+ stmt = @servertype.prepare(sql, name)
1708
+ @statements[sql] = stmt if prepare
1709
+
1710
+ puts_log "execute_without_logging Statement = #{stmt}"
1711
+
1712
+ execute_prepared_stmt(stmt, param_array)
1713
+
1714
+ if stmt and sql.strip.upcase.start_with?("SELECT")
1715
+ cols = IBM_DB.resultCols(stmt)
1716
+ results = fetch_data(stmt) if stmt
1717
+
1718
+ puts_log "execute_without_logging columns = #{cols}"
1719
+ puts_log "execute_without_logging result = #{results}"
1720
+ end
1721
+ rescue => e
1722
+ raise translate_exception_class(e, sql, binds)
1723
+ ensure
1724
+ @offset = @limit = nil
1725
+ end
1726
+ if @isAr3
1727
+ results
1728
+ elsif results.nil?
1729
+ ActiveRecord::Result.empty
1730
+ else
1731
+ ActiveRecord::Result.new(cols, results)
1732
+ end
1733
+ end
1734
+
1735
+ # Executes +sql+ statement in the context of this connection using
1736
+ # +binds+ as the bind substitutes. +name+ is logged along with
1737
+ # the executed +sql+ statement.
1738
+ # Here prepare argument is not used, by default this method creates prepared statment and execute.
1739
+ def exec_query_ret_stmt(sql, name = 'SQL', binds = [], prepare: false, async: false, allow_retry: false)
1740
+ puts_log "exec_query_ret_stmt #{sql}"
1741
+ sql = transform_query(sql)
1742
+ check_if_write_query(sql)
1743
+ mark_transaction_written_if_write(sql)
1744
+ begin
1745
+ puts_log "SQL = #{sql}"
1746
+ puts_log "Binds = #{binds}"
1747
+ param_array = type_casted_binds(binds)
1748
+ puts_log "Param array = #{param_array}"
1749
+ puts_log "Prepare flag = #{prepare}"
1750
+ puts_log "#{caller}"
1751
+
1752
+ stmt = @servertype.prepare(sql, name)
1753
+ @statements[sql] = stmt if prepare
1754
+
1755
+ puts_log "Statement = #{stmt}"
1756
+ log(sql, name, binds, param_array, async: async) do
1757
+ with_raw_connection(allow_retry: allow_retry) do |conn|
1758
+ return false unless stmt
1759
+ return stmt if execute_prepared_stmt(stmt, param_array)
1760
+ end
1761
+ end
1762
+ rescue => e
1763
+ raise translate_exception_class(e, sql, binds)
1764
+ ensure
1765
+ @offset = @limit = nil
1766
+ end
1767
+ end
1768
+
1769
+ def internal_exec_query(sql, name = 'SQL', binds = [], prepare: false, async: false)
1770
+ select_prepared(sql, name, binds, prepare: prepare, async: async)
1771
+ end
1772
+
1773
+ def select_prepared(sql, name = nil, binds = [], prepare: true, async: false)
1774
+ puts_log 'select_prepared'
1775
+ puts_log "select_prepared sql before = #{sql}"
1776
+ puts_log "select_prepared Binds = #{binds}"
1777
+ stmt = exec_query_ret_stmt(sql, name, binds, prepare: prepare, async: async)
1778
+ cols = nil
1779
+ results = nil
1780
+
1781
+ if stmt and sql.strip.upcase.start_with?("SELECT")
1782
+ cols = IBM_DB.resultCols(stmt)
1783
+
1784
+ results = fetch_data(stmt) if stmt
1785
+
1786
+ puts_log "select_prepared columns = #{cols}"
1787
+ puts_log "select_prepared sql after = #{sql}"
1788
+ puts_log "select_prepared result = #{results}"
1789
+ end
1790
+ if @isAr3
1791
+ results
1792
+ elsif results.nil?
1793
+ ActiveRecord::Result.empty
1794
+ else
1795
+ ActiveRecord::Result.new(cols, results)
1796
+ end
1797
+ end
1798
+
1799
+ def check_if_write_query(sql) # For rails 7.1 just remove this function as it will be defined in AbstractAdapter class
1800
+ return unless preventing_writes? && write_query?(sql)
1801
+
1802
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
1803
+ end
1804
+
1805
+ # Executes and logs +sql+ commands and
1806
+ # returns a +IBM_DB.Statement+ object.
1807
+ def execute(sql, name = nil, allow_retry: false)
1808
+ puts_log "execute #{sql}"
1809
+ ActiveRecord::Base.clear_query_caches_for_current_thread
1810
+ stmt = internal_execute(sql, name, allow_retry: allow_retry)
1811
+ cols = nil
1812
+ results = nil
1813
+ puts_log "raw_execute stmt = #{stmt}"
1814
+ if sql.strip.upcase.start_with?("SELECT") and stmt
1815
+ cols = IBM_DB.resultCols(stmt)
1816
+ results = fetch_data(stmt)
1817
+
1818
+ puts_log "execute columns = #{cols}"
1819
+ puts_log "execute result = #{results}"
1820
+ end
1821
+ if results.nil? || results.empty?
1822
+ stmt
1823
+ else
1824
+ formatted = cols.each_with_index.map { |col, i| { col => results[i].first } }
1825
+ puts_log "raw_execute formatted = #{formatted}"
1826
+ formatted.to_s
1827
+ end
1828
+ end
1829
+
1830
+ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
1831
+ # Logs and execute the sql instructions.
1832
+ # The +log+ method is defined in the parent class +AbstractAdapter+
1833
+ # sql='INSERT INTO ar_internal_metadata (key, value, created_at, updated_at) VALUES ('10', '10', '10', '10')
1834
+ puts_log "raw_execute sql = #{sql} #{Thread.current}"
1835
+ log(sql, name, async: async) do
1836
+ with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
1837
+ verify!
1838
+ puts_log "raw_execute executes query #{Thread.current}"
1839
+ result= @servertype.execute(sql, name)
1840
+ puts_log "raw_execute result = #{result} #{Thread.current}"
1841
+ verified!
1842
+ result
1843
+ end
1844
+ end
1845
+ end
1846
+
1847
+ # Executes an "UPDATE" SQL statement
1848
+ def update_direct(sql, name = nil)
1849
+ puts_log 'update_direct'
1850
+ if @handle_lobs_triggered # Ensure the array of sql is cleared if they have been handled in the callback
1851
+ @sql = []
1852
+ @handle_lobs_triggered = false
1853
+ end
1854
+
1855
+ # Logs and execute the given sql query.
1856
+ return unless stmt = execute(sql, name)
1857
+
1858
+ begin
1859
+ @sql << sql
1860
+ # Retrieves the number of affected rows
1861
+ IBM_DB.num_rows(stmt)
1862
+ # Ensures to free the resources associated with the statement
1863
+ ensure
1864
+ IBM_DB.free_stmt(stmt) if stmt
1865
+ end
1866
+ end
1867
+
1868
+ # Praveen
1869
+ def prepared_update(pstmt, param_array = nil)
1870
+ puts_log 'prepared_update'
1871
+ if @handle_lobs_triggered # Ensure the array of sql is cleared if they have been handled in the callback
1872
+ @sql = []
1873
+ @sql_parameter_values = []
1874
+ @handle_lobs_triggered = false
1875
+ end
1876
+
1877
+ ActiveRecord::Base.clear_query_caches_for_current_thread
1878
+
1879
+ begin
1880
+ if execute_prepared_stmt(pstmt, param_array)
1881
+ @sql << @prepared_sql
1882
+ @sql_parameter_values << param_array
1883
+ # Retrieves the number of affected rows
1884
+ IBM_DB.num_rows(pstmt)
1885
+ # Ensures to free the resources associated with the statement
1886
+ end
1887
+ rescue StandardError => e
1888
+ raise e
1889
+ ensure
1890
+ IBM_DB.free_stmt(pstmt) if pstmt
1891
+ end
1892
+ end
1893
+ # The delete method executes the delete
1894
+ # statement and returns the number of affected rows.
1895
+ # The method is an alias for +update+
1896
+ alias prepared_delete prepared_update
1897
+
1898
+ def update(arel, name = nil, binds = [])
1899
+ puts_log 'update'
1900
+ if @arelVersion < 6
1901
+ sql = to_sql(arel)
1902
+ else
1903
+ sql, binds = to_sql_and_binds(arel, binds)
1904
+ end
1905
+
1906
+ # Make sure the WHERE clause handles NULL's correctly
1907
+ sqlarray = sql.split(/\s*WHERE\s*/)
1908
+ size = sqlarray.size
1909
+ if size > 1
1910
+ sql = sqlarray[0] + ' WHERE '
1911
+ if size > 2
1912
+ 1.upto size - 2 do |index|
1913
+ sqlarray[index].gsub!(/(=\s*NULL|IN\s*\(NULL\))/i, ' IS NULL') unless sqlarray[index].nil?
1914
+ sql = sql + sqlarray[index] + ' WHERE '
1915
+ end
1916
+ end
1917
+ sqlarray[size - 1].gsub!(/(=\s*NULL|IN\s*\(NULL\))/i, ' IS NULL') unless sqlarray[size - 1].nil?
1918
+ sql += sqlarray[size - 1]
1919
+ end
1920
+
1921
+ ActiveRecord::Base.clear_query_caches_for_current_thread
1922
+
1923
+ if binds.nil? || binds.empty?
1924
+ update_direct(sql, name)
1925
+ else
1926
+ begin
1927
+ if stmt = exec_query_ret_stmt(sql, name, binds, prepare: true)
1928
+ IBM_DB.num_rows(stmt)
1929
+ end
1930
+ ensure
1931
+ IBM_DB.free_stmt(stmt) if stmt
1932
+ end
1933
+ end
1934
+ end
1935
+
1936
+ alias delete update
1937
+
1938
+ def auto_commit_on
1939
+ puts_log 'Inside auto_commit_on'
1940
+ IBM_DB.autocommit @connection, IBM_DB::SQL_AUTOCOMMIT_ON
1941
+ ac = IBM_DB::autocommit @connection
1942
+ if ac != 1
1943
+ puts_log "Cannot set IBM_DB::AUTOCOMMIT_ON"
1944
+ else
1945
+ puts_log "AUTOCOMMIT_ON set"
1946
+ end
1947
+ end
1948
+
1949
+ def auto_commit_off
1950
+ puts_log 'auto_commit_off'
1951
+ IBM_DB.autocommit(@connection, IBM_DB::SQL_AUTOCOMMIT_OFF)
1952
+ ac = IBM_DB::autocommit @connection
1953
+ if ac != 0
1954
+ puts_log "Cannot set IBM_DB::AUTOCOMMIT_OFF"
1955
+ else
1956
+ puts_log "AUTOCOMMIT_OFF set"
1957
+ end
1958
+ end
1959
+
1960
+ # Begins the transaction (and turns off auto-committing)
1961
+ def begin_db_transaction
1962
+ puts_log 'begin_db_transaction'
1963
+ log('begin transaction', 'TRANSACTION') do
1964
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
1965
+ # Turns off the auto-commit
1966
+ auto_commit_off
1967
+ verified!
1968
+ end
1969
+ end
1970
+ end
1971
+
1972
+ # Commits the transaction and turns on auto-committing
1973
+ def commit_db_transaction
1974
+ puts_log 'commit_db_transaction'
1975
+ log('commit transaction', 'TRANSACTION') do
1976
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
1977
+ # Commits the transaction
1978
+
1979
+ IBM_DB.commit @connection
1980
+ end
1981
+ rescue StandardError
1982
+ nil
1983
+ end
1984
+ # Turns on auto-committing
1985
+ auto_commit_on
1986
+ end
1987
+
1988
+ # Rolls back the transaction and turns on auto-committing. Must be
1989
+ # done if the transaction block raises an exception or returns false
1990
+ def rollback_db_transaction
1991
+ puts_log 'rollback_db_transaction'
1992
+ log('rollback transaction', 'TRANSACTION') do
1993
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
1994
+ # ROLLBACK the transaction
1995
+
1996
+ IBM_DB.rollback(@connection)
1997
+ end
1998
+ rescue StandardError
1999
+ nil
2000
+ end
2001
+ ActiveRecord::Base.clear_query_caches_for_current_thread
2002
+ # Turns on auto-committing
2003
+ auto_commit_on
2004
+ end
2005
+
2006
+ def default_sequence_name(table, column) # :nodoc:
2007
+ puts_log "default_sequence_name table = #{table}, column = #{column}"
2008
+ return nil if column.is_a?(Array)
2009
+ "#{table}_#{column}_seq"
2010
+ end
2011
+
2012
+ #==============================================
2013
+ # QUOTING
2014
+ #==============================================
2015
+
2016
+ def quote_value_for_pstmt(value, column = nil)
2017
+ puts_log 'quote_value_for_pstmt'
2018
+ return value.quoted_id if value.respond_to?(:quoted_id)
2019
+
2020
+ case value
2021
+ when String, ActiveSupport::Multibyte::Chars
2022
+ value = value.to_s
2023
+ if column && column.sql_type.to_s =~ /int|serial|float/i
2024
+ column.sql_type.to_s =~ /int|serial/i ? value.to_i : value.to_f
2025
+
2026
+ else
2027
+ value
2028
+ end
2029
+ when NilClass then nil
2030
+ when TrueClass then 1
2031
+ when FalseClass then 0
2032
+ when Float, Integer, Integer then value
2033
+ # BigDecimals need to be output in a non-normalized form and quoted.
2034
+ when BigDecimal then value.to_s('F')
2035
+ when Numeric, Symbol then value.to_s
2036
+ else
2037
+ if value.acts_like?(:date) || value.acts_like?(:time)
2038
+ quoted_date(value)
2039
+ else
2040
+ value.to_yaml
2041
+ end
2042
+ end
2043
+ end
2044
+
2045
+ # Quotes a given string, escaping single quote (') characters.
2046
+ def quote_string(string)
2047
+ puts_log 'quote_string'
2048
+ string.gsub(/'/, "''")
2049
+ # string.gsub('\\', '\&\&').gsub("'", "''")
2050
+ end
2051
+
2052
+ # *true* is represented by a smallint 1, *false*
2053
+ # by 0, as no native boolean type exists in DB2.
2054
+ # Numerics are not quoted in DB2.
2055
+ def quoted_true
2056
+ puts_log 'quoted_true'
2057
+ '1'.freeze
2058
+ end
2059
+
2060
+ def quoted_false
2061
+ puts_log 'quoted_false'
2062
+ '0'.freeze
2063
+ end
2064
+
2065
+ def unquoted_true
2066
+ puts_log 'unquoted_true'
2067
+ 1
2068
+ end
2069
+
2070
+ def unquoted_false
2071
+ puts_log 'unquoted_false'
2072
+ 0
2073
+ end
2074
+
2075
+ def quote_table_name(name)
2076
+ puts_log "quote_table_name #{name}"
2077
+ puts_log caller
2078
+ if name.start_with? '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
2079
+ name = "\"#{name}\""
2080
+ else
2081
+ name = name.to_s
2082
+ end
2083
+ puts_log "name = #{name}"
2084
+ name
2085
+ # @servertype.check_reserved_words(name).gsub('"', '').gsub("'",'')
2086
+ end
2087
+
2088
+ def quote_column_name(name)
2089
+ puts_log "quote_column_name #{name}"
2090
+ @servertype.check_reserved_words(name).gsub('"', '').gsub("'", '')
2091
+ end
2092
+
2093
+ def quoted_binary(value)
2094
+ puts_log 'quoted_binary'
2095
+ "CAST(x'#{value.hex}' AS BLOB)"
2096
+ end
2097
+ #==============================================
2098
+ # SCHEMA STATEMENTS
2099
+ #==============================================
2100
+
2101
+ # Returns a Hash of mappings from the abstract data types to the native
2102
+ # database types
2103
+ def native_database_types
2104
+ {
2105
+ primary_key: { name: @servertype.primary_key_definition(@start_id) },
2106
+ string: { name: 'varchar', limit: 400 },
2107
+ text: { name: 'clob' },
2108
+ integer: { name: 'integer' },
2109
+ float: { name: 'float' },
2110
+ datetime: { name: 'timestamp' },
2111
+ timestamp: { name: 'timestamp' },
2112
+ time: { name: 'time' },
2113
+ date: { name: 'date' },
2114
+ binary: { name: 'blob' },
2115
+
2116
+ # IBM data servers don't have a native boolean type.
2117
+ # A boolean can be represented by a smallint,
2118
+ # adopting the convention that False is 0 and True is 1
2119
+ boolean: { name: 'smallint' },
2120
+ xml: { name: 'xml' },
2121
+ decimal: { name: 'decimal' },
2122
+ rowid: { name: 'rowid' }, # rowid is a supported datatype on z/OS and i/5
2123
+ serial: { name: 'serial' }, # rowid is a supported datatype on Informix Dynamic Server
2124
+ char: { name: 'char' },
2125
+ double: { name: @servertype.get_double_mapping },
2126
+ decfloat: { name: 'decfloat' },
2127
+ graphic: { name: 'graphic' },
2128
+ vargraphic: { name: 'vargraphic' },
2129
+ bigint: { name: 'bigint' }
2130
+ }
2131
+ end
2132
+
2133
+ def build_conn_str_for_dbops
2134
+ puts_log 'build_conn_str_for_dbops'
2135
+ connect_str = 'DRIVER={IBM DB2 ODBC DRIVER};ATTACH=true;'
2136
+ unless @host.nil?
2137
+ connect_str << "HOSTNAME=#{@host};"
2138
+ connect_str << "PORT=#{@port};"
2139
+ connect_str << 'PROTOCOL=TCPIP;'
2140
+ end
2141
+ connect_str << "UID=#{@username};PWD=#{@password};"
2142
+ connect_str
2143
+ end
2144
+
2145
+ def drop_database(dbName)
2146
+ puts_log 'drop_database'
2147
+ connect_str = build_conn_str_for_dbops
2148
+
2149
+ # Ensure connection is closed before trying to drop a database.
2150
+ # As a connect call would have been made by call seeing connection in active
2151
+ disconnect!
2152
+
2153
+ begin
2154
+ dropConn = IBM_DB.connect(connect_str, '', '')
2155
+ rescue StandardError => e
2156
+ raise "Failed to connect to server due to: #{e}"
2157
+ end
2158
+
2159
+ if IBM_DB.dropDB(dropConn, dbName)
2160
+ IBM_DB.close(dropConn)
2161
+ true
2162
+ else
2163
+ error = IBM_DB.getErrormsg(dropConn, IBM_DB::DB_CONN)
2164
+ IBM_DB.close(dropConn)
2165
+ raise "Could not drop Database due to: #{error}"
2166
+ end
2167
+ end
2168
+
2169
+ def create_database(dbName, codeSet = nil, mode = nil)
2170
+ puts_log 'create_database'
2171
+ connect_str = build_conn_str_for_dbops
2172
+
2173
+ # Ensure connection is closed before trying to drop a database.
2174
+ # As a connect call would have been made by call seeing connection in active
2175
+ disconnect!
2176
+
2177
+ begin
2178
+ createConn = IBM_DB.connect(connect_str, '', '')
2179
+ rescue StandardError => e
2180
+ raise "Failed to connect to server due to: #{e}"
2181
+ end
2182
+
2183
+ if IBM_DB.createDB(createConn, dbName, codeSet, mode)
2184
+ IBM_DB.close(createConn)
2185
+ true
2186
+ else
2187
+ error = IBM_DB.getErrormsg(createConn, IBM_DB::DB_CONN)
2188
+ IBM_DB.close(createConn)
2189
+ raise "Could not create Database due to: #{error}"
2190
+ end
2191
+ end
2192
+
2193
+ def valid_type?(type)
2194
+ !native_database_types[type].nil?
2195
+ end
2196
+
2197
+ # IBM data servers do not support limits on certain data types (unlike MySQL)
2198
+ # Limit is supported for the {float, decimal, numeric, varchar, clob, blob, graphic, vargraphic} data types.
2199
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
2200
+ puts_log 'type_to_sql'
2201
+ puts_log "Type = #{type}, Limit = #{limit}"
2202
+ puts_log "type_to_sql = #{caller}"
2203
+
2204
+ if type.to_sym == :binary and limit.class == Hash and limit.has_key?('limit'.to_sym)
2205
+ sql_segment = native_database_types[type.to_sym][:name].to_s
2206
+ sql_segment << "(#{limit[:limit]})"
2207
+ return sql_segment
2208
+ end
2209
+
2210
+ if type.to_sym == :datetime and limit.class == Hash and limit.has_key?('precision'.to_sym)
2211
+ sql_segment = native_database_types[type.to_sym][:name].to_s
2212
+ if limit[:precision].nil?
2213
+ return sql_segment
2214
+ elsif (0..12).include?(limit[:precision])
2215
+ sql_segment << "(#{limit[:precision]})"
2216
+ return sql_segment
2217
+ else
2218
+ raise ArgumentError,
2219
+ "No #{sql_segment} type has precision of #{limit[:precision]}. The allowed range of precision is from 0 to 12"
2220
+ end
2221
+ end
2222
+
2223
+ if type.to_sym == :string and limit.class == Hash and limit.has_key?('limit'.to_sym)
2224
+ sql_segment = native_database_types[type.to_sym][:name].to_s
2225
+ sql_segment << "(#{limit[:limit]})"
2226
+ return sql_segment
2227
+ end
2228
+
2229
+ if type.to_sym == :decimal
2230
+ precision = limit[:precision] if limit.class == Hash && limit.has_key?('precision'.to_sym)
2231
+ scale = limit[:scale] if limit.class == Hash && limit.has_key?('scale'.to_sym)
2232
+ sql_segment = native_database_types[type.to_sym][:name].to_s
2233
+ if !precision.nil? && !scale.nil?
2234
+ sql_segment << "(#{precision},#{scale})"
2235
+ return sql_segment
2236
+ elsif scale.nil? && !precision.nil?
2237
+ sql_segment << "(#{precision})"
2238
+ return sql_segment
2239
+ elsif precision.nil? && !scale.nil?
2240
+ raise ArgumentError, 'Error adding decimal column: precision cannot be empty if scale is specified'
2241
+ else
2242
+ return sql_segment
2243
+ end
2244
+ end
2245
+
2246
+ if type.to_sym == :decfloat
2247
+ sql_segment = native_database_types[type.to_sym][:name].to_s
2248
+ sql_segment << "(#{precision})" unless precision.nil?
2249
+ return sql_segment
2250
+ end
2251
+
2252
+ if type.to_sym == :vargraphic
2253
+ sql_segment = native_database_types[type.to_sym][:name].to_s
2254
+ if limit.class == Hash
2255
+ return 'vargraphic(1)' unless limit.has_key?('limit'.to_sym)
2256
+
2257
+ limit1 = limit[:limit]
2258
+ sql_segment << "(#{limit1})"
2259
+
2260
+ else
2261
+ return 'vargraphic(1)' if limit.nil?
2262
+
2263
+ sql_segment << "(#{limit})"
2264
+
2265
+ end
2266
+ return sql_segment
2267
+ end
2268
+
2269
+ if type.to_sym == :graphic
2270
+ sql_segment = native_database_types[type.to_sym][:name].to_s
2271
+ if limit.class == Hash
2272
+ return 'graphic(1)' unless limit.has_key?('limit'.to_sym)
2273
+
2274
+ limit1 = limit[:limit]
2275
+ sql_segment << "(#{limit1})"
2276
+
2277
+ else
2278
+ return 'graphic(1)' if limit.nil?
2279
+
2280
+ sql_segment << "(#{limit})"
2281
+
2282
+ end
2283
+ return sql_segment
2284
+ end
2285
+
2286
+ if limit.class == Hash
2287
+ return super(type) if limit.has_key?('limit'.to_sym).nil?
2288
+ elsif limit.nil?
2289
+ return super(type)
2290
+ end
2291
+
2292
+ # strip off limits on data types not supporting them
2293
+ if @servertype.limit_not_supported_types.include? type.to_sym
2294
+ native_database_types[type.to_sym][:name].to_s
2295
+ elsif type.to_sym == :boolean
2296
+ 'smallint'
2297
+ else
2298
+ super(type)
2299
+ end
2300
+ end
2301
+
2302
+ # Returns the maximum length a table alias identifier can be.
2303
+ # IBM data servers (cross-platform) table limit is 128 characters
2304
+ def table_alias_length
2305
+ 128
2306
+ end
2307
+
2308
+ # Retrieves table's metadata for a specified shema name
2309
+ def tables(_name = nil)
2310
+ puts_log 'tables'
2311
+ # Initializes the tables array
2312
+ tables = []
2313
+ # Retrieve table's metadata through IBM_DB driver
2314
+ stmt = IBM_DB.tables(@connection, nil,
2315
+ @servertype.set_case(@schema))
2316
+ if stmt
2317
+ begin
2318
+ # Fetches all the records available
2319
+ while tab = IBM_DB.fetch_assoc(stmt)
2320
+ # Adds the lowercase table name to the array
2321
+ if tab['table_type'] == 'TABLE' # check, so that only tables are dumped,IBM_DB.tables also returns views,alias etc in the schema
2322
+ tables << tab['table_name'].downcase
2323
+ end
2324
+ end
2325
+ rescue StandardError => e # Handle driver fetch errors
2326
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT)
2327
+ raise "Failed to retrieve table metadata during fetch: #{error_msg}" if error_msg && !error_msg.empty?
2328
+
2329
+ error_msg = 'An unexpected error occurred during retrieval of table metadata'
2330
+ error_msg += ": #{e.message}" unless e.message.empty?
2331
+ raise error_msg
2332
+ ensure
2333
+ IBM_DB.free_stmt(stmt) if stmt # Free resources associated with the statement
2334
+ end
2335
+ else # Handle driver execution errors
2336
+ error_msg = IBM_DB.getErrormsg(@connection, IBM_DB::DB_CONN)
2337
+ raise "Failed to retrieve tables metadata due to error: #{error_msg}" if error_msg && !error_msg.empty?
2338
+
2339
+ raise StandardError.new('An unexpected error occurred during retrieval of table metadata')
2340
+
2341
+ end
2342
+ # Returns the tables array
2343
+ tables
2344
+ end
2345
+
2346
+ # Retrieves views's metadata for a specified shema name
2347
+ def views
2348
+ puts_log 'views'
2349
+ # Initializes the tables array
2350
+ tables = []
2351
+ # Retrieve view's metadata through IBM_DB driver
2352
+ stmt = IBM_DB.tables(@connection, nil, @servertype.set_case(@schema))
2353
+ if stmt
2354
+ begin
2355
+ # Fetches all the records available
2356
+ while tab = IBM_DB.fetch_assoc(stmt)
2357
+ # Adds the lowercase view's name to the array
2358
+ if tab['table_type'] == 'V' # check, so that only views are dumped,IBM_DB.tables also returns tables,alias etc in the schema
2359
+ tables << tab['table_name'].downcase
2360
+ end
2361
+ end
2362
+ rescue StandardError => e # Handle driver fetch errors
2363
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT)
2364
+ raise "Failed to retrieve views metadata during fetch: #{error_msg}" if error_msg && !error_msg.empty?
2365
+
2366
+ error_msg = 'An unexpected error occurred during retrieval of views metadata'
2367
+ error_msg += ": #{e.message}" unless e.message.empty?
2368
+ raise error_msg
2369
+ ensure
2370
+ IBM_DB.free_stmt(stmt) if stmt # Free resources associated with the statement
2371
+ end
2372
+ else # Handle driver execution errors
2373
+ error_msg = IBM_DB.getErrormsg(@connection, IBM_DB::DB_CONN)
2374
+ raise "Failed to retrieve tables metadata due to error: #{error_msg}" if error_msg && !error_msg.empty?
2375
+
2376
+ raise StandardError.new('An unexpected error occurred during retrieval of views metadata')
2377
+
2378
+ end
2379
+ # Returns the tables array
2380
+ tables
2381
+ end
2382
+
2383
+ # Returns the primary key of the mentioned table
2384
+ def primary_key(table_name)
2385
+ puts_log 'primary_key'
2386
+ pk_name = []
2387
+ stmt = IBM_DB.primary_keys(@connection, nil,
2388
+ @servertype.set_case(@schema),
2389
+ @servertype.set_case(table_name.to_s))
2390
+ if stmt
2391
+ begin
2392
+ while (pk_index_row = IBM_DB.fetch_array(stmt))
2393
+ puts_log "Primary_keys = #{pk_index_row}"
2394
+ pk_name << pk_index_row[3].downcase
2395
+ end
2396
+ rescue StandardError => e # Handle driver fetch errors
2397
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT)
2398
+ raise "Failed to retrieve primarykey metadata during fetch: #{error_msg}" if error_msg && !error_msg.empty?
2399
+
2400
+ error_msg = 'An unexpected error occurred during retrieval of primary key metadata'
2401
+ error_msg += ": #{e.message}" unless e.message.empty?
2402
+ raise error_msg
2403
+ ensure # Free resources associated with the statement
2404
+ IBM_DB.free_stmt(stmt) if stmt
2405
+ end
2406
+ else
2407
+ error_msg = IBM_DB.getErrormsg(@connection, IBM_DB::DB_CONN)
2408
+ raise "Failed to retrieve primary key metadata due to error: #{error_msg}" if error_msg && !error_msg.empty?
2409
+
2410
+ raise StandardError.new('An unexpected error occurred during primary key retrieval')
2411
+
2412
+ end
2413
+ if pk_name.length == 1
2414
+ pk_name[0]
2415
+ elsif pk_name.empty?
2416
+ nil
2417
+ else
2418
+ pk_name
2419
+ end
2420
+ end
2421
+
2422
+ # Returns an array of non-primary key indexes for a specified table name
2423
+ def indexes(table_name, _name = nil)
2424
+ puts_log 'indexes'
2425
+ puts_log "Table = #{table_name}"
2426
+ # to_s required because +table_name+ may be a symbol.
2427
+ table_name = table_name.to_s
2428
+ # Checks if a blank table name has been given.
2429
+ # If so it returns an empty array of columns.
2430
+ return [] if table_name.strip.empty?
2431
+
2432
+ indexes = []
2433
+ pk_index = nil
2434
+ index_schema = []
2435
+
2436
+ # fetch the primary keys of the table using function primary_keys
2437
+ # TABLE_SCHEM:: pk_index[1]
2438
+ # TABLE_NAME:: pk_index[2]
2439
+ # COLUMN_NAME:: pk_index[3]
2440
+ # PK_NAME:: pk_index[5]
2441
+ stmt = IBM_DB.primary_keys(@connection, nil,
2442
+ @servertype.set_case(@schema),
2443
+ @servertype.set_case(table_name))
2444
+ if stmt
2445
+ begin
2446
+ while (pk_index_row = IBM_DB.fetch_array(stmt))
2447
+ puts_log "Primary keys = #{pk_index_row}"
2448
+ puts_log "pk_index = #{pk_index}"
2449
+ next unless pk_index_row[5]
2450
+
2451
+ pk_index_name = pk_index_row[5].downcase
2452
+ pk_index_columns = [pk_index_row[3].downcase] # COLUMN_NAME
2453
+ if pk_index
2454
+ pk_index.columns << pk_index_columns
2455
+ else
2456
+ pk_index = IndexDefinition.new(table_name, pk_index_name, true, pk_index_columns)
2457
+ end
2458
+ end
2459
+ rescue StandardError => e # Handle driver fetch errors
2460
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT)
2461
+ raise "Failed to retrieve primarykey metadata during fetch: #{error_msg}" if error_msg && !error_msg.empty?
2462
+
2463
+ error_msg = 'An unexpected error occurred during retrieval of primary key metadata'
2464
+ error_msg += ": #{e.message}" unless e.message.empty?
2465
+ raise error_msg
2466
+ ensure # Free resources associated with the statement
2467
+ IBM_DB.free_stmt(stmt) if stmt
2468
+ end
2469
+ else # Handle driver execution errors
2470
+ error_msg = IBM_DB.getErrormsg(@connection, IBM_DB::DB_CONN)
2471
+ raise "Failed to retrieve primary key metadata due to error: #{error_msg}" if error_msg && !error_msg.empty?
2472
+
2473
+ raise StandardError.new('An unexpected error occurred during primary key retrieval')
2474
+
2475
+ end
2476
+
2477
+ # Query table statistics for all indexes on the table
2478
+ # "TABLE_NAME: #{index_stats[2]}"
2479
+ # "NON_UNIQUE: #{index_stats[3]}"
2480
+ # "INDEX_NAME: #{index_stats[5]}"
2481
+ # "COLUMN_NAME: #{index_stats[8]}"
2482
+ stmt = IBM_DB.statistics(@connection, nil,
2483
+ @servertype.set_case(@schema),
2484
+ @servertype.set_case(table_name), 1)
2485
+ if stmt
2486
+ begin
2487
+ while (index_stats = IBM_DB.fetch_array(stmt))
2488
+ is_composite = false
2489
+ next unless index_stats[5] # INDEX_NAME
2490
+
2491
+ index_name = index_stats[5].downcase
2492
+ index_unique = (index_stats[3] == 0)
2493
+ index_columns = [index_stats[8].downcase] # COLUMN_NAME
2494
+ index_qualifier = index_stats[4].downcase # Index_Qualifier
2495
+ # Create an IndexDefinition object and add to the indexes array
2496
+ i = 0
2497
+ indexes.each do |index|
2498
+ if index.name == index_name && index_schema[i] == index_qualifier
2499
+ # index.columns = index.columns + index_columns
2500
+ index.columns.concat index_columns
2501
+ is_composite = true
2502
+ end
2503
+ i += 1
2504
+ end
2505
+
2506
+ next if is_composite
2507
+
2508
+ sql = "select remarks from syscat.indexes where tabname = #{quote(table_name.upcase)} and indname = #{quote(index_stats[5])}"
2509
+ comment = single_value_from_rows(execute_without_logging(sql, "SCHEMA").rows)
2510
+
2511
+ indexes << IndexDefinition.new(table_name, index_name, index_unique, index_columns,
2512
+ comment: comment)
2513
+ index_schema << index_qualifier
2514
+ end
2515
+ rescue StandardError => e # Handle driver fetch errors
2516
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT)
2517
+ raise "Failed to retrieve index metadata during fetch: #{error_msg}" if error_msg && !error_msg.empty?
2518
+
2519
+ error_msg = 'An unexpected error occurred during retrieval of index metadata'
2520
+ error_msg += ": #{e.message}" unless e.message.empty?
2521
+ raise error_msg
2522
+ ensure # Free resources associated with the statement
2523
+ IBM_DB.free_stmt(stmt) if stmt
2524
+ end
2525
+ else # Handle driver execution errors
2526
+ error_msg = IBM_DB.getErrormsg(@connection, IBM_DB::DB_CONN)
2527
+ raise "Failed to retrieve index metadata due to error: #{error_msg}" if error_msg && !error_msg.empty?
2528
+
2529
+ raise StandardError.new('An unexpected error occurred during index retrieval')
2530
+
2531
+ end
2532
+
2533
+ # remove the primary key index entry.... should not be dumped by the dumper
2534
+
2535
+ puts_log "Indexes 1 = #{pk_index}"
2536
+ i = 0
2537
+ indexes.each do |index|
2538
+ indexes.delete_at(i) if pk_index && index.columns == pk_index.columns
2539
+ i += 1
2540
+ end
2541
+ # Returns the indexes array
2542
+ puts_log "Indexes 2 = #{indexes}"
2543
+ indexes
2544
+ end
2545
+
2546
+ # Mapping IBM data servers SQL datatypes to Ruby data types
2547
+ def simplified_type2(field_type)
2548
+ puts_log 'simplified_type2'
2549
+ case field_type
2550
+ # if +field_type+ contains 'for bit data' handle it as a binary
2551
+ when /for bit data/i
2552
+ 'binary'
2553
+ when /smallint/i
2554
+ 'boolean'
2555
+ when /int|serial/i
2556
+ 'integer'
2557
+ when /decimal|numeric|decfloat/i
2558
+ 'decimal'
2559
+ when /float|double|real/i
2560
+ 'float'
2561
+ when /timestamp|datetime/i
2562
+ 'timestamp'
2563
+ when /time/i
2564
+ 'time'
2565
+ when /date/i
2566
+ 'date'
2567
+ when /vargraphic/i
2568
+ 'vargraphic'
2569
+ when /graphic/i
2570
+ 'graphic'
2571
+ when /clob|text/i
2572
+ 'text'
2573
+ when /xml/i
2574
+ 'xml'
2575
+ when /blob|binary/i
2576
+ 'binary'
2577
+ when /char/i
2578
+ 'string'
2579
+ when /boolean/i
2580
+ 'boolean'
2581
+ when /rowid/i # rowid is a supported datatype on z/OS and i/5
2582
+ 'rowid'
2583
+ end
2584
+ end # method simplified_type
2585
+
2586
+ # Mapping IBM data servers SQL datatypes to Ruby data types
2587
+ def simplified_type(field_type)
2588
+ puts_log 'simplified_type'
2589
+ case field_type
2590
+ # if +field_type+ contains 'for bit data' handle it as a binary
2591
+ when /for bit data/i
2592
+ :binary
2593
+ when /smallint/i
2594
+ :boolean
2595
+ when /int|serial/i
2596
+ :integer
2597
+ when /decimal|numeric|decfloat/i
2598
+ :decimal
2599
+ when /float|double|real/i
2600
+ :float
2601
+ when /timestamp|datetime/i
2602
+ :datetime
2603
+ when /time/i
2604
+ :time
2605
+ when /date/i
2606
+ :date
2607
+ when /vargraphic/i
2608
+ :vargraphic
2609
+ when /graphic/i
2610
+ :graphic
2611
+ when /clob|text/i
2612
+ :text
2613
+ when /xml/i
2614
+ :xml
2615
+ when /blob|binary/i
2616
+ :binary
2617
+ when /char/i
2618
+ :string
2619
+ when /boolean/i
2620
+ :boolean
2621
+ when /rowid/i # rowid is a supported datatype on z/OS and i/5
2622
+ :rowid
2623
+ end
2624
+ end # method simplified_type
2625
+
2626
+ def extract_value_from_default(default)
2627
+ case default
2628
+ when /IDENTITY GENERATED BY DEFAULT/i
2629
+ nil
2630
+ when /^null$/i
2631
+ nil
2632
+ # Quoted types
2633
+ when /^'(.*)'$/m
2634
+ ::Regexp.last_match(1).gsub("''", "'")
2635
+ # Quoted types
2636
+ when /^"(.*)"$/m
2637
+ ::Regexp.last_match(1).gsub('""', '"')
2638
+ # Numeric types
2639
+ when /\A-?\d+(\.\d*)?\z/
2640
+ ::Regexp.last_match(0)
2641
+ else
2642
+ # Anything else is blank or some function
2643
+ # and we can't know the value of that, so return nil.
2644
+ nil
2645
+ end
2646
+ end
2647
+
2648
+ # Returns an array of Column objects for the table specified by +table_name+
2649
+ def columns(table_name)
2650
+ default_blob_length = 1048576
2651
+ # to_s required because it may be a symbol.
2652
+ puts_log "def columns #{table_name}"
2653
+ puts_log caller
2654
+ table_name = @servertype.set_case(table_name.to_s)
2655
+
2656
+ # Checks if a blank table name has been given.
2657
+ # If so it returns an empty array
2658
+ return [] if table_name.strip.empty?
2659
+
2660
+ # +columns+ will contain the resulting array
2661
+ columns = []
2662
+ # Statement required to access all the columns information
2663
+ stmt = IBM_DB.columns(@connection, nil,
2664
+ @servertype.set_case(@schema),
2665
+ @servertype.set_case(table_name))
2666
+ # sql = "select * from sysibm.sqlcolumns where table_name = #{quote(table_name.upcase)}"
2667
+ if @debug == true
2668
+ sql = "select * from syscat.columns where tabname = #{quote(table_name.upcase)}"
2669
+ puts_log "SYSIBM.SQLCOLUMNS = #{execute_without_logging(sql).rows}"
2670
+ end
2671
+
2672
+ pri_key = primary_key(table_name)
2673
+
2674
+ if stmt
2675
+ begin
2676
+ # Fetches all the columns and assigns them to col.
2677
+ # +col+ is an hash with keys/value pairs for a column
2678
+ while col = IBM_DB.fetch_assoc(stmt)
2679
+ rowid = false
2680
+ puts_log "def columns fecthed = #{col}"
2681
+ column_name = col['column_name'].downcase
2682
+ sql = "select 1 FROM syscat.columns where tabname = #{quote(table_name.upcase)} and generated = 'D' and colname = '#{col['column_name']}'"
2683
+ rows = execute_without_logging(sql).rows
2684
+ auto_increment = rows.dig(0, 0) == 1 ? true : nil
2685
+ puts_log "def columns auto_increment = #{rows}, #{auto_increment}"
2686
+
2687
+ # Assigns the column default value.
2688
+ column_default_value = col['column_def']
2689
+ default_value = extract_value_from_default(column_default_value)
2690
+ # Assigns the column type
2691
+ column_type = col['type_name'].downcase
2692
+
2693
+ if Array(pri_key).include?(column_name) and column_type =~ /integer|bigint/i
2694
+ rowid = true
2695
+ puts_log "def columns rowid = true"
2696
+ end
2697
+ # Assigns the field length (size) for the column
2698
+
2699
+ column_length = if column_type =~ /integer|bigint/i
2700
+ col['buffer_length']
2701
+ else
2702
+ col['column_size']
2703
+ end
2704
+ column_scale = col['decimal_digits']
2705
+ # The initializer of the class Column, requires the +column_length+ to be declared
2706
+ # between brackets after the datatype(e.g VARCHAR(50)) for :string and :text types.
2707
+ # If it's a "for bit data" field it does a subsitution in place, if not
2708
+ # it appends the (column_length) string on the supported data types
2709
+ if column_type.match(/decimal|numeric/)
2710
+ if column_length > 0 and column_scale > 0
2711
+ column_type << "(#{column_length},#{column_scale})"
2712
+ elsif column_length > 0 and column_scale == 0
2713
+ column_type << "(#{column_length})"
2714
+ end
2715
+ elsif column_type.match(/timestamp/)
2716
+ column_type << "(#{column_scale})"
2717
+ elsif column_type.match(/varchar/) and column_length > 0
2718
+ column_type << "(#{column_length})"
2719
+ end
2720
+
2721
+ column_nullable = col['nullable'] == 1
2722
+ # Make sure the hidden column (db2_generated_rowid_for_lobs) in DB2 z/OS isn't added to the list
2723
+ next if column_name.match(/db2_generated_rowid_for_lobs/i)
2724
+
2725
+ puts_log "Column type = #{column_type}"
2726
+ ruby_type = simplified_type(column_type)
2727
+ puts_log "Ruby type after = #{ruby_type}"
2728
+ precision = extract_precision(ruby_type)
2729
+
2730
+ if column_type.match(/timestamp|integer|bigint|date|time|blob/i)
2731
+ if column_type.match(/timestamp/i)
2732
+ precision = column_scale
2733
+ unless default_value.nil?
2734
+ default_value[10] = ' '
2735
+ default_value[13] = ':'
2736
+ default_value[16] = ':'
2737
+ end
2738
+ elsif column_type.match(/time/i)
2739
+ unless default_value.nil?
2740
+ default_value[2] = ':'
2741
+ default_value[5] = ':'
2742
+ end
2743
+ end
2744
+ column_scale = nil
2745
+ if !(column_type.match(/blob/i) and column_length != default_blob_length) and !column_type.match(/bigint/i)
2746
+ column_length = nil
2747
+ end
2748
+ elsif column_type.match(/decimal|numeric/)
2749
+ precision = column_length
2750
+ column_length = nil
2751
+ end
2752
+
2753
+ column_type = 'boolean' if ruby_type.to_s == 'boolean'
2754
+
2755
+ puts_log "Inside def columns() - default_value = #{default_value}, column_default_value = #{column_default_value}"
2756
+ default_function = extract_default_function(default_value, column_default_value)
2757
+ puts_log "Inside def columns() - default_function = #{default_function}"
2758
+
2759
+ sqltype_metadata = SqlTypeMetadata.new(
2760
+ # sql_type: sql_type,
2761
+ sql_type: column_type,
2762
+ type: ruby_type,
2763
+ limit: column_length,
2764
+ precision: precision,
2765
+ scale: column_scale
2766
+ )
2767
+
2768
+ columns << Column.new(column_name, default_value, sqltype_metadata, column_nullable, default_function,
2769
+ comment: col['remarks'], auto_increment: auto_increment, rowid: rowid)
2770
+ end
2771
+ rescue StandardError => e # Handle driver fetch errors
2772
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT)
2773
+ raise "Failed to retrieve column metadata during fetch: #{error_msg}" if error_msg && !error_msg.empty?
2774
+
2775
+ error_msg = 'An unexpected error occurred during retrieval of column metadata'
2776
+ error_msg += ": #{e.message}" unless e.message.empty?
2777
+ # raise error_msg
2778
+ ensure # Free resources associated with the statement
2779
+ IBM_DB.free_stmt(stmt) if stmt
2780
+ end
2781
+ else # Handle driver execution errors
2782
+ error_msg = IBM_DB.getErrormsg(@connection, IBM_DB::DB_CONN)
2783
+ raise "Failed to retrieve column metadata due to error: #{error_msg}" if error_msg && !error_msg.empty?
2784
+
2785
+ raise StandardError.new('An unexpected error occurred during retrieval of columns metadata')
2786
+
2787
+ end
2788
+ # Returns the columns array
2789
+ puts_log "Inside def columns() #{columns}"
2790
+ columns
2791
+ end
2792
+
2793
+ def extract_precision(sql_type)
2794
+ ::Regexp.last_match(1).to_i if sql_type =~ /\((\d+)(,\d+)?\)/
2795
+ end
2796
+
2797
+ def extract_default_function(default_value, default)
2798
+ default if has_default_function?(default_value, default)
2799
+ end
2800
+
2801
+ def has_default_function?(default_value, default)
2802
+ !default_value && /\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP/.match?(default)
2803
+ !default_value && /(\w+\(.*\)|CURRENT(?:[_\s]TIME|[_\s]DATE|[_\s]TIMESTAMP))/i.match?(default)
2804
+ end
2805
+
2806
+ def foreign_keys(table_name)
2807
+ puts_log "foreign_keys #{table_name}"
2808
+ # fetch the foreign keys of the table using function foreign_keys
2809
+ # PKTABLE_NAME:: fk_row[2] Name of the table containing the primary key.
2810
+ # PKCOLUMN_NAME:: fk_row[3] Name of the column containing the primary key.
2811
+ # FKTABLE_NAME:: fk_row[6] Name of the table containing the foreign key.
2812
+ # FKCOLUMN_NAME:: fk_row[7] Name of the column containing the foreign key.
2813
+ # FK_NAME:: fk_row[11] The name of the foreign key.
2814
+
2815
+ table_name = @servertype.set_case(table_name.to_s)
2816
+ foreignKeys = []
2817
+ fks_temp = []
2818
+ stmt = IBM_DB.foreignkeys(@connection, nil,
2819
+ @servertype.set_case(@schema),
2820
+ @servertype.set_case(table_name), 'FK_TABLE')
2821
+
2822
+ if stmt
2823
+ begin
2824
+ while (fk_row = IBM_DB.fetch_array(stmt))
2825
+ puts_log "foreign_keys fetch = #{fk_row}"
2826
+ options = {
2827
+ column: fk_row[7].downcase,
2828
+ name: fk_row[11].downcase,
2829
+ primary_key: fk_row[3].downcase
2830
+ }
2831
+ options[:on_update] = extract_foreign_key_action(fk_row[9])
2832
+ options[:on_delete] = extract_foreign_key_action(fk_row[10])
2833
+ fks_temp << ForeignKeyDefinition.new(fk_row[6].downcase, fk_row[2].downcase, options)
2834
+ end
2835
+
2836
+ fks_temp.each do |fkst|
2837
+ comb = false
2838
+ if foreignKeys.size > 0
2839
+ foreignKeys.each_with_index do |fks, ind|
2840
+ if fks.name == fkst.name
2841
+ if foreignKeys[ind].column.kind_of?(Array)
2842
+ foreignKeys[ind].column << fkst.column
2843
+ foreignKeys[ind].primary_key << fkst.primary_key
2844
+ else
2845
+ options = {
2846
+ name: fks.name,
2847
+ on_update: nil,
2848
+ on_delete: nil
2849
+ }
2850
+
2851
+ options[:column] = []
2852
+ options[:column] << fks.column
2853
+ options[:column] << fkst.column
2854
+
2855
+ options[:primary_key] = []
2856
+ options[:primary_key] << fks.primary_key
2857
+ options[:primary_key] << fkst.primary_key
2858
+
2859
+ foreignKeys[ind] = ForeignKeyDefinition.new(fks.from_table, fks.to_table, options)
2860
+ end
2861
+ comb = true
2862
+ break
2863
+ end
2864
+ end
2865
+ foreignKeys << fkst if !comb
2866
+ else
2867
+ foreignKeys << fkst
2868
+ end
2869
+ end
2870
+
2871
+ rescue StandardError => e # Handle driver fetch errors
2872
+ puts_log "foreign_keys e = #{e}"
2873
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT)
2874
+ raise "Failed to retrieve foreign key metadata during fetch: #{error_msg}" if error_msg && !error_msg.empty?
2875
+
2876
+ error_msg = 'An unexpected error occurred during retrieval of foreign key metadata'
2877
+ error_msg += ": #{e.message}" unless e.message.empty?
2878
+ # raise error_msg
2879
+ ensure # Free resources associated with the statement
2880
+ IBM_DB.free_stmt(stmt) if stmt
2881
+ end
2882
+ else # Handle driver execution errors
2883
+ error_msg = IBM_DB.getErrormsg(@connection, IBM_DB::DB_CONN)
2884
+ raise "Failed to retrieve foreign key metadata due to error: #{error_msg}" if error_msg && !error_msg.empty?
2885
+
2886
+ raise StandardError.new('An unexpected error occurred during foreign key retrieval')
2887
+
2888
+ end
2889
+ # Returns the foreignKeys array
2890
+ foreignKeys
2891
+ end
2892
+
2893
+ def extract_foreign_key_action(specifier) # :nodoc:
2894
+ puts_log 'extract_foreign_key_action'
2895
+ case specifier
2896
+ when 0 then :cascade
2897
+ when 1 then :restrict
2898
+ when 2 then :nullify
2899
+ end
2900
+ end
2901
+
2902
+ def supports_disable_referential_integrity? # :nodoc:
2903
+ true
2904
+ end
2905
+
2906
+ def disable_referential_integrity # :nodoc:
2907
+ puts_log 'disable_referential_integrity'
2908
+ alter_foreign_keys(tables, true) if supports_disable_referential_integrity?
2909
+
2910
+ yield
2911
+ ensure
2912
+ alter_foreign_keys(tables, false) if supports_disable_referential_integrity?
2913
+ end
2914
+
2915
+ def alter_foreign_keys(tables, not_enforced)
2916
+ puts_log 'alter_foreign_keys'
2917
+ enforced = not_enforced ? 'NOT ENFORCED' : 'ENFORCED'
2918
+ tables.each do |table|
2919
+ foreign_keys(table).each do |fk|
2920
+ puts_log "alter_foreign_keys fk = #{fk}"
2921
+ execute("ALTER TABLE #{@servertype.set_case(fk.from_table)} ALTER FOREIGN KEY #{@servertype.set_case(fk.name)} #{enforced}")
2922
+ end
2923
+ end
2924
+ end
2925
+
2926
+ def primary_keys(table_name) # :nodoc:
2927
+ puts_log 'primary_keys'
2928
+ raise ArgumentError unless table_name.present?
2929
+
2930
+ primary_key(table_name)
2931
+ end
2932
+
2933
+ # Adds comment for given table column or drops it if +comment+ is a +nil+
2934
+ def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
2935
+ puts_log 'change_column_comment'
2936
+ clear_cache!
2937
+ comment = extract_new_comment_value(comment_or_changes)
2938
+ if comment.nil?
2939
+ execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS ''"
2940
+ else
2941
+ execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}"
2942
+ end
2943
+ end
2944
+
2945
+ # Adds comment for given table or drops it if +comment+ is a +nil+
2946
+ def change_table_comment(table_name, comment_or_changes) # :nodoc:
2947
+ puts_log "change_table_comment table_name = #{table_name}, comment_or_changes = #{comment_or_changes}"
2948
+ clear_cache!
2949
+ comment = extract_new_comment_value(comment_or_changes)
2950
+ puts_log "change_table_comment new_comment = #{comment}"
2951
+ if comment.nil?
2952
+ execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS ''"
2953
+ else
2954
+ execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}"
2955
+ end
2956
+ end
2957
+
2958
+ def add_column(table_name, column_name, type, **options) # :nodoc:
2959
+ puts_log 'add_column'
2960
+ clear_cache!
2961
+ puts_log "add_column info #{table_name}, #{column_name}, #{type}, #{options}"
2962
+ puts_log caller
2963
+ if (!type.nil? && type.to_s == 'primary_key') or (options.key?(:primary_key) and options[:primary_key] == true)
2964
+ if !type.nil? and type.to_s != 'primary_key'
2965
+ execute "ALTER TABLE #{table_name} ADD COLUMN #{column_name} #{type} NOT NULL DEFAULT 0"
2966
+ else
2967
+ execute "ALTER TABLE #{table_name} ADD COLUMN #{column_name} INTEGER NOT NULL DEFAULT 0"
2968
+ end
2969
+ execute "ALTER TABLE #{table_name} alter column #{column_name} drop default"
2970
+ execute "ALTER TABLE #{table_name} alter column #{column_name} set GENERATED BY DEFAULT AS IDENTITY (START WITH 1000)"
2971
+ execute "ALTER TABLE #{table_name} add primary key (#{column_name})"
2972
+ else
2973
+ super
2974
+ end
2975
+ change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
2976
+ end
2977
+
2978
+ def table_comment(table_name) # :nodoc:
2979
+ puts_log "table_comment table_name = #{table_name}"
2980
+ sql = "select remarks from syscat.tables where tabname = #{quote(table_name.upcase)}"
2981
+ single_value_from_rows(execute_without_logging(sql).rows)
2982
+ end
2983
+
2984
+ def add_index(table_name, column_name, **options) # :nodoc:
2985
+ puts_log 'add_index'
2986
+ index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
2987
+
2988
+ return if if_not_exists && index_exists?(table_name, column_name, name: index.name)
2989
+
2990
+ if_not_exists = false if if_not_exists
2991
+ create_index = CreateIndexDefinition.new(index, algorithm, if_not_exists)
2992
+ result = execute schema_creation.accept(create_index)
2993
+
2994
+ execute "COMMENT ON INDEX #{quote_column_name(index.name)} IS #{quote(index.comment)}" if index.comment
2995
+ result
2996
+ end
2997
+
2998
+ def add_timestamps(table_name, **options)
2999
+ puts_log "add_timestamps #{table_name}"
3000
+ fragments = add_timestamps_for_alter(table_name, **options)
3001
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{fragments.join(' ')}"
3002
+ end
3003
+
3004
+ def query_values(sql, _name = nil) # :nodoc:
3005
+ puts_log 'query_values'
3006
+ select_prepared(sql).rows.map(&:first)
3007
+ end
3008
+
3009
+ def data_source_sql(name = nil, type: nil)
3010
+ puts_log 'data_source_sql'
3011
+ puts_log "servertype = #{@servertype}"
3012
+ if @servertype.instance_of? IBM_IDS
3013
+ sql = "SELECT tabname FROM systables WHERE"
3014
+ if type || name
3015
+ conditions = []
3016
+ conditions << "tabtype = #{quote(type.upcase)}" if type
3017
+ conditions << "tabname = #{quote(name.upcase)}" if name
3018
+ sql << " #{conditions.join(' AND ')}"
3019
+ end
3020
+ sql << " AND owner = #{quote(@schema.upcase)}"
3021
+ else
3022
+ sql = +'SELECT tabname FROM (SELECT tabname, type FROM syscat.tables '
3023
+ sql << " WHERE tabschema = #{quote(@schema.upcase)}) subquery"
3024
+ if type || name
3025
+ conditions = []
3026
+ conditions << "subquery.type = #{quote(type.upcase)}" if type
3027
+ conditions << "subquery.tabname = #{quote(name.upcase)}" if name
3028
+ sql << " WHERE #{conditions.join(' AND ')}"
3029
+ end
3030
+ end
3031
+ sql
3032
+ end
3033
+
3034
+ # Returns an array of table names defined in the database.
3035
+ def tables
3036
+ puts_log 'tables'
3037
+ query_values(data_source_sql(type: 'T'), 'SCHEMA').map(&:downcase)
3038
+ end
3039
+
3040
+ # Checks to see if the table +table_name+ exists on the database.
3041
+ #
3042
+ # table_exists?(:developers)
3043
+ #
3044
+ def table_exists?(table_name)
3045
+ puts_log "table_exists? = #{table_name}"
3046
+ query_values(data_source_sql(table_name, type: 'T'), 'SCHEMA').any? if table_name.present?
3047
+ rescue NotImplementedError
3048
+ tables.include?(table_name.to_s)
3049
+ end
3050
+
3051
+ # Returns an array of view names defined in the database.
3052
+ def views
3053
+ puts_log 'views'
3054
+ query_values(data_source_sql(type: 'V'), 'SCHEMA').map(&:downcase)
3055
+ end
3056
+
3057
+ # Checks to see if the view +view_name+ exists on the database.
3058
+ #
3059
+ # view_exists?(:ebooks)
3060
+ #
3061
+ def view_exists?(view_name)
3062
+ puts_log 'view_exists?'
3063
+ query_values(data_source_sql(view_name, type: 'V'), 'SCHEMA').any? if view_name.present?
3064
+ rescue NotImplementedError
3065
+ views.include?(view_name.to_s)
3066
+ end
3067
+
3068
+ # Returns the relation names useable to back Active Record models.
3069
+ # For most adapters this means all #tables and #views.
3070
+ def data_sources
3071
+ puts_log 'data_sources'
3072
+ query_values(data_source_sql, 'SCHEMA').map(&:downcase)
3073
+ rescue NotImplementedError
3074
+ tables | views
3075
+ end
3076
+
3077
+ def create_schema_dumper(options)
3078
+ puts_log 'create_schema_dumper'
3079
+ SchemaDumper.create(self, options)
3080
+ end
3081
+
3082
+ def table_options(table_name) # :nodoc:
3083
+ puts_log 'table_options'
3084
+ return unless comment = table_comment(table_name)
3085
+
3086
+ { comment: comment }
3087
+ end
3088
+
3089
+ def remove_columns(table_name, *column_names, type: nil, **options)
3090
+ if column_names.empty?
3091
+ raise ArgumentError.new('You must specify at least one column name. Example: remove_columns(:people, :first_name)')
3092
+ end
3093
+
3094
+ remove_column_fragments = remove_columns_for_alter(table_name, *column_names, type: type, **options)
3095
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_fragments.join(' ')}"
3096
+ end
3097
+
3098
+ # Renames a table.
3099
+ # ==== Example
3100
+ # rename_table('octopuses', 'octopi')
3101
+ # Overriden to satisfy IBM data servers syntax
3102
+ def rename_table(name, new_name, **options)
3103
+ puts_log "rename_table name = #{name}, new_name = #{new_name}"
3104
+ validate_table_length!(new_name) unless options[:_uses_legacy_table_name]
3105
+ clear_cache!
3106
+ schema_cache.clear_data_source_cache!(name.to_s)
3107
+ schema_cache.clear_data_source_cache!(new_name.to_s)
3108
+ name = quote_column_name(name)
3109
+ new_name = quote_column_name(new_name)
3110
+ puts_log "90 old_table = #{name}, new_table = #{new_name}"
3111
+ # SQL rename table statement
3112
+ index_list = indexes(name)
3113
+ puts_log "Index List = #{index_list}"
3114
+ drop_table_indexes(index_list)
3115
+ rename_table_sql = "RENAME TABLE #{name} TO #{new_name}"
3116
+ stmt = execute(rename_table_sql)
3117
+ create_table_indexes(index_list, new_name)
3118
+ # Ensures to free the resources associated with the statement
3119
+ ensure
3120
+ IBM_DB.free_stmt(stmt) if stmt
3121
+ end
3122
+
3123
+ def add_reference(table_name, ref_name, **options) # :nodoc:
3124
+ puts_log "add_reference table_name = #{table_name}, ref_name = #{ref_name}"
3125
+ super(table_name, ref_name, type: :integer, **options)
3126
+ end
3127
+ alias :add_belongs_to :add_reference
3128
+
3129
+ def drop_table_indexes(index_list)
3130
+ puts_log "drop_table_indexes index_list = #{index_list}"
3131
+ index_list.each do |indexs|
3132
+ remove_index(indexs.table, name: indexs.name)
3133
+ end
3134
+ end
3135
+
3136
+ def create_table_indexes(index_list, new_table)
3137
+ puts_log "create_table_indexes index_list = #{index_list}, new_table = #{new_table}"
3138
+ index_list.each do |indexs|
3139
+ generated_index_name = index_name(indexs.table, column: indexs.columns)
3140
+ custom_index_name = indexs.name
3141
+
3142
+ if generated_index_name == custom_index_name
3143
+ add_index(new_table, indexs.columns, unique: indexs.unique)
3144
+ else
3145
+ add_index(new_table, indexs.columns, name: custom_index_name, unique: indexs.unique)
3146
+ end
3147
+ end
3148
+ end
3149
+
3150
+ def drop_column_indexes(index_list, column_name)
3151
+ puts_log 'drop_column_indexes'
3152
+ index_list.each do |indexs|
3153
+ if indexs.columns.class == Array
3154
+ next unless indexs.columns.include?(column_name)
3155
+ elsif indexs.columns != column_name
3156
+ next
3157
+ end
3158
+ remove_index(indexs.table, name: indexs.name)
3159
+ end
3160
+ end
3161
+
3162
+ def create_column_indexes(index_list, column_name, new_column_name)
3163
+ puts_log 'create_column_indexes'
3164
+ index_list.each do |indexs|
3165
+ generated_index_name = index_name(indexs.table, column: indexs.columns)
3166
+ custom_index_name = indexs.name
3167
+ if indexs.columns.class == Array
3168
+ next unless indexs.columns.include?(column_name)
3169
+
3170
+ indexs.columns[indexs.columns.index(column_name)] = new_column_name
3171
+ else
3172
+ next if indexs.columns != column_name
3173
+
3174
+ indexs.columns = new_column_name
3175
+ end
3176
+
3177
+ if generated_index_name == custom_index_name
3178
+ add_index(indexs.table, indexs.columns, unique: indexs.unique)
3179
+ else
3180
+ add_index(indexs.table, indexs.columns, name: custom_index_name, unique: indexs.unique)
3181
+ end
3182
+ end
3183
+ end
3184
+
3185
+ # Renames a column in a table.
3186
+ def rename_column(table_name, column_name, new_column_name) # :nodoc:
3187
+ puts_log 'rename_column'
3188
+ column_name = quote_column_name(column_name)
3189
+ new_column_name = quote_column_name(new_column_name)
3190
+ puts_log "rename_column #{table_name}, #{column_name}, #{new_column_name}"
3191
+ clear_cache!
3192
+ unique_indexes = unique_constraints(table_name)
3193
+ puts_log "rename_column Unique Indexes = #{unique_indexes}"
3194
+ remove_unique_constraint_byColumn(unique_indexes)
3195
+ index_list = indexes(table_name)
3196
+ puts_log "rename_column Index List = #{index_list}"
3197
+ fkey_list = foreign_keys(table_name)
3198
+ puts_log "rename_column ForeignKey = #{fkey_list}"
3199
+ drop_column_indexes(index_list, column_name)
3200
+ fkey_removed = remove_foreign_key_byColumn(fkey_list, table_name, column_name)
3201
+ execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name,
3202
+ new_column_name)}")
3203
+ add_unique_constraint_byColumn(unique_indexes, new_column_name)
3204
+ add_foreign_keyList(fkey_list, table_name, column_name, new_column_name) if fkey_removed
3205
+ create_column_indexes(index_list, column_name, new_column_name)
3206
+ end
3207
+
3208
+ def add_unique_constraint_byColumn(unique_indexes, new_column_name)
3209
+ puts_log "add_unique_constraint_byColumn = #{unique_indexes}"
3210
+ unique_indexes.each do |unq|
3211
+ add_unique_constraint(unq.table_name, new_column_name, name: unq.name)
3212
+ end
3213
+ end
3214
+
3215
+ def remove_unique_constraint_byColumn(unique_indexes)
3216
+ puts_log "remove_unique_constraint_byColumn = #{unique_indexes}"
3217
+ unique_indexes.each do |unq|
3218
+ remove_unique_constraint(unq.table_name, unq.column, name: unq.name)
3219
+ end
3220
+ end
3221
+
3222
+ def add_foreign_keyList(fkey_list, table_name, column_name, new_column_name)
3223
+ puts_log "add_foreign_keyList = #{table_name}, #{column_name}, #{fkey_list}"
3224
+ fkey_list.each do |fkey|
3225
+ if fkey.options[:column] == column_name
3226
+ add_foreign_key(table_name, strip_table_name_prefix_and_suffix(fkey.to_table), column: new_column_name)
3227
+ end
3228
+ end
3229
+ end
3230
+
3231
+ def remove_foreign_key_byColumn(fkey_list, table_name, column_name)
3232
+ puts_log "remove_foreign_key_byColumn = #{table_name}, #{column_name}, #{fkey_list}"
3233
+ fkey_removed = false
3234
+ fkey_list.each do |fkey|
3235
+ if fkey.options[:column] == column_name
3236
+ remove_foreign_key(table_name, column: column_name)
3237
+ fkey_removed = true
3238
+ end
3239
+ end
3240
+ fkey_removed
3241
+ end
3242
+
3243
+ def rename_index(table_name, old_name, new_name)
3244
+ puts_log 'rename_index'
3245
+ old_name = old_name.to_s
3246
+ new_name = new_name.to_s
3247
+ validate_index_length!(table_name, new_name)
3248
+
3249
+ # this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
3250
+ old_index_def = indexes(table_name).detect { |i| i.name == old_name }
3251
+ return unless old_index_def
3252
+
3253
+ remove_index(table_name, name: old_name)
3254
+ add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
3255
+ end
3256
+
3257
+ # Removes the column from the table definition.
3258
+ # ===== Examples
3259
+ # remove_column(:suppliers, :qualification)
3260
+ def remove_column(table_name, column_name, _type = nil, **options)
3261
+ puts_log 'remove_column'
3262
+ return if options[:if_exists] == true && !column_exists?(table_name, column_name)
3263
+
3264
+ @servertype.remove_column(table_name, column_name)
3265
+ end
3266
+
3267
+ # Changes the column's definition according to the new options.
3268
+ # See TableDefinition#column for details of the options you can use.
3269
+ # ===== Examples
3270
+ # change_column(:suppliers, :name, :string, :limit => 80)
3271
+ # change_column(:accounts, :description, :text)
3272
+ def change_column(table_name, column_name, type, options = {})
3273
+ puts_log 'change_column'
3274
+ @servertype.change_column(table_name, column_name, type, options)
3275
+ change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
3276
+ end
3277
+
3278
+ # Add distinct clause to the sql if there is no order by specified
3279
+ def distinct(columns, order_by)
3280
+ puts_log 'distinct'
3281
+ if order_by.nil?
3282
+ "DISTINCT #{columns}"
3283
+ else
3284
+ "#{columns}"
3285
+ end
3286
+ end
3287
+
3288
+ # Sets a new default value for a column. This does not set the default
3289
+ # value to +NULL+, instead, it needs DatabaseStatements#execute which
3290
+ # can execute the appropriate SQL statement for setting the value.
3291
+ # ==== Examples
3292
+ # change_column_default(:suppliers, :qualification, 'new')
3293
+ # change_column_default(:accounts, :authorized, 1)
3294
+ # Method overriden to satisfy IBM data servers syntax.
3295
+ def change_column_default(table_name, column_name, default)
3296
+ puts_log 'change_column_default'
3297
+ @servertype.change_column_default(table_name, column_name, default)
3298
+ end
3299
+
3300
+ # Changes the nullability value of a column
3301
+ def change_column_null(table_name, column_name, null, default = nil)
3302
+ puts_log 'change_column_null'
3303
+ validate_change_column_null_argument!(null)
3304
+ @servertype.change_column_null(table_name, column_name, null, default)
3305
+ end
3306
+
3307
+ def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
3308
+ column = column_for(table_name, column_name)
3309
+ return unless column
3310
+
3311
+ default = extract_new_default_value(default_or_changes)
3312
+ ChangeColumnDefaultDefinition.new(column, default)
3313
+ end
3314
+
3315
+ def build_change_column_definition(table_name, column_name, type, **options) # :nodoc:
3316
+ column = column_for(table_name, column_name)
3317
+ type ||= column.sql_type
3318
+
3319
+ unless options.key?(:default)
3320
+ options[:default] = column.default
3321
+ end
3322
+
3323
+ unless options.key?(:null)
3324
+ options[:null] = column.null
3325
+ end
3326
+
3327
+ unless options.key?(:comment)
3328
+ options[:comment] = column.comment
3329
+ end
3330
+
3331
+ if options[:collation] == :no_collation
3332
+ options.delete(:collation)
3333
+ else
3334
+ options[:collation] ||= column.collation if text_type?(type)
3335
+ end
3336
+
3337
+ unless options.key?(:auto_increment)
3338
+ options[:auto_increment] = column.auto_increment?
3339
+ end
3340
+
3341
+ td = create_table_definition(table_name)
3342
+ cd = td.new_column_definition(column.name, type, **options)
3343
+ ChangeColumnDefinition.new(cd, column.name)
3344
+ end
3345
+
3346
+ def text_type?(type)
3347
+ TYPE_MAP.lookup(type).is_a?(Type::String) || TYPE_MAP.lookup(type).is_a?(Type::Text)
3348
+ end
3349
+
3350
+ def quote_schema_name(schema_name)
3351
+ quote_table_name(schema_name)
3352
+ end
3353
+
3354
+ # Creates a schema for the given schema name.
3355
+ def create_schema(schema_name, force: nil, if_not_exists: nil)
3356
+ puts_log "create_schema #{schema_name}"
3357
+ drop_schema(schema_name, if_exists: true)
3358
+
3359
+ execute("CREATE SCHEMA #{quote_schema_name(schema_name)}")
3360
+ end
3361
+
3362
+ # Drops the schema for the given schema name.
3363
+ def drop_schema(schema_name, **options)
3364
+ puts_log "drop_schema = #{schema_name}"
3365
+ schema_list = internal_exec_query("select schemaname from syscat.schemata where schemaname=#{quote(schema_name.upcase)}", "SCHEMA")
3366
+ puts_log "drop_schema schema_list = #{schema_list.columns}, #{schema_list.rows}"
3367
+ execute("DROP SCHEMA #{quote_schema_name(schema_name)} RESTRICT") if schema_list.rows.size > 0
3368
+ end
3369
+
3370
+ def add_unique_constraint(table_name, column_name = nil, **options)
3371
+ puts_log "add_unique_constraint = #{table_name}, #{column_name}, #{options}"
3372
+ options = unique_constraint_options(table_name, column_name, options)
3373
+ at = create_alter_table(table_name)
3374
+ at.add_unique_constraint(column_name, options)
3375
+
3376
+ execute schema_creation.accept(at)
3377
+ end
3378
+
3379
+ def unique_constraint_options(table_name, column_name, options) # :nodoc:
3380
+ assert_valid_deferrable(options[:deferrable])
3381
+
3382
+ if column_name && options[:using_index]
3383
+ raise ArgumentError, "Cannot specify both column_name and :using_index options."
3384
+ end
3385
+
3386
+ options = options.dup
3387
+ options[:name] ||= unique_constraint_name(table_name, column: column_name, **options)
3388
+ options
3389
+ end
3390
+
3391
+ # Returns an array of unique constraints for the given table.
3392
+ # The unique constraints are represented as UniqueConstraintDefinition objects.
3393
+ def unique_constraints(table_name)
3394
+ puts_log "unique_constraints table_name = #{table_name}"
3395
+ puts_log "unique_constraints #{caller}"
3396
+ table_name = table_name.to_s
3397
+ if table_name.include?(".")
3398
+ schema_name, table_name = table_name.split(".")
3399
+ puts_log "unique_constraints split schema_name = #{schema_name}, table_name = #{table_name}"
3400
+ else
3401
+ schema_name = @schema
3402
+ end
3403
+ unique_info = internal_exec_query(<<~SQL, "SCHEMA")
3404
+ SELECT KEYCOL.CONSTNAME, KEYCOL.COLNAME FROM SYSCAT.KEYCOLUSE KEYCOL
3405
+ INNER JOIN SYSCAT.TABCONST TABCONST ON KEYCOL.CONSTNAME=TABCONST.CONSTNAME
3406
+ WHERE TABCONST.TABSCHEMA=#{quote(schema_name.upcase)} and
3407
+ TABCONST.TABNAME=#{quote(table_name.upcase)} and TABCONST.TYPE='U'
3408
+ SQL
3409
+
3410
+ puts_log "unique_constraints unique_info = #{unique_info.columns}, #{unique_info.rows}"
3411
+ unique_info.map do |row|
3412
+ puts_log "unique_constraints row = #{row}"
3413
+ columns = []
3414
+ columns << row["colname"].downcase
3415
+
3416
+ options = {
3417
+ name: row["constname"].downcase,
3418
+ deferrable: false
3419
+ }
3420
+
3421
+ UniqueConstraintDefinition.new(table_name, columns, options)
3422
+ end
3423
+ end
3424
+
3425
+ def remove_unique_constraint(table_name, column_name = nil, **options)
3426
+ puts_log "remove_unique_constraint table_name = #{table_name}, column_name = #{column_name}, options = #{options}"
3427
+ unique_name_to_delete = unique_constraint_for!(table_name, column: column_name, **options).name
3428
+
3429
+ puts_log "remove_unique_constraint unique_name_to_delete = #{unique_name_to_delete}"
3430
+ at = create_alter_table(table_name)
3431
+ at.drop_unique_constraint(unique_name_to_delete)
3432
+
3433
+ execute schema_creation.accept(at)
3434
+ end
3435
+
3436
+ def unique_constraint_name(table_name, **options)
3437
+ puts_log "unique_constraint_name table_name = #{table_name}, options = #{options}"
3438
+ options.fetch(:name) do
3439
+ column_or_index = Array(options[:column] || options[:using_index]).map(&:to_s)
3440
+ identifier = "#{table_name}_#{column_or_index * '_and_'}_unique"
3441
+ hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
3442
+
3443
+ "uniq_rails_#{hashed_identifier}"
3444
+ end
3445
+ end
3446
+
3447
+ def unique_constraint_for(table_name, **options)
3448
+ puts_log "unique_constraint_for table_name = #{table_name}, options = #{options}"
3449
+ name = unique_constraint_name(table_name, **options)
3450
+ puts_log "unique_constraint_for name = #{name}"
3451
+ uq = unique_constraints(table_name)
3452
+ puts_log "unique_constraint_for unique_constraints = #{uq}"
3453
+ uq.detect { |unique_constraint| unique_constraint.defined_for?(name: name) }
3454
+ end
3455
+
3456
+ def unique_constraint_for!(table_name, column: nil, **options)
3457
+ puts_log "unique_constraint_for! table_name = #{table_name}, column = #{column}, options = #{options}"
3458
+ unique_constraint_for(table_name, column: column, **options) ||
3459
+ raise(ArgumentError, "Table '#{table_name}' has no unique constraint for #{column || options}")
3460
+ end
3461
+
3462
+ def foreign_key_name(table_name, options)
3463
+ puts_log "foreign_key_name table_name = #{table_name}, options = #{options}"
3464
+ options.fetch(:name) do
3465
+ columns = Array(options.fetch(:column)).map(&:to_s)
3466
+ identifier = "#{table_name}_#{columns * '_and_'}_fk"
3467
+ hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
3468
+
3469
+ "fk_rails_#{hashed_identifier}"
3470
+ end
3471
+ end
3472
+
3473
+ def foreign_key_for(from_table, **options)
3474
+ puts_log "foreign_key_for from_table = #{from_table}, options = #{options}"
3475
+ return unless use_foreign_keys?
3476
+ fks = foreign_keys(from_table)
3477
+ puts_log "foreign_key_for fks = #{fks}"
3478
+ if options.key?(:column) && options.key?(:to_table) && options[:to_table] != nil
3479
+ name = foreign_key_name(from_table, options)
3480
+ puts_log "foreign_key_for name = #{options}"
3481
+ fks.detect { |fk| fk.defined_for?(name: name) }
3482
+ else
3483
+ fks.detect { |fk| fk.defined_for?(**options) }
3484
+ end
3485
+ end
3486
+
3487
+ def foreign_key_for!(from_table, to_table: nil, **options)
3488
+ puts_log "foreign_key_for! from_table = #{from_table}, to_table = #{to_table}, options = #{options}"
3489
+ foreign_key_for(from_table, to_table: to_table, **options) ||
3490
+ raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
3491
+ end
3492
+
3493
+ def foreign_key_exists?(from_table, to_table = nil, **options)
3494
+ puts_log "foreign_key_exists? from_table = #{from_table}, to_table = #{to_table}, options = #{options}"
3495
+ foreign_key_for(from_table, to_table: to_table, **options).present?
3496
+ end
3497
+
3498
+ def remove_foreign_key(from_table, to_table = nil, **options)
3499
+ puts_log "remove_foreign_key from_table = #{from_table}, to_table = #{to_table}, options = #{options}"
3500
+ #to_table ||= options[:to_table]
3501
+ return unless use_foreign_keys?
3502
+ #return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table, **options.slice(:column))
3503
+ return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table)
3504
+
3505
+ fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
3506
+ puts_log "remove_foreign_key fk_name_to_delete = #{fk_name_to_delete}"
3507
+
3508
+ at = create_alter_table from_table
3509
+ at.drop_foreign_key fk_name_to_delete
3510
+
3511
+ execute schema_creation.accept(at)
3512
+ end
3513
+
3514
+ def create_table_definition(name, **options)
3515
+ puts_log "create_table_definition name = #{name}"
3516
+ puts_log caller
3517
+ IBM_DBAdapter::TableDefinition.new(self, name, **options)
3518
+ end
3519
+
3520
+ def create_alter_table(name)
3521
+ puts_log "create_alter_table name = #{name}"
3522
+ IBM_DBAdapter::AlterTable.new create_table_definition(name)
3523
+ end
3524
+
3525
+ def assert_valid_deferrable(deferrable)
3526
+ return if !deferrable || %i(immediate deferred).include?(deferrable)
3527
+
3528
+ raise ArgumentError, "deferrable must be `:immediate` or `:deferred`, got: `#{deferrable.inspect}`"
3529
+ end
3530
+
3531
+ def remove_index(table_name, column_name = nil, **options)
3532
+ puts_log "remove_index table_name = #{table_name}, column_name = #{column_name}, options = #{options}"
3533
+ return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
3534
+
3535
+ execute("DROP INDEX #{index_name_for_remove(table_name, column_name, options)}")
3536
+ end
3537
+
3538
+ def column_for(table_name, column_name)
3539
+ super
3540
+ end
3541
+
3542
+ protected
3543
+
3544
+ def initialize_type_map(m = type_map) # :nodoc:
3545
+ puts_log 'initialize_type_map'
3546
+ register_class_with_limit m, /boolean/i, Type::Boolean
3547
+ register_class_with_limit m, /char/i, Type::String
3548
+ register_class_with_limit m, /binary/i, Type::Binary
3549
+ register_class_with_limit m, /text/i, Type::Text
3550
+ register_class_with_precision m, /date/i, Type::Date
3551
+ register_class_with_precision m, /time/i, Type::Time
3552
+ register_class_with_precision m, /datetime/i, Type::DateTime
3553
+ register_class_with_limit m, /float/i, Type::Float
3554
+
3555
+ m.register_type(/^bigint/i, Type::Integer.new(limit: 8))
3556
+ m.register_type(/^int/i, Type::Integer.new(limit: 4))
3557
+ m.register_type(/^smallint/i, Type::Integer.new(limit: 2))
3558
+ m.register_type(/^tinyint/i, Type::Integer.new(limit: 1))
3559
+
3560
+ m.alias_type(/blob/i, 'binary')
3561
+ m.alias_type(/clob/i, 'text')
3562
+ m.alias_type(/timestamp/i, 'datetime')
3563
+ m.alias_type(/numeric/i, 'decimal')
3564
+ m.alias_type(/number/i, 'decimal')
3565
+ m.alias_type(/double/i, 'float')
3566
+
3567
+ m.register_type(/decimal/i) do |sql_type|
3568
+ scale = extract_scale(sql_type)
3569
+ precision = extract_precision(sql_type)
3570
+
3571
+ if scale == 0
3572
+ # FIXME: Remove this class as well
3573
+ Type::DecimalWithoutScale.new(precision: precision)
3574
+ else
3575
+ Type::Decimal.new(precision: precision, scale: scale)
3576
+ end
3577
+ end
3578
+
3579
+ m.alias_type(/xml/i, 'text')
3580
+ m.alias_type(/for bit data/i, 'binary')
3581
+ m.alias_type(/serial/i, 'int')
3582
+ m.alias_type(/decfloat/i, 'decimal')
3583
+ m.alias_type(/real/i, 'decimal')
3584
+ m.alias_type(/graphic/i, 'binary')
3585
+ m.alias_type(/rowid/i, 'int')
3586
+ end
3587
+
3588
+ class SchemaDumper < ConnectionAdapters::SchemaDumper
3589
+ def dump(stream) # Like in abstract class, we no need to call header() & trailer().
3590
+ header(stream)
3591
+ extensions(stream)
3592
+ tables(stream)
3593
+ stream
3594
+ end
3595
+ end
3596
+ end # class IBM_DBAdapter
3597
+
3598
+ # This class contains common code across DB's (DB2 LUW, zOS, i5 and IDS)
3599
+ class IBM_DataServer
3600
+ def initialize(adapter, ar3)
3601
+ @adapter = adapter
3602
+ @isAr3 = ar3
3603
+ end
3604
+
3605
+ def last_generated_id(stmt); end
3606
+
3607
+ def create_index_after_table(table_name, cloumn_name); end
3608
+
3609
+ def setup_for_lob_table; end
3610
+
3611
+ def reorg_table(table_name); end
3612
+
3613
+ def check_reserved_words(col_name)
3614
+ @adapter.puts_log 'check_reserved_words'
3615
+ col_name.to_s
3616
+ end
3617
+
3618
+ # This is supported by the DB2 for Linux, UNIX, Windows data servers
3619
+ # and by the DB2 for i5 data servers
3620
+ def remove_column(table_name, column_name)
3621
+ @adapter.puts_log 'remove_column'
3622
+ begin
3623
+ @adapter.execute "ALTER TABLE #{table_name} DROP #{column_name}"
3624
+ reorg_table(table_name)
3625
+ rescue StandardError => e
3626
+ # Provide details on the current XML columns support
3627
+ raise "#{e}" unless e.message.include?('SQLCODE=-1242') && e.message.include?('42997')
3628
+
3629
+ raise StatementInvalid,
3630
+ "A column that is part of a table containing an XML column cannot be dropped. \
3631
+ To remove the column, the table must be dropped and recreated without the #{column_name} column: #{e}"
3632
+ end
3633
+ end
3634
+
3635
+ def select(stmt)
3636
+ @adapter.puts_log 'select'
3637
+ results = []
3638
+ # Fetches all the results available. IBM_DB.fetch_assoc(stmt) returns
3639
+ # an hash for each single record.
3640
+ # The loop stops when there aren't any more valid records to fetch
3641
+ begin
3642
+ if @isAr3
3643
+ while single_hash = IBM_DB.fetch_assoc(stmt)
3644
+ # Add the record to the +results+ array
3645
+ results << single_hash
3646
+ end
3647
+ else
3648
+ while single_hash = IBM_DB.fetch_array(stmt)
3649
+ # Add the record to the +results+ array
3650
+ results << single_hash
3651
+ end
3652
+ end
3653
+ rescue StandardError => e # Handle driver fetch errors
3654
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT)
3655
+ raise StatementInvalid, "Failed to retrieve data: #{error_msg}" if error_msg && !error_msg.empty?
3656
+
3657
+ error_msg = 'An unexpected error occurred during data retrieval'
3658
+ error_msg += ": #{e.message}" unless e.message.empty?
3659
+ raise error_msg
3660
+ end
3661
+ results
3662
+ end
3663
+
3664
+ def select_rows(_sql, _name, stmt, results)
3665
+ @adapter.puts_log 'select_rows'
3666
+ # Fetches all the results available. IBM_DB.fetch_array(stmt) returns
3667
+ # an array representing a row in a result set.
3668
+ # The loop stops when there aren't any more valid records to fetch
3669
+ begin
3670
+ while single_array = IBM_DB.fetch_array(stmt)
3671
+ # Add the array to results array
3672
+ results << single_array
3673
+ end
3674
+ rescue StandardError => e # Handle driver fetch errors
3675
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT)
3676
+ raise StatementInvalid, "Failed to retrieve data: #{error_msg}" if error_msg && !error_msg.empty?
3677
+
3678
+ error_msg = 'An unexpected error occurred during data retrieval'
3679
+ error_msg += ": #{e.message}" unless e.message.empty?
3680
+ raise error_msg
3681
+ end
3682
+ results
3683
+ end
3684
+
3685
+ # Praveen
3686
+ def prepare(sql, _name = nil)
3687
+ @adapter.puts_log 'prepare'
3688
+ begin
3689
+ stmt = IBM_DB.prepare(@adapter.connection, sql)
3690
+ raise StatementInvalid, IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN) unless stmt
3691
+
3692
+ stmt
3693
+ rescue StandardError => e
3694
+ raise "Failed to prepare sql #{sql} due to: #{e}" if e && !e.message.empty?
3695
+
3696
+ raise 'An unexpected error occurred during SQLprepare'
3697
+ end
3698
+ end
3699
+
3700
+ # Akhil Tcheck for if_exits added so that it will try to drop even if the table does not exit.
3701
+ def execute(sql, _name = nil)
3702
+ @adapter.puts_log "IBM_DataServer execute #{sql} #{Thread.current}"
3703
+
3704
+ begin
3705
+ if @adapter.connection.nil? || @adapter.connection == false
3706
+ raise ActiveRecord::ConnectionNotEstablished, 'called on a closed database'
3707
+ elsif stmt = IBM_DB.exec(@adapter.connection, sql)
3708
+ stmt # Return the statement object
3709
+ else
3710
+ raise StatementInvalid, IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN), sql
3711
+ end
3712
+ rescue StandardError => e
3713
+ raise unless e && !e.message.empty?
3714
+
3715
+ @adapter.puts_log "104 error = #{e.message}"
3716
+ @adapter.puts_log "104 sql = #{sql}"
3717
+ raise StatementInvalid
3718
+ end
3719
+ end
3720
+
3721
+ def set_schema(schema)
3722
+ @adapter.puts_log 'set_schema'
3723
+ @adapter.execute("SET SCHEMA #{schema}")
3724
+ end
3725
+
3726
+ def get_datetime_mapping; end
3727
+
3728
+ def get_time_mapping; end
3729
+
3730
+ def get_double_mapping; end
3731
+
3732
+ def change_column_default(table_name, column_name, default); end
3733
+
3734
+ def change_column_null(table_name, column_name, null, default); end
3735
+
3736
+ def set_binary_default(value); end
3737
+
3738
+ def set_binary_value; end
3739
+
3740
+ def set_text_default; end
3741
+
3742
+ def set_case(value); end
3743
+
3744
+ def limit_not_supported_types
3745
+ %i[integer double date time timestamp xml bigint]
3746
+ end
3747
+ end # class IBM_DataServer
3748
+
3749
+ class IBM_DB2 < IBM_DataServer
3750
+ def initialize(adapter, ar3)
3751
+ super(adapter, ar3)
3752
+ @limit = @offset = nil
3753
+ end
3754
+
3755
+ def rename_column(_table_name, _column_name, _new_column_name)
3756
+ @adapter.puts_log 'rename_column'
3757
+ raise NotImplementedError, 'rename_column is not implemented yet in the IBM_DB Adapter'
3758
+ end
3759
+
3760
+ def primary_key_definition(start_id)
3761
+ @adapter.puts_log 'primary_key_definition'
3762
+ "INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH #{start_id})"
3763
+ end
3764
+
3765
+ # Returns the last automatically generated ID.
3766
+ # This method is required by the +insert+ method
3767
+ # The "stmt" parameter is ignored for DB2 but used for IDS
3768
+ def last_generated_id(stmt)
3769
+ # Queries the db to obtain the last ID that was automatically generated
3770
+ @adapter.puts_log 'last_generated_id'
3771
+ sql = 'SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1'
3772
+ stmt = IBM_DB.prepare(@adapter.connection, sql)
3773
+ if stmt
3774
+ if IBM_DB.execute(stmt, nil)
3775
+ begin
3776
+ # Fetches the only record available (containing the last id)
3777
+ IBM_DB.fetch_row(stmt)
3778
+ # Retrieves and returns the result of the query with the last id.
3779
+ id_value = IBM_DB.result(stmt, 0)
3780
+ id_value.to_i
3781
+ rescue StandardError => e # Handle driver fetch errors
3782
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT)
3783
+ raise "Failed to retrieve last generated id: #{error_msg}" if error_msg && !error_msg.empty?
3784
+
3785
+ error_msg = 'An unexpected error occurred during retrieval of last generated id'
3786
+ error_msg += ": #{e.message}" unless e.message.empty?
3787
+ raise error_msg
3788
+ ensure # Free resources associated with the statement
3789
+ IBM_DB.free_stmt(stmt) if stmt
3790
+ end
3791
+ else
3792
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT)
3793
+ IBM_DB.free_stmt(stmt) if stmt
3794
+ raise "Failed to retrieve last generated id: #{error_msg}" if error_msg && !error_msg.empty?
3795
+
3796
+ error_msg = 'An unexpected error occurred during retrieval of last generated id'
3797
+ raise error_msg
3798
+
3799
+ end
3800
+ else
3801
+ error_msg = IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN)
3802
+ raise "Failed to retrieve last generated id due to error: #{error_msg}" if error_msg && !error_msg.empty?
3803
+
3804
+ raise StandardError.new('An unexpected error occurred during retrieval of last generated id')
3805
+
3806
+ end
3807
+ end
3808
+
3809
+ def change_column(table_name, column_name, type, options)
3810
+ @adapter.puts_log "change_column #{table_name}, #{column_name}, #{type}"
3811
+ column = @adapter.column_for(table_name, column_name)
3812
+ data_type = @adapter.type_to_sql(type, options[:limit], options[:precision], options[:scale])
3813
+
3814
+ if column.sql_type != data_type
3815
+ begin
3816
+ execute "ALTER TABLE #{table_name} ALTER #{column_name} SET DATA TYPE #{data_type}"
3817
+ rescue StandardError => e
3818
+ raise "#{e}" unless e.message.include?('SQLCODE=-190')
3819
+
3820
+ raise StatementInvalid,
3821
+ "Please consult documentation for compatible data types while changing column datatype. \
3822
+ The column datatype change to [#{data_type}] is not supported by this data server: #{e}"
3823
+ end
3824
+ reorg_table(table_name)
3825
+ end
3826
+
3827
+ change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
3828
+ return unless options.key?(:null)
3829
+
3830
+ change_column_null(table_name, column_name, options[:null], nil)
3831
+ end
3832
+
3833
+ def extract_new_default_value(default_or_changes)
3834
+ @adapter.puts_log 'extract_new_default_value'
3835
+ if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
3836
+ default_or_changes[:to]
3837
+ else
3838
+ default_or_changes
3839
+ end
3840
+ end
3841
+
3842
+ # DB2 specific ALTER TABLE statement to add a default clause
3843
+ def change_column_default(table_name, column_name, default)
3844
+ @adapter.puts_log "change_column_default #{table_name} #{column_name}"
3845
+ @adapter.puts_log "Default: #{default}"
3846
+
3847
+ default = extract_new_default_value(default)
3848
+ # SQL statement which alters column's default value
3849
+ change_column_sql = if default.nil?
3850
+ "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET WITH DEFAULT NULL"
3851
+ else
3852
+ "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET WITH DEFAULT #{@adapter.quote(default)}"
3853
+ end
3854
+
3855
+ stmt = execute(change_column_sql)
3856
+ reorg_table(table_name)
3857
+ ensure
3858
+ IBM_DB.free_stmt(stmt) if stmt
3859
+ end
3860
+
3861
+ # DB2 specific ALTER TABLE statement to change the nullability of a column
3862
+ def change_column_null(table_name, column_name, null, default)
3863
+ @adapter.puts_log "change_column_null #{table_name} #{column_name}"
3864
+ change_column_default(table_name, column_name, default) unless default.nil?
3865
+
3866
+ unless null.nil?
3867
+ change_column_sql = if null
3868
+ "ALTER TABLE #{table_name} ALTER #{column_name} DROP NOT NULL"
3869
+ else
3870
+ "ALTER TABLE #{table_name} ALTER #{column_name} SET NOT NULL"
3871
+ end
3872
+ stmt = execute(change_column_sql)
3873
+ reorg_table(table_name)
3874
+ end
3875
+ ensure
3876
+ IBM_DB.free_stmt(stmt) if stmt
3877
+ end
3878
+
3879
+ # This method returns the DB2 SQL type corresponding to the Rails
3880
+ # datetime/timestamp type
3881
+ def get_datetime_mapping
3882
+ @adapter.puts_log 'get_datetime_mapping'
3883
+ 'timestamp'
3884
+ end
3885
+
3886
+ # This method returns the DB2 SQL type corresponding to the Rails
3887
+ # time type
3888
+ def get_time_mapping
3889
+ @adapter.puts_log 'get_time_mapping'
3890
+ 'time'
3891
+ end
3892
+
3893
+ # This method returns the DB2 SQL type corresponding to Rails double type
3894
+ def get_double_mapping
3895
+ @adapter.puts_log 'get_double_mapping'
3896
+ 'double'
3897
+ end
3898
+
3899
+ # This method generates the default blob value specified for
3900
+ # DB2 Dataservers
3901
+ def set_binary_default(value)
3902
+ @adapter.puts_log 'set_binary_default'
3903
+ "BLOB('#{value}')"
3904
+ end
3905
+
3906
+ # This method generates the blob value specified for DB2 Dataservers
3907
+ def set_binary_value
3908
+ @adapter.puts_log 'set_binary_value'
3909
+ "BLOB('?')"
3910
+ end
3911
+
3912
+ # This method generates the default clob value specified for
3913
+ # DB2 Dataservers
3914
+ def set_text_default(value)
3915
+ @adapter.puts_log 'set_text_default'
3916
+ "'#{value}'"
3917
+ end
3918
+
3919
+ # For DB2 Dataservers , the arguments to the meta-data functions
3920
+ # need to be in upper-case
3921
+ def set_case(value)
3922
+ @adapter.puts_log 'set_case'
3923
+ value.upcase
3924
+ end
3925
+ end # class IBM_DB2
3926
+
3927
+ class IBM_DB2_LUW < IBM_DB2
3928
+ # Reorganizes the table for column changes
3929
+ def reorg_table(table_name)
3930
+ execute("CALL ADMIN_CMD('REORG TABLE #{table_name}')")
3931
+ end
3932
+ end # class IBM_DB2_LUW
3933
+
3934
+ class IBM_DB2_LUW_COBRA < IBM_DB2_LUW
3935
+ # Cobra supports parameterised timestamp,
3936
+ # hence overriding following method to allow timestamp datatype to be parameterised
3937
+ def limit_not_supported_types
3938
+ %i[integer double date time xml bigint]
3939
+ end
3940
+
3941
+ # Alter table column for renaming a column
3942
+ # This feature is supported for against DB2 V97 and above only
3943
+ def rename_column(table_name, column_name, new_column_name)
3944
+ _table_name = table_name.to_s
3945
+ _column_name = column_name.to_s
3946
+ _new_column_name = new_column_name.to_s
3947
+
3948
+ nil_condition = _table_name.nil? || _column_name.nil? || _new_column_name.nil?
3949
+ unless nil_condition
3950
+ empty_condition = _table_name.empty? ||
3951
+ _column_name.empty? ||
3952
+ _new_column_name.empty?
3953
+ end
3954
+
3955
+ if nil_condition || empty_condition
3956
+ raise ArgumentError, 'One of the arguments passed to rename_column is empty or nil'
3957
+ end
3958
+
3959
+ begin
3960
+ rename_column_sql = "ALTER TABLE #{_table_name} RENAME COLUMN #{_column_name} \
3961
+ TO #{_new_column_name}"
3962
+
3963
+ unless stmt = execute(rename_column_sql)
3964
+ error_msg = IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN)
3965
+ raise "Rename column failed : #{error_msg}" if error_msg && !error_msg.empty?
3966
+
3967
+ raise StandardError.new('An unexpected error occurred during renaming the column')
3968
+
3969
+ end
3970
+
3971
+ reorg_table(_table_name)
3972
+ ensure
3973
+ IBM_DB.free_stmt(stmt) if stmt
3974
+ end # End of begin
3975
+ end # End of rename_column
3976
+ end # IBM_DB2_LUW_COBRA
3977
+
3978
+ module HostedDataServer
3979
+ require 'pathname'
3980
+ # find DB2-i5-zOS rezerved words file relative path
3981
+ rfile = Pathname.new(File.dirname(__FILE__)).parent + 'vendor' + 'db2-i5-zOS.yaml'
3982
+ raise "Failed to locate IBM_DB Adapter dependency: #{rfile}" unless rfile
3983
+
3984
+ RESERVED_WORDS = open(rfile.to_s) { |f| YAML.load(f) }
3985
+ def check_reserved_words(col_name)
3986
+ puts_log '164'
3987
+ if RESERVED_WORDS[col_name]
3988
+ '"' + RESERVED_WORDS[col_name] + '"'
3989
+ else
3990
+ col_name.to_s
3991
+ end
3992
+ end
3993
+ end # module HostedDataServer
3994
+
3995
+ class IBM_DB2_ZOS < IBM_DB2
3996
+ # since v9 doesn't need, suggest putting it in HostedDataServer?
3997
+ def create_index_after_table(table_name, column_name)
3998
+ @adapter.add_index(table_name, column_name, unique: true)
3999
+ end
4000
+
4001
+ def remove_column(table_name, column_name)
4002
+ raise NotImplementedError,
4003
+ 'remove_column is not supported by the DB2 for zOS data server'
4004
+ end
4005
+
4006
+ # Alter table column for renaming a column
4007
+ def rename_column(table_name, column_name, new_column_name)
4008
+ _table_name = table_name.to_s
4009
+ _column_name = column_name.to_s
4010
+ _new_column_name = new_column_name.to_s
4011
+
4012
+ nil_condition = _table_name.nil? || _column_name.nil? || _new_column_name.nil?
4013
+ unless nil_condition
4014
+ empty_condition = _table_name.empty? ||
4015
+ _column_name.empty? ||
4016
+ _new_column_name.empty?
4017
+ end
4018
+
4019
+ if nil_condition || empty_condition
4020
+ raise ArgumentError, 'One of the arguments passed to rename_column is empty or nil'
4021
+ end
4022
+
4023
+ begin
4024
+ rename_column_sql = "ALTER TABLE #{_table_name} RENAME COLUMN #{_column_name} \
4025
+ TO #{_new_column_name}"
4026
+
4027
+ unless stmt = execute(rename_column_sql)
4028
+ error_msg = IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN)
4029
+ raise "Rename column failed : #{error_msg}" if error_msg && !error_msg.empty?
4030
+
4031
+ raise StandardError.new('An unexpected error occurred during renaming the column')
4032
+
4033
+ end
4034
+
4035
+ reorg_table(_table_name)
4036
+ ensure
4037
+ IBM_DB.free_stmt(stmt) if stmt
4038
+ end # End of begin
4039
+ end # End of rename_column
4040
+
4041
+ # DB2 z/OS only allows NULL or "" (empty) string as DEFAULT value for a BLOB column.
4042
+ # For non-empty string and non-NULL values, the server returns error
4043
+ def set_binary_default(value)
4044
+ "#{value}"
4045
+ end
4046
+
4047
+ def change_column_default(table_name, column_name, default)
4048
+ if default
4049
+ super
4050
+ else
4051
+ raise NotImplementedError,
4052
+ 'DB2 for zOS data server version 9 does not support changing the column default to NULL'
4053
+ end
4054
+ end
4055
+
4056
+ def change_column_null(table_name, column_name, null, default)
4057
+ raise NotImplementedError,
4058
+ "DB2 for zOS data server does not support changing the column's nullability"
4059
+ end
4060
+ end # class IBM_DB2_ZOS
4061
+
4062
+ class IBM_DB2_ZOS_8 < IBM_DB2_ZOS
4063
+ include HostedDataServer
4064
+
4065
+ # This call is needed on DB2 z/OS v8 for the creation of tables
4066
+ # with LOBs. When issued, this call does the following:
4067
+ # DB2 creates LOB table spaces, auxiliary tables, and indexes on auxiliary
4068
+ # tables for LOB columns.
4069
+ def setup_for_lob_table
4070
+ execute "SET CURRENT RULES = 'STD'"
4071
+ end
4072
+
4073
+ def rename_column(table_name, column_name, new_column_name)
4074
+ raise NotImplementedError, 'rename_column is not implemented for DB2 on zOS 8'
4075
+ end
4076
+
4077
+ def change_column_default(table_name, column_name, default)
4078
+ raise NotImplementedError,
4079
+ 'DB2 for zOS data server version 8 does not support changing the column default'
4080
+ end
4081
+ end # class IBM_DB2_ZOS_8
4082
+
4083
+ class IBM_DB2_I5 < IBM_DB2
4084
+ include HostedDataServer
4085
+ end # class IBM_DB2_I5
4086
+
4087
+ class IBM_IDS < IBM_DataServer
4088
+ # IDS does not support the SET SCHEMA syntax
4089
+ def set_schema(schema); end
4090
+
4091
+ # IDS specific ALTER TABLE statement to rename a column
4092
+ def rename_column(table_name, column_name, new_column_name)
4093
+ _table_name = table_name.to_s
4094
+ _column_name = column_name.to_s
4095
+ _new_column_name = new_column_name.to_s
4096
+
4097
+ nil_condition = _table_name.nil? || _column_name.nil? || _new_column_name.nil?
4098
+ unless nil_condition
4099
+ empty_condition = _table_name.empty? ||
4100
+ _column_name.empty? ||
4101
+ _new_column_name.empty?
4102
+ end
4103
+
4104
+ if nil_condition || empty_condition
4105
+ raise ArgumentError, 'One of the arguments passed to rename_column is empty or nil'
4106
+ end
4107
+
4108
+ begin
4109
+ rename_column_sql = "RENAME COLUMN #{table_name}.#{column_name} TO \
4110
+ #{new_column_name}"
4111
+
4112
+ unless stmt = execute(rename_column_sql)
4113
+ error_msg = IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN)
4114
+ raise "Rename column failed : #{error_msg}" if error_msg && !error_msg.empty?
4115
+
4116
+ raise StandardError.new('An unexpected error occurred during renaming the column')
4117
+
4118
+ end
4119
+
4120
+ reorg_table(_table_name)
4121
+ ensure
4122
+ IBM_DB.free_stmt(stmt) if stmt
4123
+ end # End of begin
4124
+ end # End of rename_column
4125
+
4126
+ def primary_key_definition(start_id)
4127
+ "SERIAL(#{start_id})"
4128
+ end
4129
+
4130
+ def change_column(table_name, column_name, type, options)
4131
+ if !options[:null].nil? && !options[:null]
4132
+ execute "ALTER TABLE #{table_name} MODIFY #{column_name} #{@adapter.type_to_sql(type, options[:limit],
4133
+ options[:precision], options[:scale])} NOT NULL"
4134
+ else
4135
+ execute "ALTER TABLE #{table_name} MODIFY #{column_name} #{@adapter.type_to_sql(type, options[:limit],
4136
+ options[:precision], options[:scale])}"
4137
+ end
4138
+ change_column_default(table_name, column_name, options[:default]) unless options[:default].nil?
4139
+ reorg_table(table_name)
4140
+ end
4141
+
4142
+ # IDS specific ALTER TABLE statement to add a default clause
4143
+ # IDS requires the data type to be explicitly specified when adding the
4144
+ # DEFAULT clause
4145
+ def change_column_default(table_name, column_name, default)
4146
+ sql_type = nil
4147
+ is_nullable = true
4148
+ @adapter.columns(table_name).select do |col|
4149
+ if col.name == column_name
4150
+ sql_type = @adapter.type_to_sql(col.sql_type, col.limit, col.precision, col.scale)
4151
+ is_nullable = col.null
4152
+ end
4153
+ end
4154
+ # SQL statement which alters column's default value
4155
+ change_column_sql = "ALTER TABLE #{table_name} MODIFY #{column_name} #{sql_type} DEFAULT #{@adapter.quote(default)}"
4156
+ change_column_sql << ' NOT NULL' unless is_nullable
4157
+ stmt = execute(change_column_sql)
4158
+ reorg_table(table_name)
4159
+ # Ensures to free the resources associated with the statement
4160
+ ensure
4161
+ IBM_DB.free_stmt(stmt) if stmt
4162
+ end
4163
+
4164
+ # IDS specific ALTER TABLE statement to change the nullability of a column
4165
+ def change_column_null(table_name, column_name, null, default)
4166
+ change_column_default table_name, column_name, default unless default.nil?
4167
+ sql_type = nil
4168
+ @adapter.columns(table_name).select do |col|
4169
+ sql_type = @adapter.type_to_sql(col.sql_type, col.limit, col.precision, col.scale) if col.name == column_name
4170
+ end
4171
+ unless null.nil?
4172
+ change_column_sql = if !null
4173
+ "ALTER TABLE #{table_name} MODIFY #{column_name} #{sql_type} NOT NULL"
4174
+ else
4175
+ "ALTER TABLE #{table_name} MODIFY #{column_name} #{sql_type}"
4176
+ end
4177
+ stmt = execute(change_column_sql)
4178
+ reorg_table(table_name)
4179
+ end
4180
+ ensure
4181
+ IBM_DB.free_stmt(stmt) if stmt
4182
+ end
4183
+
4184
+ # Reorganizes the table for column changes
4185
+ def reorg_table(table_name)
4186
+ execute("UPDATE STATISTICS FOR TABLE #{table_name}")
4187
+ end
4188
+
4189
+ # This method returns the IDS SQL type corresponding to the Rails
4190
+ # datetime/timestamp type
4191
+ def get_datetime_mapping
4192
+ 'datetime year to fraction(5)'
4193
+ end
4194
+
4195
+ # This method returns the IDS SQL type corresponding to the Rails
4196
+ # time type
4197
+ def get_time_mapping
4198
+ 'datetime hour to second'
4199
+ end
4200
+
4201
+ # This method returns the IDS SQL type corresponding to Rails double type
4202
+ def get_double_mapping
4203
+ 'double precision'
4204
+ end
4205
+
4206
+ # Method that returns the last automatically generated ID
4207
+ # on the given +@connection+. This method is required by the +insert+
4208
+ # method. IDS returns the last generated serial value in the SQLCA unlike
4209
+ # DB2 where the generated value has to be retrieved using the
4210
+ # IDENTITY_VAL_LOCAL function. We used the "stmt" parameter to identify
4211
+ # the statement resource from which to get the last generated value
4212
+ def last_generated_id(stmt)
4213
+ IBM_DB.get_last_serial_value(stmt)
4214
+ end
4215
+
4216
+ # This method throws an error when trying to create a default value on a
4217
+ # BLOB/CLOB column for IDS. The documentation states: "if the column is a
4218
+ # BLOB or CLOB datatype, NULL is the only valid default value."
4219
+ def set_binary_default(value)
4220
+ return if value == 'NULL'
4221
+
4222
+ raise 'Informix Dynamic Server only allows NULL as a valid default value for a BLOB data type'
4223
+ end
4224
+
4225
+ # For Informix Dynamic Server, we treat binary value same as we treat a
4226
+ # text value. We support literals by converting the insert into a dummy
4227
+ # insert and an update (See handle_lobs method above)
4228
+ def set_binary_value
4229
+ "'@@@IBMBINARY@@@'"
4230
+ end
4231
+
4232
+ # This method throws an error when trying to create a default value on a
4233
+ # BLOB/CLOB column for IDS. The documentation states: "if the column is
4234
+ # a BLOB or CLOB datatype, NULL is the only valid default value."
4235
+ def set_text_default(value)
4236
+ return if value == 'NULL'
4237
+
4238
+ raise 'Informix Dynamic Server only allows NULL as a valid default value for a CLOB data type'
4239
+ end
4240
+
4241
+ # For Informix Dynamic Server, the arguments to the meta-data functions
4242
+ # need to be in lower-case
4243
+ def set_case(value)
4244
+ value.downcase
4245
+ end
4246
+ end # class IBM_IDS
4247
+ end # module ConnectionAdapters
4248
+ end # module ActiveRecord
4249
+
4250
+ module Arel
4251
+ # Check Arel version
4252
+ begin
4253
+ arelVersion = Arel::VERSION.to_i
4254
+ rescue StandardError
4255
+ arelVersion = 0
4256
+ end
4257
+ if arelVersion >= 6
4258
+ module Collectors
4259
+ class Bind
4260
+ def changeFirstSegment(segment)
4261
+ @parts[0] = segment
4262
+ end
4263
+
4264
+ def changeEndSegment(segment)
4265
+ len = @parts.length
4266
+ @parts[len] = segment
4267
+ end
4268
+ end
4269
+ end
4270
+ end
4271
+
4272
+ module Visitors
4273
+ class Visitor # opening and closing the class to ensure backward compatibility
4274
+ end
4275
+
4276
+ # Check Arel version
4277
+ begin
4278
+ arelVersion = Arel::VERSION.to_i
4279
+ rescue StandardError
4280
+ arelVersion = 0
4281
+ end
4282
+
4283
+ if arelVersion >= 6 && arelVersion <= 9
4284
+ class ToSql < Arel::Visitors::Reduce # opening and closing the class to ensure backward compatibility
4285
+ # In case when using Rails-2.3.x there is no arel used due to which the constructor has to be defined explicitly
4286
+ # to ensure the same code works on any version of Rails
4287
+
4288
+ # Check Arel version
4289
+ begin
4290
+ @arelVersion = Arel::VERSION.to_i
4291
+ rescue StandardError
4292
+ @arelVersion = 0
4293
+ end
4294
+
4295
+ if @arelVersion >= 3
4296
+ def initialize(connection)
4297
+ super()
4298
+ @connection = connection
4299
+ @schema_cache = connection.schema_cache if connection.respond_to?(:schema_cache)
4300
+ @quoted_tables = {}
4301
+ @quoted_columns = {}
4302
+ @last_column = nil
4303
+ end
4304
+ end
4305
+ end
4306
+ else
4307
+ class ToSql < Arel::Visitors::Visitor # opening and closing the class to ensure backward compatibility
4308
+ # In case when using Rails-2.3.x there is no arel used due to which the constructor has to be defined explicitly
4309
+ # to ensure the same code works on any version of Rails
4310
+
4311
+ # Check Arel version
4312
+ begin
4313
+ @arelVersion = Arel::VERSION.to_i
4314
+ rescue StandardError
4315
+ @arelVersion = 0
4316
+ end
4317
+ if @arelVersion >= 3
4318
+ def initialize(connection)
4319
+ super()
4320
+ @connection = connection
4321
+ @schema_cache = connection.schema_cache if connection.respond_to?(:schema_cache)
4322
+ @quoted_tables = {}
4323
+ @quoted_columns = {}
4324
+ @last_column = nil
4325
+ end
4326
+ end
4327
+ end
4328
+ end
4329
+
4330
+ class IBM_DB < Arel::Visitors::ToSql
4331
+ private
4332
+
4333
+ def visit_Arel_Nodes_Limit(o, collector)
4334
+ collector << ' LIMIT '
4335
+ visit o.expr, collector
4336
+ end
4337
+
4338
+ def visit_Arel_Nodes_Offset(o, collector)
4339
+ @connection.puts_log "visit_Arel_Nodes_Offset #{@connection.servertype}"
4340
+ if !@connection.servertype.instance_of? ActiveRecord::ConnectionAdapters::IBM_IDS
4341
+ collector << ' OFFSET '
4342
+ visit o.expr, collector
4343
+ end
4344
+ end
4345
+
4346
+ def visit_Arel_Nodes_ValuesList(o, collector)
4347
+ collector << 'VALUES '
4348
+ o.rows.each_with_index do |row, i|
4349
+ collector << ', ' unless i == 0
4350
+ collector << '('
4351
+ row.each_with_index do |value, k|
4352
+ collector << ', ' unless k == 0
4353
+ case value
4354
+ when Nodes::SqlLiteral, Nodes::BindParam, ActiveModel::Attribute
4355
+ collector = visit(value, collector)
4356
+ # collector << quote(value).to_s
4357
+ else
4358
+ collector << quote(value).to_s
4359
+ end
4360
+ end
4361
+ collector << ')'
4362
+ end
4363
+ collector
4364
+ end
4365
+
4366
+ def visit_Arel_Nodes_SelectStatement(o, collector)
4367
+ @connection.puts_log "visit_Arel_Nodes_SelectStatement #{@connection.servertype}"
4368
+ if o.with
4369
+ collector = visit o.with, collector
4370
+ collector << ' '
4371
+ end
4372
+
4373
+ collector = o.cores.inject(collector) do |c, x|
4374
+ visit_Arel_Nodes_SelectCore(x, c)
4375
+ end
4376
+
4377
+ unless o.orders.empty?
4378
+ collector << ' ORDER BY '
4379
+ len = o.orders.length - 1
4380
+ o.orders.each_with_index do |x, i|
4381
+ collector = visit(x, collector)
4382
+ collector << ', ' unless len == i
4383
+ end
4384
+ end
4385
+
4386
+ if o.offset && o.limit
4387
+ visit_Arel_Nodes_Limit(o.limit, collector)
4388
+ visit_Arel_Nodes_Offset(o.offset, collector)
4389
+ elsif o.offset && o.limit.nil?
4390
+ if !@connection.servertype.instance_of? ActiveRecord::ConnectionAdapters::IBM_IDS
4391
+ collector << ' OFFSET '
4392
+ visit o.offset.expr, collector
4393
+ collector << ' ROWS '
4394
+ maybe_visit o.lock, collector
4395
+ end
4396
+ else
4397
+ visit_Arel_Nodes_SelectOptions(o, collector)
4398
+ end
4399
+ end
4400
+
4401
+ # Locks are not supported in SQLite
4402
+ def visit_Arel_Nodes_Lock(_o, collector)
4403
+ collector
4404
+ end
4405
+ end
4406
+ end
4407
+ end