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.
- checksums.yaml +7 -0
- data/CHANGES +299 -0
- data/LICENSE +55 -0
- data/MANIFEST +14 -0
- data/ParameterizedQueries README +39 -0
- data/README +210 -0
- data/ext/Makefile +270 -0
- data/ext/Makefile.nt32 +181 -0
- data/ext/Makefile.nt32.191 +212 -0
- data/ext/extconf.rb +320 -0
- data/ext/gil_release_version.h +3 -0
- data/ext/ibm_db.bundle +0 -0
- data/ext/ibm_db.c +11865 -0
- data/ext/ibm_db.o +0 -0
- data/ext/mkmf.log +98 -0
- data/ext/ruby_ibm_db.h +241 -0
- data/ext/ruby_ibm_db_cli.c +867 -0
- data/ext/ruby_ibm_db_cli.h +508 -0
- data/ext/ruby_ibm_db_cli.o +0 -0
- data/ext/unicode_support_version.h +3 -0
- data/init.rb +42 -0
- data/lib/IBM_DB.rb +27 -0
- data/lib/active_record/connection_adapters/ibm_db_adapter.rb +4407 -0
- data/lib/active_record/connection_adapters/ibm_db_pstmt.rb +1965 -0
- data/lib/active_record/connection_adapters/ibmdb_adapter.rb +5 -0
- data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -0
- data/lib/ibm_db.bundle +0 -0
- data/test/active_record/connection_adapters/fake_adapter.rb +52 -0
- data/test/activejob/destroy_association_async_test.rb +305 -0
- data/test/activejob/destroy_async_job_not_present_test.rb +31 -0
- data/test/activejob/helper.rb +15 -0
- data/test/assets/example.log +1 -0
- data/test/assets/flowers.jpg +0 -0
- data/test/assets/schema_dump_5_1.yml +345 -0
- data/test/assets/test.txt +1 -0
- data/test/cases/adapter_prevent_writes_test.rb +334 -0
- data/test/cases/adapter_test.rb +565 -0
- data/test/cases/adapters/mysql2/active_schema_test.rb +203 -0
- data/test/cases/adapters/mysql2/auto_increment_test.rb +34 -0
- data/test/cases/adapters/mysql2/bind_parameter_test.rb +52 -0
- data/test/cases/adapters/mysql2/boolean_test.rb +102 -0
- data/test/cases/adapters/mysql2/case_sensitivity_test.rb +65 -0
- data/test/cases/adapters/mysql2/charset_collation_test.rb +57 -0
- data/test/cases/adapters/mysql2/connection_test.rb +208 -0
- data/test/cases/adapters/mysql2/count_deleted_rows_with_lock_test.rb +28 -0
- data/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb +49 -0
- data/test/cases/adapters/mysql2/enum_test.rb +47 -0
- data/test/cases/adapters/mysql2/explain_test.rb +23 -0
- data/test/cases/adapters/mysql2/json_test.rb +24 -0
- data/test/cases/adapters/mysql2/mysql2_adapter_prevent_writes_test.rb +208 -0
- data/test/cases/adapters/mysql2/mysql2_adapter_test.rb +238 -0
- data/test/cases/adapters/mysql2/nested_deadlock_test.rb +75 -0
- data/test/cases/adapters/mysql2/optimizer_hints_test.rb +69 -0
- data/test/cases/adapters/mysql2/reserved_word_test.rb +152 -0
- data/test/cases/adapters/mysql2/schema_migrations_test.rb +64 -0
- data/test/cases/adapters/mysql2/schema_test.rb +128 -0
- data/test/cases/adapters/mysql2/set_test.rb +32 -0
- data/test/cases/adapters/mysql2/sp_test.rb +38 -0
- data/test/cases/adapters/mysql2/sql_types_test.rb +16 -0
- data/test/cases/adapters/mysql2/table_options_test.rb +125 -0
- data/test/cases/adapters/mysql2/transaction_test.rb +151 -0
- data/test/cases/adapters/mysql2/unsigned_type_test.rb +68 -0
- data/test/cases/adapters/mysql2/virtual_column_test.rb +66 -0
- data/test/cases/adapters/postgresql/active_schema_test.rb +113 -0
- data/test/cases/adapters/postgresql/array_test.rb +394 -0
- data/test/cases/adapters/postgresql/bit_string_test.rb +84 -0
- data/test/cases/adapters/postgresql/bytea_test.rb +135 -0
- data/test/cases/adapters/postgresql/case_insensitive_test.rb +27 -0
- data/test/cases/adapters/postgresql/change_schema_test.rb +40 -0
- data/test/cases/adapters/postgresql/cidr_test.rb +27 -0
- data/test/cases/adapters/postgresql/citext_test.rb +78 -0
- data/test/cases/adapters/postgresql/collation_test.rb +55 -0
- data/test/cases/adapters/postgresql/composite_test.rb +134 -0
- data/test/cases/adapters/postgresql/connection_test.rb +245 -0
- data/test/cases/adapters/postgresql/create_unlogged_tables_test.rb +74 -0
- data/test/cases/adapters/postgresql/datatype_test.rb +89 -0
- data/test/cases/adapters/postgresql/date_test.rb +42 -0
- data/test/cases/adapters/postgresql/domain_test.rb +49 -0
- data/test/cases/adapters/postgresql/enum_test.rb +93 -0
- data/test/cases/adapters/postgresql/explain_test.rb +22 -0
- data/test/cases/adapters/postgresql/extension_migration_test.rb +64 -0
- data/test/cases/adapters/postgresql/foreign_table_test.rb +109 -0
- data/test/cases/adapters/postgresql/full_text_test.rb +46 -0
- data/test/cases/adapters/postgresql/geometric_test.rb +372 -0
- data/test/cases/adapters/postgresql/hstore_test.rb +390 -0
- data/test/cases/adapters/postgresql/infinity_test.rb +108 -0
- data/test/cases/adapters/postgresql/integer_test.rb +27 -0
- data/test/cases/adapters/postgresql/interval_test.rb +99 -0
- data/test/cases/adapters/postgresql/json_test.rb +52 -0
- data/test/cases/adapters/postgresql/ltree_test.rb +51 -0
- data/test/cases/adapters/postgresql/money_test.rb +127 -0
- data/test/cases/adapters/postgresql/network_test.rb +102 -0
- data/test/cases/adapters/postgresql/numbers_test.rb +51 -0
- data/test/cases/adapters/postgresql/optimizer_hints_test.rb +71 -0
- data/test/cases/adapters/postgresql/partitions_test.rb +22 -0
- data/test/cases/adapters/postgresql/postgresql_adapter_prevent_writes_test.rb +205 -0
- data/test/cases/adapters/postgresql/postgresql_adapter_test.rb +447 -0
- data/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb +27 -0
- data/test/cases/adapters/postgresql/prepared_statements_test.rb +22 -0
- data/test/cases/adapters/postgresql/quoting_test.rb +50 -0
- data/test/cases/adapters/postgresql/range_test.rb +457 -0
- data/test/cases/adapters/postgresql/referential_integrity_test.rb +112 -0
- data/test/cases/adapters/postgresql/rename_table_test.rb +35 -0
- data/test/cases/adapters/postgresql/schema_authorization_test.rb +110 -0
- data/test/cases/adapters/postgresql/schema_test.rb +713 -0
- data/test/cases/adapters/postgresql/serial_test.rb +156 -0
- data/test/cases/adapters/postgresql/statement_pool_test.rb +61 -0
- data/test/cases/adapters/postgresql/timestamp_test.rb +92 -0
- data/test/cases/adapters/postgresql/transaction_nested_test.rb +114 -0
- data/test/cases/adapters/postgresql/transaction_test.rb +189 -0
- data/test/cases/adapters/postgresql/type_lookup_test.rb +35 -0
- data/test/cases/adapters/postgresql/utils_test.rb +64 -0
- data/test/cases/adapters/postgresql/uuid_test.rb +411 -0
- data/test/cases/adapters/postgresql/xml_test.rb +50 -0
- data/test/cases/adapters/sqlite3/collation_test.rb +64 -0
- data/test/cases/adapters/sqlite3/copy_table_test.rb +101 -0
- data/test/cases/adapters/sqlite3/explain_test.rb +23 -0
- data/test/cases/adapters/sqlite3/json_test.rb +29 -0
- data/test/cases/adapters/sqlite3/quoting_test.rb +79 -0
- data/test/cases/adapters/sqlite3/sqlite3_adapter_prevent_writes_test.rb +186 -0
- data/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +628 -0
- data/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb +24 -0
- data/test/cases/adapters/sqlite3/statement_pool_test.rb +21 -0
- data/test/cases/adapters/sqlite3/transaction_test.rb +123 -0
- data/test/cases/aggregations_test.rb +170 -0
- data/test/cases/annotate_test.rb +46 -0
- data/test/cases/ar_schema_test.rb +213 -0
- data/test/cases/arel/attributes/attribute_test.rb +1145 -0
- data/test/cases/arel/attributes/math_test.rb +83 -0
- data/test/cases/arel/attributes_test.rb +27 -0
- data/test/cases/arel/collectors/bind_test.rb +40 -0
- data/test/cases/arel/collectors/composite_test.rb +47 -0
- data/test/cases/arel/collectors/sql_string_test.rb +41 -0
- data/test/cases/arel/collectors/substitute_bind_collector_test.rb +48 -0
- data/test/cases/arel/crud_test.rb +65 -0
- data/test/cases/arel/delete_manager_test.rb +53 -0
- data/test/cases/arel/factory_methods_test.rb +46 -0
- data/test/cases/arel/helper.rb +45 -0
- data/test/cases/arel/insert_manager_test.rb +241 -0
- data/test/cases/arel/nodes/and_test.rb +30 -0
- data/test/cases/arel/nodes/as_test.rb +36 -0
- data/test/cases/arel/nodes/ascending_test.rb +46 -0
- data/test/cases/arel/nodes/bin_test.rb +35 -0
- data/test/cases/arel/nodes/binary_test.rb +29 -0
- data/test/cases/arel/nodes/bind_param_test.rb +22 -0
- data/test/cases/arel/nodes/case_test.rb +96 -0
- data/test/cases/arel/nodes/casted_test.rb +18 -0
- data/test/cases/arel/nodes/comment_test.rb +22 -0
- data/test/cases/arel/nodes/count_test.rb +35 -0
- data/test/cases/arel/nodes/delete_statement_test.rb +36 -0
- data/test/cases/arel/nodes/descending_test.rb +46 -0
- data/test/cases/arel/nodes/distinct_test.rb +21 -0
- data/test/cases/arel/nodes/equality_test.rb +62 -0
- data/test/cases/arel/nodes/extract_test.rb +43 -0
- data/test/cases/arel/nodes/false_test.rb +21 -0
- data/test/cases/arel/nodes/grouping_test.rb +26 -0
- data/test/cases/arel/nodes/infix_operation_test.rb +42 -0
- data/test/cases/arel/nodes/insert_statement_test.rb +44 -0
- data/test/cases/arel/nodes/named_function_test.rb +48 -0
- data/test/cases/arel/nodes/node_test.rb +22 -0
- data/test/cases/arel/nodes/not_test.rb +31 -0
- data/test/cases/arel/nodes/or_test.rb +36 -0
- data/test/cases/arel/nodes/over_test.rb +69 -0
- data/test/cases/arel/nodes/select_core_test.rb +79 -0
- data/test/cases/arel/nodes/select_statement_test.rb +51 -0
- data/test/cases/arel/nodes/sql_literal_test.rb +75 -0
- data/test/cases/arel/nodes/sum_test.rb +35 -0
- data/test/cases/arel/nodes/table_alias_test.rb +29 -0
- data/test/cases/arel/nodes/true_test.rb +21 -0
- data/test/cases/arel/nodes/unary_operation_test.rb +41 -0
- data/test/cases/arel/nodes/update_statement_test.rb +60 -0
- data/test/cases/arel/nodes/window_test.rb +81 -0
- data/test/cases/arel/nodes_test.rb +34 -0
- data/test/cases/arel/select_manager_test.rb +1238 -0
- data/test/cases/arel/support/fake_record.rb +135 -0
- data/test/cases/arel/table_test.rb +216 -0
- data/test/cases/arel/update_manager_test.rb +126 -0
- data/test/cases/arel/visitors/dispatch_contamination_test.rb +78 -0
- data/test/cases/arel/visitors/dot_test.rb +90 -0
- data/test/cases/arel/visitors/mysql_test.rb +157 -0
- data/test/cases/arel/visitors/postgres_test.rb +366 -0
- data/test/cases/arel/visitors/sqlite_test.rb +75 -0
- data/test/cases/arel/visitors/to_sql_test.rb +750 -0
- data/test/cases/associations/association_scope_test.rb +16 -0
- data/test/cases/associations/belongs_to_associations_test.rb +1493 -0
- data/test/cases/associations/bidirectional_destroy_dependencies_test.rb +43 -0
- data/test/cases/associations/callbacks_test.rb +208 -0
- data/test/cases/associations/cascaded_eager_loading_test.rb +245 -0
- data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +156 -0
- data/test/cases/associations/eager_load_nested_include_test.rb +127 -0
- data/test/cases/associations/eager_singularization_test.rb +148 -0
- data/test/cases/associations/eager_test.rb +1658 -0
- data/test/cases/associations/extension_test.rb +93 -0
- data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +1025 -0
- data/test/cases/associations/has_many_associations_test.rb +3074 -0
- data/test/cases/associations/has_many_through_associations_test.rb +1580 -0
- data/test/cases/associations/has_one_associations_test.rb +872 -0
- data/test/cases/associations/has_one_through_associations_test.rb +429 -0
- data/test/cases/associations/inner_join_association_test.rb +215 -0
- data/test/cases/associations/inverse_associations_test.rb +941 -0
- data/test/cases/associations/join_model_test.rb +787 -0
- data/test/cases/associations/left_outer_join_association_test.rb +123 -0
- data/test/cases/associations/nested_through_associations_test.rb +636 -0
- data/test/cases/associations/required_test.rb +127 -0
- data/test/cases/associations_test.rb +516 -0
- data/test/cases/attribute_decorators_test.rb +126 -0
- data/test/cases/attribute_methods/read_test.rb +60 -0
- data/test/cases/attribute_methods_test.rb +1124 -0
- data/test/cases/attribute_set_test.rb +270 -0
- data/test/cases/attribute_test.rb +246 -0
- data/test/cases/attributes_test.rb +371 -0
- data/test/cases/autosave_association_test.rb +1953 -0
- data/test/cases/base_prevent_writes_test.rb +229 -0
- data/test/cases/base_test.rb +1770 -0
- data/test/cases/batches_test.rb +695 -0
- data/test/cases/binary_test.rb +39 -0
- data/test/cases/bind_parameter_test.rb +283 -0
- data/test/cases/boolean_test.rb +52 -0
- data/test/cases/cache_key_test.rb +131 -0
- data/test/cases/calculations_test.rb +1361 -0
- data/test/cases/callbacks_test.rb +503 -0
- data/test/cases/clone_test.rb +45 -0
- data/test/cases/coders/json_test.rb +17 -0
- data/test/cases/coders/yaml_column_test.rb +66 -0
- data/test/cases/collection_cache_key_test.rb +272 -0
- data/test/cases/column_alias_test.rb +19 -0
- data/test/cases/column_definition_test.rb +34 -0
- data/test/cases/comment_test.rb +204 -0
- data/test/cases/connection_adapters/adapter_leasing_test.rb +60 -0
- data/test/cases/connection_adapters/connection_handler_test.rb +467 -0
- data/test/cases/connection_adapters/connection_handlers_multi_db_test.rb +400 -0
- data/test/cases/connection_adapters/connection_handlers_multi_pool_config_test.rb +103 -0
- data/test/cases/connection_adapters/connection_handlers_sharding_db_test.rb +499 -0
- data/test/cases/connection_adapters/connection_specification_test.rb +12 -0
- data/test/cases/connection_adapters/connection_swapping_nested_test.rb +457 -0
- data/test/cases/connection_adapters/legacy_connection_handlers_multi_db_test.rb +486 -0
- data/test/cases/connection_adapters/legacy_connection_handlers_sharding_db_test.rb +586 -0
- data/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +436 -0
- data/test/cases/connection_adapters/mysql_type_lookup_test.rb +81 -0
- data/test/cases/connection_adapters/quoting_test.rb +13 -0
- data/test/cases/connection_adapters/schema_cache_test.rb +294 -0
- data/test/cases/connection_adapters/type_lookup_test.rb +119 -0
- data/test/cases/connection_management_test.rb +114 -0
- data/test/cases/connection_pool_test.rb +754 -0
- data/test/cases/connection_specification/resolver_test.rb +131 -0
- data/test/cases/core_test.rb +136 -0
- data/test/cases/counter_cache_test.rb +368 -0
- data/test/cases/custom_locking_test.rb +19 -0
- data/test/cases/database_configurations/hash_config_test.rb +74 -0
- data/test/cases/database_configurations/resolver_test.rb +150 -0
- data/test/cases/database_configurations_test.rb +145 -0
- data/test/cases/database_selector_test.rb +296 -0
- data/test/cases/database_statements_test.rb +36 -0
- data/test/cases/date_test.rb +36 -0
- data/test/cases/date_time_precision_test.rb +129 -0
- data/test/cases/date_time_test.rb +76 -0
- data/test/cases/defaults_test.rb +254 -0
- data/test/cases/delegated_type_test.rb +57 -0
- data/test/cases/dirty_test.rb +959 -0
- data/test/cases/disconnected_test.rb +30 -0
- data/test/cases/dup_test.rb +184 -0
- data/test/cases/enum_test.rb +823 -0
- data/test/cases/errors_test.rb +16 -0
- data/test/cases/explain_subscriber_test.rb +66 -0
- data/test/cases/explain_test.rb +79 -0
- data/test/cases/filter_attributes_test.rb +153 -0
- data/test/cases/finder_respond_to_test.rb +60 -0
- data/test/cases/finder_test.rb +1676 -0
- data/test/cases/fixture_set/file_test.rb +152 -0
- data/test/cases/fixtures_test.rb +1645 -0
- data/test/cases/forbidden_attributes_protection_test.rb +130 -0
- data/test/cases/habtm_destroy_order_test.rb +61 -0
- data/test/cases/helper.rb +233 -0
- data/test/cases/hot_compatibility_test.rb +143 -0
- data/test/cases/i18n_test.rb +46 -0
- data/test/cases/inheritance_test.rb +671 -0
- data/test/cases/insert_all_test.rb +489 -0
- data/test/cases/instrumentation_test.rb +101 -0
- data/test/cases/integration_test.rb +243 -0
- data/test/cases/invalid_connection_test.rb +26 -0
- data/test/cases/invertible_migration_test.rb +527 -0
- data/test/cases/json_attribute_test.rb +35 -0
- data/test/cases/json_serialization_test.rb +310 -0
- data/test/cases/json_shared_test_cases.rb +290 -0
- data/test/cases/locking_test.rb +787 -0
- data/test/cases/log_subscriber_test.rb +267 -0
- data/test/cases/marshal_serialization_test.rb +39 -0
- data/test/cases/migration/change_schema_test.rb +504 -0
- data/test/cases/migration/change_table_test.rb +364 -0
- data/test/cases/migration/check_constraint_test.rb +162 -0
- data/test/cases/migration/column_attributes_test.rb +186 -0
- data/test/cases/migration/column_positioning_test.rb +68 -0
- data/test/cases/migration/columns_test.rb +326 -0
- data/test/cases/migration/command_recorder_test.rb +437 -0
- data/test/cases/migration/compatibility_test.rb +673 -0
- data/test/cases/migration/create_join_table_test.rb +167 -0
- data/test/cases/migration/foreign_key_test.rb +581 -0
- data/test/cases/migration/helper.rb +40 -0
- data/test/cases/migration/index_test.rb +267 -0
- data/test/cases/migration/logger_test.rb +39 -0
- data/test/cases/migration/pending_migrations_test.rb +106 -0
- data/test/cases/migration/references_foreign_key_test.rb +235 -0
- data/test/cases/migration/references_index_test.rb +120 -0
- data/test/cases/migration/references_statements_test.rb +137 -0
- data/test/cases/migration/rename_table_test.rb +116 -0
- data/test/cases/migration_test.rb +1525 -0
- data/test/cases/migrator_test.rb +527 -0
- data/test/cases/mixin_test.rb +64 -0
- data/test/cases/modules_test.rb +174 -0
- data/test/cases/multi_db_migrator_test.rb +223 -0
- data/test/cases/multiparameter_attributes_test.rb +399 -0
- data/test/cases/multiple_db_test.rb +116 -0
- data/test/cases/nested_attributes_test.rb +1119 -0
- data/test/cases/nested_attributes_with_callbacks_test.rb +146 -0
- data/test/cases/null_relation_test.rb +84 -0
- data/test/cases/numeric_data_test.rb +93 -0
- data/test/cases/persistence_test.rb +1093 -0
- data/test/cases/pooled_connections_test.rb +73 -0
- data/test/cases/prepared_statement_status_test.rb +48 -0
- data/test/cases/primary_keys_test.rb +482 -0
- data/test/cases/query_cache_test.rb +915 -0
- data/test/cases/quoting_test.rb +303 -0
- data/test/cases/readonly_test.rb +120 -0
- data/test/cases/reaper_test.rb +199 -0
- data/test/cases/reflection_test.rb +520 -0
- data/test/cases/relation/delegation_test.rb +76 -0
- data/test/cases/relation/delete_all_test.rb +117 -0
- data/test/cases/relation/merging_test.rb +434 -0
- data/test/cases/relation/mutation_test.rb +145 -0
- data/test/cases/relation/or_test.rb +192 -0
- data/test/cases/relation/predicate_builder_test.rb +31 -0
- data/test/cases/relation/record_fetch_warning_test.rb +42 -0
- data/test/cases/relation/select_test.rb +67 -0
- data/test/cases/relation/update_all_test.rb +317 -0
- data/test/cases/relation/where_chain_test.rb +141 -0
- data/test/cases/relation/where_clause_test.rb +257 -0
- data/test/cases/relation/where_test.rb +429 -0
- data/test/cases/relation_test.rb +482 -0
- data/test/cases/relations_test.rb +2251 -0
- data/test/cases/reload_models_test.rb +26 -0
- data/test/cases/reserved_word_test.rb +141 -0
- data/test/cases/result_test.rb +141 -0
- data/test/cases/sanitize_test.rb +192 -0
- data/test/cases/schema_dumper_test.rb +550 -0
- data/test/cases/schema_loading_test.rb +53 -0
- data/test/cases/scoping/default_scoping_test.rb +569 -0
- data/test/cases/scoping/named_scoping_test.rb +649 -0
- data/test/cases/scoping/relation_scoping_test.rb +522 -0
- data/test/cases/secure_token_test.rb +47 -0
- data/test/cases/serialization_test.rb +106 -0
- data/test/cases/serialized_attribute_test.rb +455 -0
- data/test/cases/signed_id_test.rb +168 -0
- data/test/cases/statement_cache_test.rb +153 -0
- data/test/cases/statement_invalid_test.rb +42 -0
- data/test/cases/store_test.rb +320 -0
- data/test/cases/strict_loading_test.rb +473 -0
- data/test/cases/suppressor_test.rb +77 -0
- data/test/cases/tasks/database_tasks_test.rb +1526 -0
- data/test/cases/tasks/mysql_rake_test.rb +417 -0
- data/test/cases/tasks/postgresql_rake_test.rb +534 -0
- data/test/cases/tasks/sqlite_rake_test.rb +267 -0
- data/test/cases/test_case.rb +142 -0
- data/test/cases/test_databases_test.rb +79 -0
- data/test/cases/test_fixtures_test.rb +96 -0
- data/test/cases/time_precision_test.rb +125 -0
- data/test/cases/timestamp_test.rb +504 -0
- data/test/cases/touch_later_test.rb +123 -0
- data/test/cases/transaction_callbacks_test.rb +772 -0
- data/test/cases/transaction_isolation_test.rb +106 -0
- data/test/cases/transactions_test.rb +1285 -0
- data/test/cases/type/adapter_specific_registry_test.rb +145 -0
- data/test/cases/type/date_time_test.rb +16 -0
- data/test/cases/type/integer_test.rb +29 -0
- data/test/cases/type/string_test.rb +24 -0
- data/test/cases/type/time_test.rb +28 -0
- data/test/cases/type/type_map_test.rb +178 -0
- data/test/cases/type/unsigned_integer_test.rb +19 -0
- data/test/cases/type_test.rb +41 -0
- data/test/cases/types_test.rb +26 -0
- data/test/cases/unconnected_test.rb +46 -0
- data/test/cases/unsafe_raw_sql_test.rb +274 -0
- data/test/cases/validations/absence_validation_test.rb +75 -0
- data/test/cases/validations/association_validation_test.rb +99 -0
- data/test/cases/validations/i18n_generate_message_validation_test.rb +102 -0
- data/test/cases/validations/i18n_validation_test.rb +87 -0
- data/test/cases/validations/length_validation_test.rb +80 -0
- data/test/cases/validations/numericality_validation_test.rb +181 -0
- data/test/cases/validations/presence_validation_test.rb +105 -0
- data/test/cases/validations/uniqueness_validation_test.rb +618 -0
- data/test/cases/validations_repair_helper.rb +21 -0
- data/test/cases/validations_test.rb +229 -0
- data/test/cases/view_test.rb +222 -0
- data/test/cases/yaml_serialization_test.rb +166 -0
- data/test/config.example.yml +97 -0
- data/test/config.rb +7 -0
- data/test/config.yml +220 -0
- data/test/connections/native_ibm_db/connection.rb +44 -0
- data/test/fixtures/accounts.yml +29 -0
- data/test/fixtures/admin/accounts.yml +2 -0
- data/test/fixtures/admin/randomly_named_a9.yml +7 -0
- data/test/fixtures/admin/randomly_named_b0.yml +7 -0
- data/test/fixtures/admin/users.yml +10 -0
- data/test/fixtures/all/admin +1 -0
- data/test/fixtures/all/developers.yml +0 -0
- data/test/fixtures/all/namespaced/accounts.yml +2 -0
- data/test/fixtures/all/people.yml +0 -0
- data/test/fixtures/all/tasks.yml +0 -0
- data/test/fixtures/author_addresses.yml +11 -0
- data/test/fixtures/author_favorites.yml +4 -0
- data/test/fixtures/authors.yml +17 -0
- data/test/fixtures/bad_posts.yml +9 -0
- data/test/fixtures/binaries.yml +137 -0
- data/test/fixtures/books.yml +38 -0
- data/test/fixtures/bulbs.yml +5 -0
- data/test/fixtures/cars.yml +9 -0
- data/test/fixtures/categories/special_categories.yml +9 -0
- data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
- data/test/fixtures/categories.yml +19 -0
- data/test/fixtures/categories_ordered.yml +7 -0
- data/test/fixtures/categories_posts.yml +34 -0
- data/test/fixtures/categorizations.yml +23 -0
- data/test/fixtures/citations.yml +5 -0
- data/test/fixtures/clubs.yml +8 -0
- data/test/fixtures/collections.yml +3 -0
- data/test/fixtures/colleges.yml +3 -0
- data/test/fixtures/comments.yml +72 -0
- data/test/fixtures/companies.yml +72 -0
- data/test/fixtures/computers.yml +12 -0
- data/test/fixtures/content.yml +3 -0
- data/test/fixtures/content_positions.yml +3 -0
- data/test/fixtures/courses.yml +8 -0
- data/test/fixtures/customers.yml +35 -0
- data/test/fixtures/dashboards.yml +6 -0
- data/test/fixtures/dead_parrots.yml +5 -0
- data/test/fixtures/developers.yml +22 -0
- data/test/fixtures/developers_projects.yml +17 -0
- data/test/fixtures/dog_lovers.yml +7 -0
- data/test/fixtures/dogs.yml +4 -0
- data/test/fixtures/doubloons.yml +3 -0
- data/test/fixtures/edges.yml +5 -0
- data/test/fixtures/entrants.yml +14 -0
- data/test/fixtures/essays.yml +16 -0
- data/test/fixtures/faces.yml +11 -0
- data/test/fixtures/fk_test_has_fk.yml +3 -0
- data/test/fixtures/fk_test_has_pk.yml +2 -0
- data/test/fixtures/friendships.yml +4 -0
- data/test/fixtures/funny_jokes.yml +10 -0
- data/test/fixtures/humans.yml +5 -0
- data/test/fixtures/interests.yml +33 -0
- data/test/fixtures/items.yml +3 -0
- data/test/fixtures/jobs.yml +7 -0
- data/test/fixtures/legacy_things.yml +3 -0
- data/test/fixtures/live_parrots.yml +4 -0
- data/test/fixtures/mateys.yml +4 -0
- data/test/fixtures/member_details.yml +8 -0
- data/test/fixtures/member_types.yml +6 -0
- data/test/fixtures/members.yml +11 -0
- data/test/fixtures/memberships.yml +41 -0
- data/test/fixtures/men.yml +5 -0
- data/test/fixtures/minimalistics.yml +5 -0
- data/test/fixtures/minivans.yml +5 -0
- data/test/fixtures/mixed_case_monkeys.yml +6 -0
- data/test/fixtures/mixins.yml +29 -0
- data/test/fixtures/movies.yml +7 -0
- data/test/fixtures/naked/yml/accounts.yml +1 -0
- data/test/fixtures/naked/yml/companies.yml +1 -0
- data/test/fixtures/naked/yml/courses.yml +1 -0
- data/test/fixtures/naked/yml/courses_with_invalid_key.yml +3 -0
- data/test/fixtures/naked/yml/parrots.yml +3 -0
- data/test/fixtures/naked/yml/trees.yml +3 -0
- data/test/fixtures/nodes.yml +29 -0
- data/test/fixtures/organizations.yml +5 -0
- data/test/fixtures/other_books.yml +26 -0
- data/test/fixtures/other_comments.yml +6 -0
- data/test/fixtures/other_dogs.yml +2 -0
- data/test/fixtures/other_posts.yml +8 -0
- data/test/fixtures/other_topics.yml +42 -0
- data/test/fixtures/owners.yml +9 -0
- data/test/fixtures/parrots.yml +33 -0
- data/test/fixtures/parrots_pirates.yml +7 -0
- data/test/fixtures/people.yml +24 -0
- data/test/fixtures/peoples_treasures.yml +3 -0
- data/test/fixtures/pets.yml +19 -0
- data/test/fixtures/pirates.yml +15 -0
- data/test/fixtures/posts.yml +88 -0
- data/test/fixtures/price_estimates.yml +16 -0
- data/test/fixtures/products.yml +4 -0
- data/test/fixtures/projects.yml +7 -0
- data/test/fixtures/randomly_named_a9.yml +7 -0
- data/test/fixtures/ratings.yml +14 -0
- data/test/fixtures/readers.yml +17 -0
- data/test/fixtures/references.yml +17 -0
- data/test/fixtures/reserved_words/distinct.yml +5 -0
- data/test/fixtures/reserved_words/distinct_select.yml +11 -0
- data/test/fixtures/reserved_words/group.yml +14 -0
- data/test/fixtures/reserved_words/select.yml +8 -0
- data/test/fixtures/reserved_words/values.yml +7 -0
- data/test/fixtures/ships.yml +6 -0
- data/test/fixtures/speedometers.yml +8 -0
- data/test/fixtures/sponsors.yml +15 -0
- data/test/fixtures/strict_zines.yml +2 -0
- data/test/fixtures/string_key_objects.yml +7 -0
- data/test/fixtures/subscribers.yml +11 -0
- data/test/fixtures/subscriptions.yml +12 -0
- data/test/fixtures/taggings.yml +78 -0
- data/test/fixtures/tags.yml +11 -0
- data/test/fixtures/tasks.yml +7 -0
- data/test/fixtures/teapots.yml +3 -0
- data/test/fixtures/to_be_linked/accounts.yml +2 -0
- data/test/fixtures/to_be_linked/users.yml +10 -0
- data/test/fixtures/topics.yml +49 -0
- data/test/fixtures/toys.yml +14 -0
- data/test/fixtures/traffic_lights.yml +10 -0
- data/test/fixtures/treasures.yml +10 -0
- data/test/fixtures/trees.yml +3 -0
- data/test/fixtures/uuid_children.yml +3 -0
- data/test/fixtures/uuid_parents.yml +2 -0
- data/test/fixtures/variants.yml +4 -0
- data/test/fixtures/vegetables.yml +20 -0
- data/test/fixtures/vertices.yml +4 -0
- data/test/fixtures/warehouse-things.yml +3 -0
- data/test/fixtures/warehouse_things.yml +3 -0
- data/test/fixtures/zines.yml +5 -0
- data/test/ibm_db_test.rb +25 -0
- data/test/migrations/10_urban/9_add_expressions.rb +13 -0
- data/test/migrations/decimal/1_give_me_big_numbers.rb +17 -0
- data/test/migrations/magic/1_currencies_have_symbols.rb +13 -0
- data/test/migrations/missing/1000_people_have_middle_names.rb +11 -0
- data/test/migrations/missing/1_people_have_last_names.rb +11 -0
- data/test/migrations/missing/3_we_need_reminders.rb +14 -0
- data/test/migrations/missing/4_innocent_jointable.rb +14 -0
- data/test/migrations/rename/1_we_need_things.rb +13 -0
- data/test/migrations/rename/2_rename_things.rb +11 -0
- data/test/migrations/to_copy/1_people_have_hobbies.rb +11 -0
- data/test/migrations/to_copy/2_people_have_descriptions.rb +11 -0
- data/test/migrations/to_copy2/1_create_articles.rb +9 -0
- data/test/migrations/to_copy2/2_create_comments.rb +9 -0
- data/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb +11 -0
- data/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb +11 -0
- data/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb +11 -0
- data/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb +9 -0
- data/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb +9 -0
- data/test/migrations/valid/1_valid_people_have_last_names.rb +11 -0
- data/test/migrations/valid/2_we_need_reminders.rb +14 -0
- data/test/migrations/valid/3_innocent_jointable.rb +14 -0
- data/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb +11 -0
- data/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb +14 -0
- data/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb +14 -0
- data/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb +11 -0
- data/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb +14 -0
- data/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb +14 -0
- data/test/migrations/version_check/20131219224947_migration_version_check.rb +10 -0
- data/test/models/account.rb +46 -0
- data/test/models/admin/account.rb +5 -0
- data/test/models/admin/randomly_named_c1.rb +9 -0
- data/test/models/admin/user.rb +48 -0
- data/test/models/admin.rb +7 -0
- data/test/models/aircraft.rb +7 -0
- data/test/models/arunit2_model.rb +5 -0
- data/test/models/author.rb +260 -0
- data/test/models/auto_id.rb +6 -0
- data/test/models/autoloadable/extra_firm.rb +4 -0
- data/test/models/binary.rb +4 -0
- data/test/models/binary_field.rb +6 -0
- data/test/models/bird.rb +24 -0
- data/test/models/book.rb +33 -0
- data/test/models/book_destroy_async.rb +24 -0
- data/test/models/boolean.rb +7 -0
- data/test/models/bulb.rb +61 -0
- data/test/models/cake_designer.rb +5 -0
- data/test/models/car.rb +36 -0
- data/test/models/carrier.rb +4 -0
- data/test/models/cart.rb +5 -0
- data/test/models/cat.rb +12 -0
- data/test/models/categorization.rb +21 -0
- data/test/models/category.rb +47 -0
- data/test/models/chef.rb +10 -0
- data/test/models/citation.rb +7 -0
- data/test/models/club.rb +28 -0
- data/test/models/college.rb +12 -0
- data/test/models/column.rb +5 -0
- data/test/models/column_name.rb +5 -0
- data/test/models/comment.rb +98 -0
- data/test/models/company.rb +226 -0
- data/test/models/company_in_module.rb +99 -0
- data/test/models/computer.rb +5 -0
- data/test/models/contact.rb +43 -0
- data/test/models/content.rb +42 -0
- data/test/models/contract.rb +38 -0
- data/test/models/country.rb +5 -0
- data/test/models/course.rb +8 -0
- data/test/models/customer.rb +85 -0
- data/test/models/customer_carrier.rb +16 -0
- data/test/models/dashboard.rb +5 -0
- data/test/models/default.rb +4 -0
- data/test/models/department.rb +6 -0
- data/test/models/destroy_async_parent.rb +15 -0
- data/test/models/destroy_async_parent_soft_delete.rb +20 -0
- data/test/models/developer.rb +341 -0
- data/test/models/dl_keyed_belongs_to.rb +13 -0
- data/test/models/dl_keyed_belongs_to_soft_delete.rb +19 -0
- data/test/models/dl_keyed_has_many.rb +5 -0
- data/test/models/dl_keyed_has_many_through.rb +5 -0
- data/test/models/dl_keyed_has_one.rb +5 -0
- data/test/models/dl_keyed_join.rb +10 -0
- data/test/models/dog.rb +7 -0
- data/test/models/dog_lover.rb +7 -0
- data/test/models/doubloon.rb +14 -0
- data/test/models/drink_designer.rb +20 -0
- data/test/models/edge.rb +7 -0
- data/test/models/electron.rb +7 -0
- data/test/models/engine.rb +5 -0
- data/test/models/entrant.rb +5 -0
- data/test/models/entry.rb +5 -0
- data/test/models/essay.rb +8 -0
- data/test/models/essay_destroy_async.rb +12 -0
- data/test/models/event.rb +5 -0
- data/test/models/eye.rb +39 -0
- data/test/models/face.rb +17 -0
- data/test/models/family.rb +6 -0
- data/test/models/family_tree.rb +6 -0
- data/test/models/friendship.rb +8 -0
- data/test/models/frog.rb +8 -0
- data/test/models/guid.rb +4 -0
- data/test/models/guitar.rb +6 -0
- data/test/models/hotel.rb +13 -0
- data/test/models/human.rb +39 -0
- data/test/models/image.rb +5 -0
- data/test/models/interest.rb +16 -0
- data/test/models/invoice.rb +6 -0
- data/test/models/item.rb +9 -0
- data/test/models/job.rb +9 -0
- data/test/models/joke.rb +9 -0
- data/test/models/keyboard.rb +5 -0
- data/test/models/legacy_thing.rb +5 -0
- data/test/models/lesson.rb +13 -0
- data/test/models/line_item.rb +5 -0
- data/test/models/liquid.rb +6 -0
- data/test/models/man.rb +11 -0
- data/test/models/matey.rb +6 -0
- data/test/models/measurement.rb +4 -0
- data/test/models/member.rb +45 -0
- data/test/models/member_detail.rb +11 -0
- data/test/models/member_type.rb +5 -0
- data/test/models/membership.rb +38 -0
- data/test/models/mentor.rb +5 -0
- data/test/models/message.rb +5 -0
- data/test/models/minimalistic.rb +4 -0
- data/test/models/minivan.rb +10 -0
- data/test/models/mixed_case_monkey.rb +5 -0
- data/test/models/mocktail_designer.rb +2 -0
- data/test/models/molecule.rb +8 -0
- data/test/models/mouse.rb +6 -0
- data/test/models/movie.rb +7 -0
- data/test/models/node.rb +7 -0
- data/test/models/non_primary_key.rb +4 -0
- data/test/models/notification.rb +5 -0
- data/test/models/numeric_data.rb +12 -0
- data/test/models/order.rb +6 -0
- data/test/models/organization.rb +16 -0
- data/test/models/other_dog.rb +7 -0
- data/test/models/owner.rb +39 -0
- data/test/models/parrot.rb +36 -0
- data/test/models/person.rb +147 -0
- data/test/models/personal_legacy_thing.rb +6 -0
- data/test/models/pet.rb +20 -0
- data/test/models/pet_treasure.rb +8 -0
- data/test/models/pirate.rb +116 -0
- data/test/models/possession.rb +5 -0
- data/test/models/post.rb +371 -0
- data/test/models/price_estimate.rb +14 -0
- data/test/models/professor.rb +7 -0
- data/test/models/project.rb +42 -0
- data/test/models/publisher/article.rb +6 -0
- data/test/models/publisher/magazine.rb +5 -0
- data/test/models/publisher.rb +4 -0
- data/test/models/randomly_named_c1.rb +5 -0
- data/test/models/rating.rb +8 -0
- data/test/models/reader.rb +25 -0
- data/test/models/recipe.rb +5 -0
- data/test/models/record.rb +4 -0
- data/test/models/reference.rb +25 -0
- data/test/models/reply.rb +79 -0
- data/test/models/room.rb +6 -0
- data/test/models/section.rb +6 -0
- data/test/models/seminar.rb +6 -0
- data/test/models/session.rb +6 -0
- data/test/models/ship.rb +42 -0
- data/test/models/ship_part.rb +10 -0
- data/test/models/shop.rb +19 -0
- data/test/models/shop_account.rb +8 -0
- data/test/models/speedometer.rb +8 -0
- data/test/models/sponsor.rb +10 -0
- data/test/models/squeak.rb +6 -0
- data/test/models/strict_zine.rb +7 -0
- data/test/models/string_key_object.rb +5 -0
- data/test/models/student.rb +6 -0
- data/test/models/subject.rb +16 -0
- data/test/models/subscriber.rb +10 -0
- data/test/models/subscription.rb +8 -0
- data/test/models/tag.rb +16 -0
- data/test/models/tagging.rb +20 -0
- data/test/models/task.rb +7 -0
- data/test/models/topic.rb +153 -0
- data/test/models/toy.rb +10 -0
- data/test/models/traffic_light.rb +6 -0
- data/test/models/treasure.rb +16 -0
- data/test/models/treaty.rb +5 -0
- data/test/models/tree.rb +5 -0
- data/test/models/tuning_peg.rb +6 -0
- data/test/models/tyre.rb +13 -0
- data/test/models/user.rb +22 -0
- data/test/models/uuid_child.rb +5 -0
- data/test/models/uuid_item.rb +8 -0
- data/test/models/uuid_parent.rb +5 -0
- data/test/models/vegetables.rb +33 -0
- data/test/models/vehicle.rb +7 -0
- data/test/models/vertex.rb +11 -0
- data/test/models/warehouse_thing.rb +7 -0
- data/test/models/wheel.rb +5 -0
- data/test/models/without_table.rb +5 -0
- data/test/models/zine.rb +5 -0
- data/test/schema/i5/ibm_db_specific_schema.rb +137 -0
- data/test/schema/ids/ibm_db_specific_schema.rb +140 -0
- data/test/schema/luw/ibm_db_specific_schema.rb +137 -0
- data/test/schema/mysql2_specific_schema.rb +82 -0
- data/test/schema/oracle_specific_schema.rb +38 -0
- data/test/schema/postgresql_specific_schema.rb +125 -0
- data/test/schema/schema.rb +1237 -0
- data/test/schema/schema.rb.original +1057 -0
- data/test/schema/sqlite_specific_schema.rb +11 -0
- data/test/schema/zOS/ibm_db_specific_schema.rb +208 -0
- data/test/support/config.rb +43 -0
- data/test/support/connection.rb +29 -0
- data/test/support/connection_helper.rb +16 -0
- data/test/support/ddl_helper.rb +10 -0
- data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_6_0_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_6_0_topic_associations.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/Mysql2/rails_6_0_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/Mysql2/rails_6_0_topic_associations.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_6_0_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_6_0_topic_associations.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLite/rails_6_0_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLite/rails_6_0_topic_associations.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/legacy_6_0_record_mysql.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/legacy_relation.dump +0 -0
- data/test/support/schema_dumping_helper.rb +22 -0
- data/test/support/stubs/strong_parameters.rb +40 -0
- data/test/support/yaml_compatibility_fixtures/rails_4_1.yml +22 -0
- data/test/support/yaml_compatibility_fixtures/rails_4_2_0.yml +182 -0
- data/test/support/yaml_compatibility_fixtures/rails_v1_mysql.yml +206 -0
- data/test/support/yaml_compatibility_fixtures/rails_v2.yml +55 -0
- 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
|