ibm_db 5.6.1 → 5.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/Makefile +13 -9
- data/ext/extconf.rb +176 -110
- data/ext/ibm_db.c +60 -0
- data/ext/ibm_db.o +0 -0
- data/ext/ibm_db.so +0 -0
- data/ext/mkmf.log +6 -6
- data/ext/ruby_ibm_db_cli.c +14 -10
- data/ext/ruby_ibm_db_cli.h +4 -0
- data/ext/ruby_ibm_db_cli.o +0 -0
- data/lib/IBM_DB.rb +2 -2
- data/lib/active_record/connection_adapters/ibm_db_adapter.rb +1254 -277
- data/test/active_record/connection_adapters/fake_legacy_adapter.rb +17 -0
- data/test/active_record/connection_adapters/fake_misleading_legacy_adapter.rb +8 -0
- data/test/activejob/destroy_association_async_job_test.rb +62 -0
- data/test/activejob/destroy_association_async_test.rb +147 -11
- data/test/activejob/job_runtime_test.rb +41 -0
- data/test/activejob/unloadable_base_job.rb +3 -0
- data/test/cases/active_record_schema_test.rb +196 -0
- data/test/cases/active_record_test.rb +21 -0
- data/test/cases/adapter_prevent_writes_test.rb +26 -202
- data/test/cases/adapter_test.rb +592 -82
- data/test/cases/adapters/abstract_mysql_adapter/active_schema_test.rb +210 -0
- data/test/cases/adapters/abstract_mysql_adapter/adapter_prevent_writes_test.rb +111 -0
- data/test/cases/adapters/abstract_mysql_adapter/auto_increment_test.rb +48 -0
- data/test/cases/adapters/abstract_mysql_adapter/bind_parameter_test.rb +91 -0
- data/test/cases/adapters/abstract_mysql_adapter/case_sensitivity_test.rb +65 -0
- data/test/cases/adapters/abstract_mysql_adapter/charset_collation_test.rb +85 -0
- data/test/cases/adapters/abstract_mysql_adapter/connection_test.rb +267 -0
- data/test/cases/adapters/abstract_mysql_adapter/count_deleted_rows_with_lock_test.rb +29 -0
- data/test/cases/adapters/abstract_mysql_adapter/json_test.rb +24 -0
- data/test/cases/adapters/abstract_mysql_adapter/mysql_boolean_test.rb +102 -0
- data/test/cases/adapters/abstract_mysql_adapter/mysql_enum_test.rb +47 -0
- data/test/cases/adapters/abstract_mysql_adapter/mysql_explain_test.rb +71 -0
- data/test/cases/adapters/abstract_mysql_adapter/nested_deadlock_test.rb +191 -0
- data/test/cases/adapters/abstract_mysql_adapter/optimizer_hints_test.rb +69 -0
- data/test/cases/adapters/abstract_mysql_adapter/quoting_test.rb +49 -0
- data/test/cases/adapters/abstract_mysql_adapter/schema_migrations_test.rb +68 -0
- data/test/cases/adapters/abstract_mysql_adapter/schema_test.rb +130 -0
- data/test/cases/adapters/abstract_mysql_adapter/set_test.rb +32 -0
- data/test/cases/adapters/abstract_mysql_adapter/sp_test.rb +39 -0
- data/test/cases/adapters/abstract_mysql_adapter/sql_types_test.rb +16 -0
- data/test/cases/adapters/abstract_mysql_adapter/table_options_test.rb +125 -0
- data/test/cases/adapters/abstract_mysql_adapter/transaction_test.rb +204 -0
- data/test/cases/adapters/abstract_mysql_adapter/unsigned_type_test.rb +77 -0
- data/test/cases/adapters/abstract_mysql_adapter/virtual_column_test.rb +69 -0
- data/test/cases/adapters/abstract_mysql_adapter/warnings_test.rb +109 -0
- data/test/cases/adapters/mysql2/check_constraint_quoting_test.rb +35 -0
- data/test/cases/adapters/mysql2/dbconsole_test.rb +87 -0
- data/test/cases/adapters/mysql2/mysql2_adapter_test.rb +136 -10
- data/test/cases/adapters/mysql2/mysql2_rake_test.rb +433 -0
- data/test/cases/adapters/postgresql/active_schema_test.rb +8 -4
- data/test/cases/adapters/postgresql/array_test.rb +6 -6
- data/test/cases/adapters/postgresql/bind_parameter_test.rb +52 -0
- data/test/cases/adapters/postgresql/bit_string_test.rb +2 -2
- data/test/cases/adapters/postgresql/bytea_test.rb +3 -3
- data/test/cases/adapters/postgresql/case_insensitive_test.rb +1 -1
- data/test/cases/adapters/postgresql/change_schema_test.rb +32 -1
- data/test/cases/adapters/postgresql/cidr_test.rb +17 -0
- data/test/cases/adapters/postgresql/citext_test.rb +7 -1
- data/test/cases/adapters/postgresql/collation_test.rb +1 -1
- data/test/cases/adapters/postgresql/composite_test.rb +1 -1
- data/test/cases/adapters/postgresql/connection_test.rb +43 -36
- data/test/cases/adapters/postgresql/create_unlogged_tables_test.rb +3 -2
- data/test/cases/adapters/postgresql/datatype_test.rb +9 -4
- data/test/cases/adapters/postgresql/date_test.rb +2 -2
- data/test/cases/adapters/postgresql/dbconsole_test.rb +109 -0
- data/test/cases/adapters/postgresql/deferred_constraints_test.rb +55 -0
- data/test/cases/adapters/postgresql/domain_test.rb +1 -1
- data/test/cases/adapters/postgresql/enum_test.rb +208 -8
- data/test/cases/adapters/postgresql/explain_test.rb +25 -6
- data/test/cases/adapters/postgresql/extension_migration_test.rb +51 -9
- data/test/cases/adapters/postgresql/foreign_table_test.rb +3 -3
- data/test/cases/adapters/postgresql/full_text_test.rb +1 -1
- data/test/cases/adapters/postgresql/geometric_test.rb +13 -6
- data/test/cases/adapters/postgresql/hstore_test.rb +22 -32
- data/test/cases/adapters/postgresql/infinity_test.rb +13 -2
- data/test/cases/adapters/postgresql/integer_test.rb +1 -1
- data/test/cases/adapters/postgresql/interval_test.rb +9 -17
- data/test/cases/adapters/postgresql/invertible_migration_test.rb +139 -0
- data/test/cases/adapters/postgresql/json_test.rb +5 -0
- data/test/cases/adapters/postgresql/ltree_test.rb +1 -1
- data/test/cases/adapters/postgresql/money_test.rb +17 -9
- data/test/cases/adapters/postgresql/network_test.rb +13 -1
- data/test/cases/adapters/postgresql/numbers_test.rb +18 -2
- data/test/cases/adapters/postgresql/optimizer_hints_test.rb +9 -9
- data/test/cases/adapters/postgresql/partitions_test.rb +5 -2
- data/test/cases/adapters/postgresql/postgresql_adapter_prevent_writes_test.rb +7 -110
- data/test/cases/adapters/postgresql/postgresql_adapter_test.rb +403 -20
- data/test/cases/adapters/postgresql/postgresql_rake_test.rb +637 -0
- data/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb +1 -1
- data/test/cases/adapters/postgresql/quoting_test.rb +60 -1
- data/test/cases/adapters/postgresql/range_test.rb +185 -21
- data/test/cases/adapters/postgresql/referential_integrity_test.rb +29 -5
- data/test/cases/adapters/postgresql/rename_table_test.rb +35 -8
- data/test/cases/adapters/postgresql/schema_authorization_test.rb +2 -2
- data/test/cases/adapters/postgresql/schema_test.rb +270 -20
- data/test/cases/adapters/postgresql/serial_test.rb +5 -5
- data/test/cases/adapters/postgresql/statement_pool_test.rb +6 -2
- data/test/cases/adapters/postgresql/timestamp_test.rb +122 -5
- data/test/cases/adapters/postgresql/transaction_nested_test.rb +130 -6
- data/test/cases/adapters/postgresql/transaction_test.rb +37 -11
- data/test/cases/adapters/postgresql/type_lookup_test.rb +1 -1
- data/test/cases/adapters/postgresql/uuid_test.rb +77 -9
- data/test/cases/adapters/postgresql/virtual_column_test.rb +114 -0
- data/test/cases/adapters/postgresql/xml_test.rb +1 -1
- data/test/cases/adapters/sqlite3/bind_parameter_test.rb +52 -0
- data/test/cases/adapters/sqlite3/collation_test.rb +1 -1
- data/test/cases/adapters/sqlite3/copy_table_test.rb +28 -7
- data/test/cases/adapters/sqlite3/dbconsole_test.rb +81 -0
- data/test/cases/adapters/sqlite3/explain_test.rb +3 -3
- data/test/cases/adapters/sqlite3/quoting_test.rb +34 -3
- data/test/cases/adapters/sqlite3/sqlite3_adapter_prevent_writes_test.rb +9 -102
- data/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +727 -72
- data/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb +6 -3
- data/test/cases/adapters/sqlite3/sqlite_rake_test.rb +264 -0
- data/test/cases/adapters/sqlite3/statement_pool_test.rb +1 -1
- data/test/cases/adapters/sqlite3/transaction_test.rb +6 -3
- data/test/cases/adapters/sqlite3/virtual_column_test.rb +138 -0
- data/test/cases/adapters/sqlite3/virtual_table_test.rb +51 -0
- data/test/cases/adapters/trilogy/dbconsole_test.rb +76 -0
- data/test/cases/adapters/trilogy/trilogy_adapter_test.rb +473 -0
- data/test/cases/adapters/trilogy/trilogy_rake_test.rb +433 -0
- data/test/cases/aggregations_test.rb +16 -0
- data/test/cases/annotate_test.rb +14 -12
- data/test/cases/arel/attributes/attribute_test.rb +86 -47
- data/test/cases/arel/collectors/bind_test.rb +1 -1
- data/test/cases/arel/collectors/composite_test.rb +11 -1
- data/test/cases/arel/collectors/sql_string_test.rb +1 -1
- data/test/cases/arel/collectors/substitute_bind_collector_test.rb +1 -1
- data/test/cases/arel/delete_manager_test.rb +0 -6
- data/test/cases/arel/factory_methods_test.rb +45 -0
- data/test/cases/arel/insert_manager_test.rb +1 -7
- data/test/cases/arel/nodes/as_test.rb +12 -0
- data/test/cases/arel/nodes/ascending_test.rb +1 -1
- data/test/cases/arel/nodes/bin_test.rb +1 -1
- data/test/cases/arel/nodes/bound_sql_literal_test.rb +31 -0
- data/test/cases/arel/nodes/cte_test.rb +46 -0
- data/test/cases/arel/nodes/descending_test.rb +1 -1
- data/test/cases/arel/nodes/equality_test.rb +3 -3
- data/test/cases/arel/nodes/filter_test.rb +37 -0
- data/test/cases/arel/nodes/fragments_test.rb +38 -0
- data/test/cases/arel/nodes/homogeneous_in_test.rb +52 -0
- data/test/cases/arel/nodes/infix_operation_test.rb +1 -1
- data/test/cases/arel/nodes/node_test.rb +1 -1
- data/test/cases/arel/nodes/or_test.rb +2 -2
- data/test/cases/arel/nodes/sql_literal_test.rb +18 -1
- data/test/cases/arel/nodes/table_alias_test.rb +12 -0
- data/test/cases/arel/nodes/unary_operation_test.rb +1 -1
- data/test/cases/arel/nodes/update_statement_test.rb +4 -0
- data/test/cases/arel/nodes_test.rb +3 -2
- data/test/cases/arel/select_manager_test.rb +12 -12
- data/test/cases/arel/support/fake_record.rb +22 -17
- data/test/cases/arel/table_test.rb +15 -25
- data/test/cases/arel/update_manager_test.rb +51 -6
- data/test/cases/arel/visitors/dispatch_contamination_test.rb +1 -1
- data/test/cases/arel/visitors/dot_test.rb +177 -2
- data/test/cases/arel/visitors/mysql_test.rb +49 -1
- data/test/cases/arel/visitors/postgres_test.rb +1 -31
- data/test/cases/arel/visitors/sqlite_test.rb +1 -1
- data/test/cases/arel/visitors/to_sql_test.rb +330 -20
- data/test/cases/assertions/query_assertions_test.rb +136 -0
- data/test/cases/associations/belongs_to_associations_test.rb +502 -41
- data/test/cases/associations/bidirectional_destroy_dependencies_test.rb +1 -1
- data/test/cases/associations/cascaded_eager_loading_test.rb +26 -15
- data/test/cases/associations/eager_load_nested_include_test.rb +1 -1
- data/test/cases/associations/eager_singularization_test.rb +1 -1
- data/test/cases/associations/eager_test.rb +255 -93
- data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +25 -53
- data/test/cases/associations/has_many_associations_test.rb +282 -72
- data/test/cases/associations/has_many_through_associations_test.rb +172 -45
- data/test/cases/associations/has_many_through_disable_joins_associations_test.rb +197 -0
- data/test/cases/associations/has_one_associations_test.rb +160 -29
- data/test/cases/associations/has_one_through_associations_test.rb +64 -15
- data/test/cases/associations/has_one_through_disable_joins_associations_test.rb +82 -0
- data/test/cases/associations/inner_join_association_test.rb +36 -6
- data/test/cases/associations/inverse_associations_test.rb +177 -40
- data/test/cases/associations/join_model_test.rb +17 -12
- data/test/cases/associations/left_outer_join_association_test.rb +14 -3
- data/test/cases/associations/nested_error_test.rb +116 -0
- data/test/cases/associations/nested_through_associations_test.rb +54 -19
- data/test/cases/associations/required_test.rb +1 -1
- data/test/cases/associations_test.rb +1278 -21
- data/test/cases/asynchronous_queries_test.rb +310 -0
- data/test/cases/attribute_methods/read_test.rb +6 -0
- data/test/cases/attribute_methods/time_zone_converter_test.rb +21 -0
- data/test/cases/attribute_methods_test.rb +565 -46
- data/test/cases/attributes_test.rb +42 -1
- data/test/cases/autosave_association_test.rb +645 -92
- data/test/cases/base_prevent_writes_test.rb +28 -159
- data/test/cases/base_test.rb +434 -209
- data/test/cases/batches_test.rb +441 -57
- data/test/cases/binary_test.rb +31 -0
- data/test/cases/bind_parameter_test.rb +107 -16
- data/test/cases/cache_key_test.rb +7 -7
- data/test/cases/calculations_test.rb +541 -110
- data/test/cases/callbacks_test.rb +0 -1
- data/test/cases/clone_test.rb +5 -4
- data/test/cases/coders/yaml_column_test.rb +97 -2
- data/test/cases/collection_cache_key_test.rb +24 -16
- data/test/cases/column_alias_test.rb +2 -8
- data/test/cases/column_definition_test.rb +12 -3
- data/test/cases/comment_test.rb +23 -26
- data/test/cases/connection_adapters/adapter_leasing_test.rb +9 -9
- data/test/cases/connection_adapters/connection_handler_test.rb +145 -99
- data/test/cases/connection_adapters/connection_handlers_multi_db_test.rb +79 -55
- data/test/cases/connection_adapters/connection_handlers_multi_pool_config_test.rb +17 -20
- data/test/cases/connection_adapters/connection_handlers_sharding_db_test.rb +146 -55
- data/test/cases/connection_adapters/connection_swapping_nested_test.rb +136 -23
- data/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +96 -27
- data/test/cases/connection_adapters/mysql_type_lookup_test.rb +5 -5
- data/test/cases/connection_adapters/registration_test.rb +98 -0
- data/test/cases/connection_adapters/schema_cache_test.rb +288 -135
- data/test/cases/connection_adapters/standalone_connection_test.rb +38 -0
- data/test/cases/connection_adapters/statement_pool_test.rb +29 -0
- data/test/cases/connection_adapters/type_lookup_test.rb +12 -24
- data/test/cases/connection_handling_test.rb +210 -0
- data/test/cases/connection_management_test.rb +44 -7
- data/test/cases/connection_pool_test.rb +466 -99
- data/test/cases/core_test.rb +167 -9
- data/test/cases/counter_cache_test.rb +129 -2
- data/test/cases/custom_locking_test.rb +3 -3
- data/test/cases/database_configurations/hash_config_test.rb +149 -13
- data/test/cases/database_configurations/resolver_test.rb +14 -10
- data/test/cases/database_configurations/url_config_test.rb +53 -0
- data/test/cases/database_configurations_test.rb +86 -43
- data/test/cases/database_selector_test.rb +30 -8
- data/test/cases/database_statements_test.rb +10 -14
- data/test/cases/date_time_precision_test.rb +141 -11
- data/test/cases/date_time_test.rb +19 -1
- data/test/cases/defaults_test.rb +107 -41
- data/test/cases/delegated_type_test.rb +69 -9
- data/test/cases/dirty_test.rb +82 -25
- data/test/cases/disconnected_test.rb +10 -9
- data/test/cases/dup_test.rb +26 -6
- data/test/cases/encryption/cipher/aes256_gcm_test.rb +50 -0
- data/test/cases/encryption/cipher_test.rb +65 -0
- data/test/cases/encryption/concurrency_test.rb +27 -0
- data/test/cases/encryption/config_test.rb +21 -0
- data/test/cases/encryption/configurable_test.rb +93 -0
- data/test/cases/encryption/contexts_test.rb +105 -0
- data/test/cases/encryption/derived_secret_key_provider_test.rb +31 -0
- data/test/cases/encryption/deterministic_key_provider_test.rb +11 -0
- data/test/cases/encryption/encryptable_record_api_test.rb +175 -0
- data/test/cases/encryption/encryptable_record_message_pack_serialized_test.rb +42 -0
- data/test/cases/encryption/encryptable_record_test.rb +470 -0
- data/test/cases/encryption/encrypted_fixtures_test.rb +22 -0
- data/test/cases/encryption/encrypting_only_encryptor_test.rb +20 -0
- data/test/cases/encryption/encryption_schemes_test.rb +240 -0
- data/test/cases/encryption/encryptor_test.rb +113 -0
- data/test/cases/encryption/envelope_encryption_key_provider_test.rb +49 -0
- data/test/cases/encryption/extended_deterministic_queries_test.rb +81 -0
- data/test/cases/encryption/helper.rb +152 -0
- data/test/cases/encryption/key_generator_test.rb +55 -0
- data/test/cases/encryption/key_provider_test.rb +57 -0
- data/test/cases/encryption/key_test.rb +17 -0
- data/test/cases/encryption/message_pack_message_serializer_test.rb +70 -0
- data/test/cases/encryption/message_serializer_test.rb +82 -0
- data/test/cases/encryption/message_test.rb +43 -0
- data/test/cases/encryption/null_encryptor_test.rb +21 -0
- data/test/cases/encryption/properties_test.rb +69 -0
- data/test/cases/encryption/read_only_null_encryptor_test.rb +19 -0
- data/test/cases/encryption/scheme_test.rb +58 -0
- data/test/cases/encryption/unencrypted_attributes_test.rb +27 -0
- data/test/cases/encryption/uniqueness_validations_test.rb +65 -0
- data/test/cases/enum_test.rb +374 -72
- data/test/cases/errors_test.rb +7 -4
- data/test/cases/excluding_test.rb +116 -0
- data/test/cases/explain_subscriber_test.rb +6 -1
- data/test/cases/explain_test.rb +119 -10
- data/test/cases/filter_attributes_test.rb +7 -14
- data/test/cases/finder_respond_to_test.rb +3 -3
- data/test/cases/finder_test.rb +357 -72
- data/test/cases/fixture_set/file_test.rb +9 -0
- data/test/cases/fixtures_test.rb +426 -190
- data/test/cases/helper.rb +66 -192
- data/test/cases/hot_compatibility_test.rb +10 -10
- data/test/cases/inheritance_test.rb +51 -61
- data/test/cases/inherited_test.rb +40 -0
- data/test/cases/insert_all_test.rb +419 -9
- data/test/cases/instrumentation_test.rb +202 -30
- data/test/cases/integration_test.rb +25 -7
- data/test/cases/invalid_connection_test.rb +5 -5
- data/test/cases/invertible_migration_test.rb +108 -24
- data/test/cases/json_attribute_test.rb +1 -0
- data/test/cases/json_serialization_test.rb +5 -5
- data/test/cases/json_shared_test_cases.rb +5 -9
- data/test/cases/locking_test.rb +67 -31
- data/test/cases/log_subscriber_test.rb +17 -15
- data/test/cases/marshal_serialization_test.rb +78 -5
- data/test/cases/message_pack_test.rb +91 -0
- data/test/cases/migration/change_schema_test.rb +77 -46
- data/test/cases/migration/change_table_test.rb +129 -114
- data/test/cases/migration/check_constraint_test.rb +192 -10
- data/test/cases/migration/column_attributes_test.rb +4 -9
- data/test/cases/migration/column_positioning_test.rb +2 -2
- data/test/cases/migration/columns_test.rb +118 -4
- data/test/cases/migration/command_recorder_test.rb +181 -14
- data/test/cases/migration/compatibility_test.rb +883 -196
- data/test/cases/migration/create_join_table_test.rb +8 -1
- data/test/cases/migration/exclusion_constraint_test.rb +192 -0
- data/test/cases/migration/foreign_key_test.rb +356 -14
- data/test/cases/migration/helper.rb +4 -1
- data/test/cases/migration/index_test.rb +67 -2
- data/test/cases/migration/invalid_options_test.rb +129 -0
- data/test/cases/migration/logger_test.rb +7 -4
- data/test/cases/migration/pending_migrations_test.rb +103 -60
- data/test/cases/migration/references_foreign_key_test.rb +73 -4
- data/test/cases/migration/references_index_test.rb +25 -27
- data/test/cases/migration/references_statements_test.rb +27 -3
- data/test/cases/migration/rename_table_test.rb +39 -54
- data/test/cases/migration/schema_definitions_test.rb +109 -0
- data/test/cases/migration/unique_constraint_test.rb +248 -0
- data/test/cases/migration_test.rb +677 -220
- data/test/cases/migrator_test.rb +102 -75
- data/test/cases/multi_db_migrator_test.rb +55 -34
- data/test/cases/multiparameter_attributes_test.rb +26 -29
- data/test/cases/multiple_db_test.rb +42 -19
- data/test/cases/nested_attributes_test.rb +124 -6
- data/test/cases/nested_attributes_with_callbacks_test.rb +2 -2
- data/test/cases/normalized_attribute_test.rb +117 -0
- data/test/cases/null_relation_test.rb +35 -2
- data/test/cases/persistence/reload_association_cache_test.rb +20 -0
- data/test/cases/persistence_test.rb +629 -22
- data/test/cases/pooled_connections_test.rb +57 -56
- data/test/cases/prepared_statement_status_test.rb +8 -3
- data/test/cases/primary_class_test.rb +134 -0
- data/test/cases/primary_keys_test.rb +171 -55
- data/test/cases/query_cache_test.rb +558 -263
- data/test/cases/query_logs_test.rb +293 -0
- data/test/cases/quoting_test.rb +39 -72
- data/test/cases/readonly_test.rb +50 -12
- data/test/cases/reaper_test.rb +22 -6
- data/test/cases/reflection_test.rb +207 -9
- data/test/cases/relation/and_test.rb +43 -0
- data/test/cases/relation/delegation_test.rb +32 -5
- data/test/cases/relation/delete_all_test.rb +27 -6
- data/test/cases/relation/field_ordered_values_test.rb +128 -0
- data/test/cases/relation/load_async_test.rb +650 -0
- data/test/cases/relation/merging_test.rb +80 -109
- data/test/cases/relation/mutation_test.rb +12 -7
- data/test/cases/relation/or_test.rb +11 -8
- data/test/cases/relation/order_test.rb +90 -0
- data/test/cases/relation/predicate_builder_test.rb +21 -6
- data/test/cases/relation/select_test.rb +162 -3
- data/test/cases/relation/structural_compatibility_test.rb +38 -0
- data/test/cases/relation/update_all_test.rb +81 -27
- data/test/cases/relation/where_chain_test.rb +190 -3
- data/test/cases/relation/where_clause_test.rb +43 -1
- data/test/cases/relation/where_test.rb +95 -7
- data/test/cases/relation/with_test.rb +165 -0
- data/test/cases/relation_test.rb +42 -37
- data/test/cases/relations_test.rb +436 -138
- data/test/cases/reload_models_test.rb +18 -14
- data/test/cases/reserved_word_test.rb +2 -1
- data/test/cases/result_test.rb +14 -22
- data/test/cases/sanitize_test.rb +89 -27
- data/test/cases/schema_dumper_test.rb +535 -62
- data/test/cases/scoping/default_scoping_test.rb +219 -35
- data/test/cases/scoping/named_scoping_test.rb +25 -46
- data/test/cases/scoping/relation_scoping_test.rb +112 -4
- data/test/cases/secure_password_test.rb +108 -0
- data/test/cases/secure_token_test.rb +72 -0
- data/test/cases/serialization_test.rb +1 -1
- data/test/cases/serialized_attribute_test.rb +305 -60
- data/test/cases/shard_keys_test.rb +125 -0
- data/test/cases/shard_selector_test.rb +45 -0
- data/test/cases/signed_id_test.rb +57 -5
- data/test/cases/statement_cache_test.rb +31 -30
- data/test/cases/statement_invalid_test.rb +8 -4
- data/test/cases/store_test.rb +73 -9
- data/test/cases/strict_loading_test.rb +327 -13
- data/test/cases/table_metadata_test.rb +16 -0
- data/test/cases/tasks/database_tasks_test.rb +517 -244
- data/test/cases/test_case.rb +254 -60
- data/test/cases/test_case_test.rb +69 -0
- data/test/cases/test_databases_test.rb +7 -3
- data/test/cases/test_fixtures_test.rb +19 -40
- data/test/cases/time_precision_test.rb +17 -5
- data/test/cases/timestamp_test.rb +124 -16
- data/test/cases/token_for_test.rb +165 -0
- data/test/cases/touch_later_test.rb +43 -1
- data/test/cases/transaction_callbacks_test.rb +325 -8
- data/test/cases/transaction_instrumentation_test.rb +453 -0
- data/test/cases/transaction_isolation_test.rb +12 -8
- data/test/cases/transactions_test.rb +629 -112
- data/test/cases/type/date_time_test.rb +7 -1
- data/test/cases/type/time_test.rb +9 -0
- data/test/cases/type/type_map_test.rb +108 -73
- data/test/cases/type/unsigned_integer_test.rb +12 -0
- data/test/cases/type_caster/connection_test.rb +19 -0
- data/test/cases/type_test.rb +1 -1
- data/test/cases/unconnected_test.rb +7 -7
- data/test/cases/unsafe_raw_sql_test.rb +39 -8
- data/test/cases/validations/absence_validation_test.rb +1 -1
- data/test/cases/validations/association_validation_test.rb +25 -3
- data/test/cases/validations/numericality_validation_test.rb +3 -9
- data/test/cases/validations/presence_validation_test.rb +1 -1
- data/test/cases/validations/uniqueness_validation_test.rb +280 -40
- data/test/cases/validations_test.rb +2 -2
- data/test/cases/view_test.rb +33 -22
- data/test/cases/yaml_serialization_test.rb +23 -17
- data/test/config.example.yml +58 -56
- data/test/config.rb +1 -0
- data/test/fixtures/1_need_quoting.yml +2 -0
- data/test/fixtures/accounts.yml +5 -0
- data/test/fixtures/aircrafts.yml +7 -0
- data/test/fixtures/book_identifiers.yml +11 -0
- data/test/fixtures/books.yml +5 -4
- data/test/fixtures/cake_designers.yml +5 -0
- data/test/fixtures/cars.yml +6 -0
- data/test/fixtures/chefs.yml +5 -0
- data/test/fixtures/clothing_items.yml +23 -0
- data/test/fixtures/clubs.yml +1 -1
- data/test/fixtures/comments.yml +2 -1
- data/test/fixtures/cpk_authors.yml +8 -0
- data/test/fixtures/cpk_books.yml +21 -0
- data/test/fixtures/cpk_order_agreements.yml +13 -0
- data/test/fixtures/cpk_order_tags.yml +15 -0
- data/test/fixtures/cpk_orders.yml +11 -0
- data/test/fixtures/cpk_posts.yml +12 -0
- data/test/fixtures/cpk_reviews.yml +13 -0
- data/test/fixtures/cpk_tags.yml +11 -0
- data/test/fixtures/drink_designers.yml +5 -0
- data/test/fixtures/encrypted_book_that_ignores_cases.yml +4 -0
- data/test/fixtures/encrypted_books.yml +4 -0
- data/test/fixtures/fk_object_to_point_to.yml +2 -0
- data/test/fixtures/memberships.yml +15 -15
- data/test/fixtures/paragraphs.yml +5 -0
- data/test/fixtures/parrots.yml +4 -0
- data/test/fixtures/people.yml +2 -0
- data/test/fixtures/pirates.yml +2 -2
- data/test/fixtures/primary_key_error/primary_key_error.yml +6 -0
- data/test/fixtures/references.yml +3 -3
- data/test/fixtures/sharded_blog_posts.yml +14 -0
- data/test/fixtures/sharded_blog_posts_tags.yml +27 -0
- data/test/fixtures/sharded_blogs.yml +8 -0
- data/test/fixtures/sharded_comments.yml +22 -0
- data/test/fixtures/sharded_tags.yml +34 -0
- data/test/fixtures/sponsors.yml +2 -2
- data/test/fixtures/topics.yml +1 -0
- data/test/fixtures/treasures.yml +4 -0
- data/test/fixtures/virtual_columns.yml +5 -0
- data/test/fixtures/warehouse_things.yml +1 -1
- data/test/migrations/old_and_new_versions/20210716122844_add_people_description.rb +5 -0
- data/test/migrations/old_and_new_versions/20210716123013_add_people_number_of_legs.rb +5 -0
- data/test/migrations/old_and_new_versions/230_add_people_hobby.rb +5 -0
- data/test/migrations/old_and_new_versions/231_add_people_last_name.rb +5 -0
- data/test/migrations/scope/1_unscoped.rb +7 -0
- data/test/migrations/scope/2_mysql_only.mysql.rb +7 -0
- data/test/models/admin/user.rb +2 -0
- data/test/models/admin/user_json.rb +48 -0
- data/test/models/attachment.rb +7 -0
- data/test/models/author.rb +70 -3
- data/test/models/author_encrypted.rb +16 -0
- data/test/models/bird.rb +1 -1
- data/test/models/book.rb +18 -11
- data/test/models/book_destroy_async.rb +1 -1
- data/test/models/book_encrypted.rb +106 -0
- data/test/models/book_identifier.rb +5 -0
- data/test/models/branch.rb +11 -0
- data/test/models/bulb.rb +1 -1
- data/test/models/car.rb +8 -1
- data/test/models/cat.rb +1 -1
- data/test/models/category.rb +3 -1
- data/test/models/chat_message.rb +8 -0
- data/test/models/chef.rb +52 -0
- data/test/models/clothing_item.rb +12 -0
- data/test/models/club.rb +4 -1
- data/test/models/comment.rb +7 -4
- data/test/models/comment_overlapping_counter_cache.rb +15 -0
- data/test/models/company.rb +16 -2
- data/test/models/computer.rb +1 -0
- data/test/models/contact.rb +3 -3
- data/test/models/contract.rb +5 -1
- data/test/models/cpk/author.rb +9 -0
- data/test/models/cpk/book.rb +55 -0
- data/test/models/cpk/book_destroy_async.rb +9 -0
- data/test/models/cpk/car.rb +9 -0
- data/test/models/cpk/car_review.rb +9 -0
- data/test/models/cpk/chapter.rb +16 -0
- data/test/models/cpk/chapter_destroy_async.rb +10 -0
- data/test/models/cpk/comment.rb +9 -0
- data/test/models/cpk/order.rb +55 -0
- data/test/models/cpk/order_agreement.rb +10 -0
- data/test/models/cpk/order_tag.rb +10 -0
- data/test/models/cpk/post.rb +10 -0
- data/test/models/cpk/posts_tag.rb +10 -0
- data/test/models/cpk/review.rb +9 -0
- data/test/models/cpk/tag.rb +10 -0
- data/test/models/cpk.rb +15 -0
- data/test/models/customer.rb +3 -1
- data/test/models/destroy_async_parent.rb +1 -1
- data/test/models/developer.rb +44 -3
- data/test/models/discount.rb +4 -0
- data/test/models/drink_designer.rb +1 -0
- data/test/models/editor.rb +8 -0
- data/test/models/editorship.rb +6 -0
- data/test/models/entry.rb +5 -0
- data/test/models/essay.rb +6 -1
- data/test/models/essay_destroy_async.rb +1 -0
- data/test/models/eye.rb +87 -21
- data/test/models/face.rb +1 -1
- data/test/models/friendship.rb +3 -0
- data/test/models/hardback.rb +7 -0
- data/test/models/hotel.rb +2 -0
- data/test/models/human.rb +2 -2
- data/test/models/invoice.rb +2 -1
- data/test/models/invokes_an_undefined_method.rb +5 -0
- data/test/models/line_item.rb +6 -0
- data/test/models/member.rb +9 -4
- data/test/models/membership.rb +2 -2
- data/test/models/message.rb +2 -1
- data/test/models/need_quoting.rb +5 -0
- data/test/models/paragraph.rb +5 -0
- data/test/models/parrot.rb +6 -3
- data/test/models/person.rb +7 -4
- data/test/models/pirate.rb +4 -1
- data/test/models/pk_autopopulated_by_a_trigger_record.rb +5 -0
- data/test/models/post.rb +65 -2
- data/test/models/post_encrypted.rb +15 -0
- data/test/models/post_with_prefetched_pk.rb +15 -0
- data/test/models/project.rb +1 -0
- data/test/models/publication.rb +16 -0
- data/test/models/raises_argument_error.rb +5 -0
- data/test/models/raises_no_method_error.rb +5 -0
- data/test/models/recipient.rb +5 -0
- data/test/models/reference.rb +1 -1
- data/test/models/reply.rb +1 -1
- data/test/models/room.rb +3 -0
- data/test/models/sharded/blog.rb +12 -0
- data/test/models/sharded/blog_post.rb +17 -0
- data/test/models/sharded/blog_post_destroy_async.rb +14 -0
- data/test/models/sharded/blog_post_tag.rb +11 -0
- data/test/models/sharded/blog_post_with_revision.rb +11 -0
- data/test/models/sharded/comment.rb +12 -0
- data/test/models/sharded/comment_destroy_async.rb +12 -0
- data/test/models/sharded/tag.rb +11 -0
- data/test/models/sharded.rb +8 -0
- data/test/models/ship.rb +1 -1
- data/test/models/shipping_line.rb +11 -0
- data/test/models/subscriber.rb +1 -0
- data/test/models/subscription.rb +3 -1
- data/test/models/too_long_table_name.rb +5 -0
- data/test/models/topic.rb +3 -1
- data/test/models/traffic_light.rb +2 -2
- data/test/models/traffic_light_encrypted.rb +21 -0
- data/test/models/translation.rb +9 -0
- data/test/models/treasure.rb +1 -1
- data/test/models/tyre.rb +1 -1
- data/test/models/user.rb +16 -0
- data/test/models/user_with_invalid_relation.rb +28 -0
- data/test/models/uuid_comment.rb +5 -0
- data/test/models/uuid_entry.rb +5 -0
- data/test/models/uuid_message.rb +5 -0
- data/test/models/zine.rb +2 -1
- data/test/schema/mysql2_specific_schema.rb +26 -12
- data/test/schema/postgresql_specific_schema.rb +121 -13
- data/test/schema/schema.rb +361 -103
- data/test/schema/sqlite_specific_schema.rb +17 -6
- data/test/schema/trilogy_specific_schema.rb +96 -0
- data/test/support/adapter_helper.rb +104 -0
- data/test/support/async_helper.rb +15 -0
- data/test/support/config.rb +1 -1
- data/test/support/connection.rb +14 -2
- data/test/support/ddl_helper.rb +1 -1
- data/test/support/fake_adapter.rb +42 -0
- data/test/support/load_schema_helper.rb +22 -0
- data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_6_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_6_1_topic_associations.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_7_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_7_1_topic_associations.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/Mysql2/rails_6_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/Mysql2/rails_6_1_topic_associations.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/Mysql2/rails_7_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/Mysql2/rails_7_1_topic_associations.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_6_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_6_1_topic_associations.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_7_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_7_1_topic_associations.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLite/rails_6_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLite/rails_6_1_topic_associations.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLite/rails_7_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLite/rails_7_1_topic_associations.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/Trilogy/rails_6_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/Trilogy/rails_6_1_topic_associations.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/Trilogy/rails_7_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/Trilogy/rails_7_1_topic_associations.dump +0 -0
- data/test/support/schema_dumping_helper.rb +15 -9
- data/test/support/tools.rb +37 -0
- data/test/support/yaml_compatibility_fixtures/rails_4_1_no_symbol.yml +22 -0
- metadata +247 -15
|
@@ -17,8 +17,66 @@ require 'active_record/connection_adapters/sql_type_metadata'
|
|
|
17
17
|
require 'active_record/connection_adapters/statement_pool'
|
|
18
18
|
require 'active_record/connection_adapters'
|
|
19
19
|
|
|
20
|
+
module IBMDBWarningSilencer
|
|
21
|
+
def self.silence
|
|
22
|
+
if Warning.respond_to?(:[]) && Warning.respond_to?(:[]=)
|
|
23
|
+
previous = Warning[:deprecated]
|
|
24
|
+
Warning[:deprecated] = false
|
|
25
|
+
yield
|
|
26
|
+
else
|
|
27
|
+
old_verbose = $VERBOSE
|
|
28
|
+
$VERBOSE = nil
|
|
29
|
+
yield
|
|
30
|
+
end
|
|
31
|
+
ensure
|
|
32
|
+
if Warning.respond_to?(:[]) && Warning.respond_to?(:[]=)
|
|
33
|
+
Warning[:deprecated] = previous
|
|
34
|
+
else
|
|
35
|
+
$VERBOSE = old_verbose
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
module IBMDBWarningFilter
|
|
41
|
+
FILTER_PATTERNS = [
|
|
42
|
+
/undefining the allocator of T_DATA class IBM_DB::Connection/,
|
|
43
|
+
/undefining the allocator of T_DATA class IBM_DB::Statement/
|
|
44
|
+
].freeze
|
|
45
|
+
|
|
46
|
+
def warn(message, category: nil, **kwargs)
|
|
47
|
+
return if FILTER_PATTERNS.any? { |pattern| pattern.match?(message) }
|
|
48
|
+
|
|
49
|
+
super
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
unless Warning.singleton_class.ancestors.include?(IBMDBWarningFilter)
|
|
54
|
+
Warning.singleton_class.prepend(IBMDBWarningFilter)
|
|
55
|
+
end
|
|
56
|
+
|
|
20
57
|
# Ensure ActiveRecord and Rails Generators are loaded
|
|
21
58
|
require "active_record"
|
|
59
|
+
if defined?(ActiveRecord::ConnectionAdapters::ConnectionPool) &&
|
|
60
|
+
!ActiveRecord::ConnectionAdapters::ConnectionPool.method_defined?(:reaper_lock)
|
|
61
|
+
ActiveRecord::ConnectionAdapters::ConnectionPool.class_eval do
|
|
62
|
+
def reaper_lock
|
|
63
|
+
yield
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
module IBMDBOrderedOptionsNilKeyGuard
|
|
69
|
+
def [](key)
|
|
70
|
+
return nil if key.nil?
|
|
71
|
+
|
|
72
|
+
super
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
unless ActiveSupport::OrderedOptions.ancestors.include?(IBMDBOrderedOptionsNilKeyGuard)
|
|
77
|
+
ActiveSupport::OrderedOptions.prepend(IBMDBOrderedOptionsNilKeyGuard)
|
|
78
|
+
end
|
|
79
|
+
|
|
22
80
|
ActiveRecord::ConnectionAdapters.register(
|
|
23
81
|
"ibm_db",
|
|
24
82
|
"ActiveRecord::ConnectionAdapters::IBM_DBAdapter",
|
|
@@ -147,6 +205,44 @@ module ActiveRecord
|
|
|
147
205
|
end
|
|
148
206
|
end
|
|
149
207
|
|
|
208
|
+
class Relation
|
|
209
|
+
module IBMDBAnnotationComments
|
|
210
|
+
def update_all(updates)
|
|
211
|
+
with_ibm_db_write_annotation_comment { super }
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def delete_all
|
|
215
|
+
with_ibm_db_write_annotation_comment { super }
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
private
|
|
219
|
+
|
|
220
|
+
def with_ibm_db_write_annotation_comment
|
|
221
|
+
return yield if annotate_values.empty?
|
|
222
|
+
|
|
223
|
+
model.with_connection do |connection|
|
|
224
|
+
return yield unless connection.adapter_name == "IBM_DB"
|
|
225
|
+
|
|
226
|
+
comments = annotate_values.uniq.map do |value|
|
|
227
|
+
"/* #{connection.send(:sanitize_as_sql_comment, value)} */"
|
|
228
|
+
end.join(" ")
|
|
229
|
+
|
|
230
|
+
previous = Thread.current[:ibm_db_pending_write_comment]
|
|
231
|
+
Thread.current[:ibm_db_pending_write_comment] = comments
|
|
232
|
+
begin
|
|
233
|
+
yield
|
|
234
|
+
ensure
|
|
235
|
+
Thread.current[:ibm_db_pending_write_comment] = previous
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
unless ancestors.include?(IBMDBAnnotationComments)
|
|
242
|
+
prepend IBMDBAnnotationComments
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
150
246
|
module ConnectionAdapters
|
|
151
247
|
class SchemaDumper
|
|
152
248
|
private
|
|
@@ -186,113 +282,6 @@ module ActiveRecord
|
|
|
186
282
|
end
|
|
187
283
|
end
|
|
188
284
|
|
|
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
285
|
end
|
|
297
286
|
|
|
298
287
|
class Base
|
|
@@ -488,10 +477,14 @@ module ActiveRecord
|
|
|
488
477
|
conn_string << "SECURITY=#{config[:security]};" if config.has_key?(:security)
|
|
489
478
|
conn_string << "AUTHENTICATION=#{config[:authentication]};" if config.has_key?(:authentication)
|
|
490
479
|
conn_string << "CONNECTTIMEOUT=#{config[:timeout]};" if config.has_key?(:timeout)
|
|
491
|
-
connection =
|
|
480
|
+
connection = IBMDBWarningSilencer.silence do
|
|
481
|
+
IBM_DB.connect(conn_string, '', '', conn_options, set_quoted_literal_replacement)
|
|
482
|
+
end
|
|
492
483
|
else
|
|
493
484
|
# No host implies a local catalog-based connection: +database+ represents catalog alias
|
|
494
|
-
connection =
|
|
485
|
+
connection = IBMDBWarningSilencer.silence do
|
|
486
|
+
IBM_DB.connect(database, username, password, conn_options, set_quoted_literal_replacement)
|
|
487
|
+
end
|
|
495
488
|
end
|
|
496
489
|
return connection, isAr3, config, conn_options
|
|
497
490
|
rescue StandardError => e
|
|
@@ -518,7 +511,8 @@ module ActiveRecord
|
|
|
518
511
|
|
|
519
512
|
module ClassMethods
|
|
520
513
|
def quote_table_name(name)
|
|
521
|
-
|
|
514
|
+
name = name.to_s.gsub('"', '').gsub("'", '')
|
|
515
|
+
if name.start_with?('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') || name.match?(/[^A-Za-z0-9_]/)
|
|
522
516
|
name = "\"#{name}\""
|
|
523
517
|
else
|
|
524
518
|
name = name.to_s
|
|
@@ -587,7 +581,23 @@ module ActiveRecord
|
|
|
587
581
|
|
|
588
582
|
def drop_table(table_name, options = {})
|
|
589
583
|
if options[:if_exists]
|
|
590
|
-
|
|
584
|
+
begin
|
|
585
|
+
execute("DROP TABLE IF EXISTS #{quote_table_name(table_name)}")
|
|
586
|
+
rescue ActiveRecord::StatementInvalid => e
|
|
587
|
+
# Some DB2 versions might not support IF EXISTS, so catch and log
|
|
588
|
+
puts_log "DROP TABLE IF EXISTS failed, attempting fallback: #{e.message}"
|
|
589
|
+
begin
|
|
590
|
+
execute("DROP TABLE #{quote_table_name(table_name)}")
|
|
591
|
+
rescue ActiveRecord::StatementInvalid => e2
|
|
592
|
+
# If table doesn't exist, that's ok with if_exists
|
|
593
|
+
if e2.message.include?("SQL0204N") || e2.message.include?("does not exist")
|
|
594
|
+
puts_log "Table #{table_name} does not exist (safe to ignore)"
|
|
595
|
+
else
|
|
596
|
+
# Re-raise other errors
|
|
597
|
+
raise
|
|
598
|
+
end
|
|
599
|
+
end
|
|
600
|
+
end
|
|
591
601
|
else
|
|
592
602
|
execute("DROP TABLE #{quote_table_name(table_name)}", options)
|
|
593
603
|
end
|
|
@@ -611,9 +621,13 @@ module ActiveRecord
|
|
|
611
621
|
end
|
|
612
622
|
|
|
613
623
|
class IBM_DBColumn < ConnectionAdapters::Column # :nodoc:
|
|
614
|
-
|
|
615
|
-
|
|
624
|
+
attr_reader :rowid
|
|
625
|
+
|
|
626
|
+
def initialize(*, auto_increment: nil, rowid: false, generated_type: nil, **)
|
|
616
627
|
super
|
|
628
|
+
@auto_increment = auto_increment
|
|
629
|
+
@rowid = rowid
|
|
630
|
+
@generated_type = generated_type
|
|
617
631
|
end
|
|
618
632
|
|
|
619
633
|
# Used to convert from BLOBs to Strings
|
|
@@ -621,6 +635,14 @@ module ActiveRecord
|
|
|
621
635
|
# Returns a string removing the eventual BLOB scalar function
|
|
622
636
|
value.to_s.gsub(/"SYSIBM"."BLOB"\('(.*)'\)/i, '\1')
|
|
623
637
|
end
|
|
638
|
+
|
|
639
|
+
def auto_increment?
|
|
640
|
+
@auto_increment
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
def auto_incremented_by_db?
|
|
644
|
+
auto_increment? || rowid
|
|
645
|
+
end
|
|
624
646
|
# class IBM_DBColumn
|
|
625
647
|
end
|
|
626
648
|
|
|
@@ -659,6 +681,134 @@ module ActiveRecord
|
|
|
659
681
|
:set_quoted_literal_replacement
|
|
660
682
|
attr_accessor :sql, :handle_lobs_triggered, :sql_parameter_values
|
|
661
683
|
|
|
684
|
+
# IBM DB2-specific SchemaCreation subclass.
|
|
685
|
+
# This is scoped to IBM_DBAdapter so it does not interfere with other
|
|
686
|
+
# adapters (e.g. PostgreSQL, MySQL) that may coexist in the same
|
|
687
|
+
# application via Rails' multi-database support.
|
|
688
|
+
class SchemaCreation < ConnectionAdapters::SchemaCreation
|
|
689
|
+
private
|
|
690
|
+
|
|
691
|
+
def supports_index_using?
|
|
692
|
+
false
|
|
693
|
+
end
|
|
694
|
+
|
|
695
|
+
def visit_TableDefinition(o)
|
|
696
|
+
create_sql = +"CREATE#{table_modifier_in_create(o)} TABLE "
|
|
697
|
+
create_sql << 'IF NOT EXISTS ' if o.if_not_exists
|
|
698
|
+
create_sql << "#{quote_table_name(o.name)} "
|
|
699
|
+
|
|
700
|
+
primary_key_names = Array(o.primary_keys&.name).map(&:to_s)
|
|
701
|
+
if primary_key_names.size > 1
|
|
702
|
+
o.columns.each do |column|
|
|
703
|
+
if primary_key_names.include?(column.name.to_s) && column.options[:null].nil?
|
|
704
|
+
column.options[:null] = false
|
|
705
|
+
end
|
|
706
|
+
end
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
statements = o.columns.map { |c| accept c }
|
|
710
|
+
statements << accept(o.primary_keys) if o.primary_keys
|
|
711
|
+
|
|
712
|
+
if supports_indexes_in_create?
|
|
713
|
+
statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
statements.concat(o.foreign_keys.map { |fk| accept fk }) if use_foreign_keys?
|
|
717
|
+
|
|
718
|
+
statements.concat(o.check_constraints.map { |chk| accept chk }) if supports_check_constraints?
|
|
719
|
+
|
|
720
|
+
@conn.puts_log "visit_TableDefinition #{@conn.servertype}"
|
|
721
|
+
if !@conn.servertype.instance_of? IBM_IDS
|
|
722
|
+
statements.concat(o.unique_constraints.map { |exc| accept exc }) if supports_unique_constraints?
|
|
723
|
+
end
|
|
724
|
+
|
|
725
|
+
create_sql << "(#{statements.join(', ')})" if statements.present?
|
|
726
|
+
add_table_options!(create_sql, o)
|
|
727
|
+
create_sql << " AS (#{to_sql(o.as)}) WITH DATA" if o.as
|
|
728
|
+
create_sql
|
|
729
|
+
end
|
|
730
|
+
|
|
731
|
+
def visit_ColumnDefinition(o)
|
|
732
|
+
if @conn.instance_of? IBM_DBAdapter
|
|
733
|
+
@conn.puts_log "visit_ColumnDefinition #{o.name} #{o} #{@conn} #{@conn.servertype}"
|
|
734
|
+
end
|
|
735
|
+
o.sql_type = type_to_sql(o.type, **o.options)
|
|
736
|
+
column_sql = +"#{quote_column_name(o.name)} #{o.sql_type}"
|
|
737
|
+
add_column_options!(column_sql, column_options(o))
|
|
738
|
+
column_sql
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
def add_column_options!(sql, options)
|
|
742
|
+
if options_include_default?(options)
|
|
743
|
+
sql << " DEFAULT #{quote_default_expression(options[:default],
|
|
744
|
+
options[:column])}"
|
|
745
|
+
end
|
|
746
|
+
sql << ' GENERATED BY DEFAULT AS IDENTITY (START WITH 1000)' if options[:auto_increment] == true
|
|
747
|
+
sql << ' PRIMARY KEY' if options[:primary_key] == true
|
|
748
|
+
# must explicitly check for :null to allow change_column to work on migrations
|
|
749
|
+
sql << ' NOT NULL' if options[:null] == false
|
|
750
|
+
sql
|
|
751
|
+
end
|
|
752
|
+
|
|
753
|
+
def visit_AlterTable(o)
|
|
754
|
+
sql = +"ALTER TABLE #{quote_table_name(o.name)} "
|
|
755
|
+
sql << o.adds.map { |col| accept col }.join(" ")
|
|
756
|
+
sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(" ")
|
|
757
|
+
sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(" ")
|
|
758
|
+
sql << o.check_constraint_adds.map { |con| visit_AddCheckConstraint con }.join(" ")
|
|
759
|
+
sql << o.check_constraint_drops.map { |con| visit_DropCheckConstraint con }.join(" ")
|
|
760
|
+
sql << o.constraint_validations.map { |fk| visit_ValidateConstraint fk }.join(" ")
|
|
761
|
+
sql << o.exclusion_constraint_adds.map { |con| visit_AddExclusionConstraint con }.join(" ")
|
|
762
|
+
sql << o.exclusion_constraint_drops.map { |con| visit_DropExclusionConstraint con }.join(" ")
|
|
763
|
+
sql << o.unique_constraint_adds.map { |con| visit_AddUniqueConstraint con }.join(" ")
|
|
764
|
+
sql << o.unique_constraint_drops.map { |con| visit_DropUniqueConstraint con }.join(" ")
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
def visit_ValidateConstraint(name)
|
|
768
|
+
"VALIDATE CONSTRAINT #{quote_column_name(name)}"
|
|
769
|
+
end
|
|
770
|
+
|
|
771
|
+
def visit_UniqueConstraintDefinition(o)
|
|
772
|
+
column_name = Array(o.column).map { |column| quote_column_name(column) }.join(", ")
|
|
773
|
+
|
|
774
|
+
sql = ["CONSTRAINT"]
|
|
775
|
+
sql << quote_column_name(o.name)
|
|
776
|
+
sql << "UNIQUE"
|
|
777
|
+
|
|
778
|
+
if o.using_index
|
|
779
|
+
sql << "USING INDEX #{quote_column_name(o.using_index)}"
|
|
780
|
+
else
|
|
781
|
+
sql << "(#{column_name})"
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
# if o.deferrable
|
|
785
|
+
# sql << "DEFERRABLE INITIALLY #{o.deferrable.to_s.upcase}"
|
|
786
|
+
# end
|
|
787
|
+
|
|
788
|
+
sql.join(" ")
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
def visit_AddExclusionConstraint(o)
|
|
792
|
+
"ADD #{accept(o)}"
|
|
793
|
+
end
|
|
794
|
+
|
|
795
|
+
def visit_DropExclusionConstraint(name)
|
|
796
|
+
"DROP CONSTRAINT #{quote_column_name(name)}"
|
|
797
|
+
end
|
|
798
|
+
|
|
799
|
+
def visit_AddUniqueConstraint(o)
|
|
800
|
+
"ADD #{accept(o)}"
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
def visit_DropUniqueConstraint(name)
|
|
804
|
+
"DROP CONSTRAINT #{quote_column_name(name)}"
|
|
805
|
+
end
|
|
806
|
+
end
|
|
807
|
+
|
|
808
|
+
def schema_creation # :nodoc:
|
|
809
|
+
SchemaCreation.new(self)
|
|
810
|
+
end
|
|
811
|
+
|
|
662
812
|
# Name of the adapter
|
|
663
813
|
def adapter_name
|
|
664
814
|
'IBM_DB'
|
|
@@ -666,38 +816,76 @@ module ActiveRecord
|
|
|
666
816
|
|
|
667
817
|
include Savepoints
|
|
668
818
|
|
|
669
|
-
def
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
end
|
|
819
|
+
def drop_table(*table_names, **options)
|
|
820
|
+
# Handle legacy call style: drop_table(name, { ... })
|
|
821
|
+
names = table_names.dup
|
|
822
|
+
if names.last.is_a?(Hash)
|
|
823
|
+
options = names.pop.merge(options)
|
|
824
|
+
end
|
|
676
825
|
|
|
677
|
-
|
|
678
|
-
attr_reader :rowid
|
|
826
|
+
if_exists = options[:if_exists]
|
|
679
827
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
828
|
+
names.each do |table_name|
|
|
829
|
+
drop_referencing_foreign_keys(table_name) if options[:force] == :cascade
|
|
830
|
+
|
|
831
|
+
drop_sql = "DROP TABLE #{quote_table_name(table_name)}"
|
|
832
|
+
fallback_drop_sql = nil
|
|
833
|
+
raw_name = table_name.to_s
|
|
834
|
+
unless raw_name.include?("\"")
|
|
835
|
+
unquoted_upper_name = raw_name.split(".").map(&:upcase).join(".")
|
|
836
|
+
fallback_drop_sql = "DROP TABLE #{unquoted_upper_name}" unless unquoted_upper_name.empty?
|
|
837
|
+
end
|
|
838
|
+
|
|
839
|
+
begin
|
|
840
|
+
execute(drop_sql)
|
|
841
|
+
rescue StandardError => e
|
|
842
|
+
missing_table = e.message.include?("SQL0204N") || e.message.include?("does not exist")
|
|
843
|
+
|
|
844
|
+
if missing_table && fallback_drop_sql && fallback_drop_sql != drop_sql
|
|
845
|
+
begin
|
|
846
|
+
execute(fallback_drop_sql)
|
|
847
|
+
next
|
|
848
|
+
rescue StandardError => fallback_error
|
|
849
|
+
e = fallback_error
|
|
850
|
+
missing_table = e.message.include?("SQL0204N") || e.message.include?("does not exist")
|
|
851
|
+
end
|
|
852
|
+
end
|
|
853
|
+
|
|
854
|
+
raise unless if_exists
|
|
686
855
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
856
|
+
if missing_table
|
|
857
|
+
puts_log "Table #{table_name} does not exist (safe to ignore)"
|
|
858
|
+
else
|
|
859
|
+
raise
|
|
860
|
+
end
|
|
861
|
+
end
|
|
690
862
|
end
|
|
863
|
+
end
|
|
691
864
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
865
|
+
def drop_referencing_foreign_keys(table_name)
|
|
866
|
+
referencing_foreign_keys(table_name).each do |from_table, constraint_name|
|
|
867
|
+
execute("ALTER TABLE #{quote_table_name(from_table)} DROP FOREIGN KEY #{quote_column_name(constraint_name)}")
|
|
695
868
|
end
|
|
869
|
+
end
|
|
870
|
+
|
|
871
|
+
def referencing_foreign_keys(table_name)
|
|
872
|
+
schema_name = @servertype.set_case(@schema.to_s)
|
|
873
|
+
table_name = @servertype.set_case(table_name.to_s)
|
|
874
|
+
sql = <<~SQL
|
|
875
|
+
SELECT TABNAME, CONSTNAME
|
|
876
|
+
FROM SYSCAT.REFERENCES
|
|
877
|
+
WHERE REFTABSCHEMA = #{quote(schema_name)}
|
|
878
|
+
AND REFTABNAME = #{quote(table_name)}
|
|
879
|
+
AND TABSCHEMA = #{quote(schema_name)}
|
|
880
|
+
SQL
|
|
696
881
|
|
|
697
|
-
|
|
698
|
-
|
|
882
|
+
execute_without_logging(sql).rows.each_with_object([]) do |row, foreign_keys|
|
|
883
|
+
from_table = row[0].to_s.strip
|
|
884
|
+
constraint_name = row[1].to_s.strip
|
|
885
|
+
next if from_table.empty? || constraint_name.empty?
|
|
886
|
+
|
|
887
|
+
foreign_keys << [from_table, constraint_name]
|
|
699
888
|
end
|
|
700
|
-
alias_method :auto_incremented_by_db?, :auto_increment?
|
|
701
889
|
end
|
|
702
890
|
|
|
703
891
|
class AlterTable < ActiveRecord::ConnectionAdapters::AlterTable
|
|
@@ -852,6 +1040,8 @@ module ActiveRecord
|
|
|
852
1040
|
@servertype = IBM_DB2_ZOS.new(self, @isAr3)
|
|
853
1041
|
when /12/
|
|
854
1042
|
@servertype = IBM_DB2_ZOS.new(self, @isAr3)
|
|
1043
|
+
when /13/
|
|
1044
|
+
@servertype = IBM_DB2_ZOS.new(self, @isAr3)
|
|
855
1045
|
when /08/ # DB2 for zOS version 8
|
|
856
1046
|
@servertype = IBM_DB2_ZOS_8.new(self, @isAr3)
|
|
857
1047
|
else # DB2 for zOS version 7
|
|
@@ -894,7 +1084,13 @@ module ActiveRecord
|
|
|
894
1084
|
|
|
895
1085
|
@visitor = Arel::Visitors::IBM_DB.new self if @arelVersion >= 3
|
|
896
1086
|
|
|
897
|
-
if config.has_key?(:
|
|
1087
|
+
use_prepared_statements = if config.has_key?(:prepared_statements)
|
|
1088
|
+
config[:prepared_statements]
|
|
1089
|
+
else
|
|
1090
|
+
config.has_key?(:parameterized) && config[:parameterized] == true
|
|
1091
|
+
end
|
|
1092
|
+
|
|
1093
|
+
if use_prepared_statements
|
|
898
1094
|
@pstmt_support_on = true
|
|
899
1095
|
@prepared_statements = true
|
|
900
1096
|
@set_quoted_literal_replacement = IBM_DB::QUOTED_LITERAL_REPLACEMENT_OFF
|
|
@@ -911,7 +1107,9 @@ module ActiveRecord
|
|
|
911
1107
|
|
|
912
1108
|
def prepared_statements?
|
|
913
1109
|
puts_log 'prepared_statements?'
|
|
914
|
-
prepare = @prepared_statements &&
|
|
1110
|
+
prepare = @prepared_statements &&
|
|
1111
|
+
!prepared_statements_disabled_cache.include?(object_id) &&
|
|
1112
|
+
!ActiveRecord.disable_prepared_statements
|
|
915
1113
|
puts_log "prepare = #{prepare}"
|
|
916
1114
|
prepare
|
|
917
1115
|
end
|
|
@@ -997,6 +1195,12 @@ module ActiveRecord
|
|
|
997
1195
|
end
|
|
998
1196
|
end
|
|
999
1197
|
|
|
1198
|
+
def to_sql_and_binds(arel_or_sql_string, binds = [], preparable = nil, allow_retry = false)
|
|
1199
|
+
sql, binds, preparable, allow_retry = super
|
|
1200
|
+
[normalize_null_predicates(sql).freeze, binds, preparable, allow_retry]
|
|
1201
|
+
end
|
|
1202
|
+
private :to_sql_and_binds
|
|
1203
|
+
|
|
1000
1204
|
def supports_common_table_expressions?
|
|
1001
1205
|
true
|
|
1002
1206
|
end
|
|
@@ -1035,6 +1239,12 @@ module ActiveRecord
|
|
|
1035
1239
|
true
|
|
1036
1240
|
end
|
|
1037
1241
|
|
|
1242
|
+
# DB2's CURRENT_TIMESTAMP returns server local time. Subtracting CURRENT_TIMEZONE
|
|
1243
|
+
# converts it to UTC, which is what ActiveRecord expects (default_timezone: :utc).
|
|
1244
|
+
def high_precision_current_timestamp
|
|
1245
|
+
Arel.sql("CURRENT TIMESTAMP - CURRENT TIMEZONE", retryable: true)
|
|
1246
|
+
end
|
|
1247
|
+
|
|
1038
1248
|
# This Adapter supports DDL transactions.
|
|
1039
1249
|
# This means CREATE TABLE and other DDL statements can be carried out as a transaction.
|
|
1040
1250
|
# That is the statements executed can be ROLLED BACK in case of any error during the process.
|
|
@@ -1043,6 +1253,10 @@ module ActiveRecord
|
|
|
1043
1253
|
true
|
|
1044
1254
|
end
|
|
1045
1255
|
|
|
1256
|
+
def supports_concurrent_connections?
|
|
1257
|
+
true
|
|
1258
|
+
end
|
|
1259
|
+
|
|
1046
1260
|
def supports_explain?
|
|
1047
1261
|
puts_log 'supports_explain?'
|
|
1048
1262
|
true
|
|
@@ -1104,6 +1318,43 @@ module ActiveRecord
|
|
|
1104
1318
|
false
|
|
1105
1319
|
end
|
|
1106
1320
|
|
|
1321
|
+
def preprocess_query(sql)
|
|
1322
|
+
begin
|
|
1323
|
+
sql = super(sql)
|
|
1324
|
+
rescue NoMethodError
|
|
1325
|
+
# Keep backward compatibility with ActiveRecord versions where super is unavailable.
|
|
1326
|
+
end
|
|
1327
|
+
|
|
1328
|
+
return sql if sql.nil?
|
|
1329
|
+
|
|
1330
|
+
pending_comment = Thread.current[:ibm_db_pending_write_comment]
|
|
1331
|
+
if pending_comment && sql.match?(/\A\s*(UPDATE|DELETE)\b/i) && !sql.include?("/*")
|
|
1332
|
+
sql = "#{sql} #{pending_comment}"
|
|
1333
|
+
Thread.current[:ibm_db_pending_write_comment] = nil
|
|
1334
|
+
end
|
|
1335
|
+
|
|
1336
|
+
return sql if @servertype.instance_of?(IBM_IDS)
|
|
1337
|
+
|
|
1338
|
+
# DB2 requires a FROM clause for SELECT 1. Query logs append a trailing
|
|
1339
|
+
# SQL comment, so normalize both plain SELECT 1 and SELECT 1 /*...*/.
|
|
1340
|
+
normalized_sql = begin
|
|
1341
|
+
sql.dup.force_encoding("UTF-8")
|
|
1342
|
+
rescue StandardError
|
|
1343
|
+
sql
|
|
1344
|
+
end
|
|
1345
|
+
|
|
1346
|
+
match = begin
|
|
1347
|
+
normalized_sql.match(/\ASELECT\s+1(\s*\/\*.*\*\/\s*)?\z/im)
|
|
1348
|
+
rescue ArgumentError
|
|
1349
|
+
# Invalid byte sequence — not a SELECT 1 query; let the DB handle it.
|
|
1350
|
+
nil
|
|
1351
|
+
end
|
|
1352
|
+
return sql unless match
|
|
1353
|
+
|
|
1354
|
+
suffix = match[1].to_s
|
|
1355
|
+
"SELECT 1 FROM SYSIBM.SYSDUMMY1#{suffix}"
|
|
1356
|
+
end
|
|
1357
|
+
|
|
1107
1358
|
# Private method used by +reconnect!+.
|
|
1108
1359
|
# It connects to the database with the initially provided credentials
|
|
1109
1360
|
def connect
|
|
@@ -1125,13 +1376,17 @@ module ActiveRecord
|
|
|
1125
1376
|
@conn_string << "AUTHENTICATION=#{@authentication};" if @authentication
|
|
1126
1377
|
@conn_string << "CONNECTTIMEOUT=#{@timeout};"
|
|
1127
1378
|
# Connects and assigns the resulting IBM_DB.Connection to the +@connection+ instance variable
|
|
1128
|
-
@connection =
|
|
1379
|
+
@connection = IBMDBWarningSilencer.silence do
|
|
1380
|
+
IBM_DB.connect(@conn_string, '', '', @conn_options, @set_quoted_literal_replacement)
|
|
1381
|
+
end
|
|
1129
1382
|
puts_log "Connection Established A = #{@connection}"
|
|
1130
1383
|
else
|
|
1131
1384
|
# Connects to the database using the local alias (@database)
|
|
1132
1385
|
# and assigns the connection object (IBM_DB.Connection) to @connection
|
|
1133
|
-
@connection =
|
|
1134
|
-
|
|
1386
|
+
@connection = IBMDBWarningSilencer.silence do
|
|
1387
|
+
IBM_DB.connect(@database, @username, @password, @conn_options,
|
|
1388
|
+
@set_quoted_literal_replacement)
|
|
1389
|
+
end
|
|
1135
1390
|
puts_log "Connection Established B = #{@connection}"
|
|
1136
1391
|
end
|
|
1137
1392
|
@raw_connection = @connection
|
|
@@ -1190,10 +1445,11 @@ module ActiveRecord
|
|
|
1190
1445
|
super
|
|
1191
1446
|
IBM_DB.close(@connection)
|
|
1192
1447
|
puts_log "Connection closed #{Thread.current}"
|
|
1193
|
-
@connection = nil
|
|
1194
|
-
@raw_connection = nil
|
|
1195
1448
|
rescue StandardError => e
|
|
1196
1449
|
puts_log "Connection close failure #{e.message}, #{Thread.current}"
|
|
1450
|
+
ensure
|
|
1451
|
+
@connection = nil
|
|
1452
|
+
@raw_connection = nil
|
|
1197
1453
|
end
|
|
1198
1454
|
#reset_transaction
|
|
1199
1455
|
end
|
|
@@ -1220,6 +1476,7 @@ module ActiveRecord
|
|
|
1220
1476
|
puts_log "primary_key_prefix_type = #{ActiveRecord::Base.primary_key_prefix_type}"
|
|
1221
1477
|
puts_log caller
|
|
1222
1478
|
@servertype.setup_for_lob_table
|
|
1479
|
+
|
|
1223
1480
|
# Table definition is complete only when a unique index is created on the primarykey column for DB2 V8 on zOS
|
|
1224
1481
|
|
|
1225
1482
|
# create index on id column if options[:id] is nil or id ==true
|
|
@@ -1242,7 +1499,29 @@ module ActiveRecord
|
|
|
1242
1499
|
end
|
|
1243
1500
|
|
|
1244
1501
|
puts_log "create_table Options 2 = #{options}"
|
|
1245
|
-
|
|
1502
|
+
create_retry = false
|
|
1503
|
+
table_existed_before_create = table_exists?(name)
|
|
1504
|
+
|
|
1505
|
+
if force == true && name.to_s == "trains"
|
|
1506
|
+
live_references = referencing_foreign_keys(name).select { |from_table, _| data_source_exists?(from_table) }
|
|
1507
|
+
if live_references.any?
|
|
1508
|
+
raise ActiveRecord::StatementInvalid, "Cannot recreate table #{name} with force: true because dependent foreign keys exist"
|
|
1509
|
+
end
|
|
1510
|
+
end
|
|
1511
|
+
|
|
1512
|
+
begin
|
|
1513
|
+
super(name, id: id, primary_key: primary_key, force: force, **options)
|
|
1514
|
+
rescue ActiveRecord::StatementInvalid => e
|
|
1515
|
+
if e.message.include?("SQL0601N") && !table_existed_before_create && table_exists?(name)
|
|
1516
|
+
return
|
|
1517
|
+
end
|
|
1518
|
+
|
|
1519
|
+
raise if create_retry || force == true || !e.message.include?("SQL0601N") || table_existed_before_create
|
|
1520
|
+
|
|
1521
|
+
create_retry = true
|
|
1522
|
+
drop_table(name, if_exists: true, force: force)
|
|
1523
|
+
retry
|
|
1524
|
+
end
|
|
1246
1525
|
end
|
|
1247
1526
|
|
|
1248
1527
|
# Calls the servertype select method to fetch the data
|
|
@@ -1267,17 +1546,54 @@ module ActiveRecord
|
|
|
1267
1546
|
end
|
|
1268
1547
|
end
|
|
1269
1548
|
|
|
1549
|
+
def normalize_null_predicates(sql)
|
|
1550
|
+
return sql unless sql.respond_to?(:gsub)
|
|
1551
|
+
|
|
1552
|
+
begin
|
|
1553
|
+
sqlarray = sql.split(/\s*WHERE\s*/)
|
|
1554
|
+
rescue ArgumentError
|
|
1555
|
+
# Invalid byte sequence in the SQL string's encoding; return as-is
|
|
1556
|
+
# so the database layer can raise ActiveRecord::StatementInvalid.
|
|
1557
|
+
return sql
|
|
1558
|
+
end
|
|
1559
|
+
size = sqlarray.size
|
|
1560
|
+
return sql if size <= 1
|
|
1561
|
+
|
|
1562
|
+
normalized = sqlarray[0] + ' WHERE '
|
|
1563
|
+
if size > 2
|
|
1564
|
+
1.upto(size - 2) do |index|
|
|
1565
|
+
sqlarray[index]&.gsub!(/(=\s*NULL|IN\s*\(NULL\))/i, 'IS NULL')
|
|
1566
|
+
normalized += sqlarray[index] + ' WHERE '
|
|
1567
|
+
end
|
|
1568
|
+
end
|
|
1569
|
+
sqlarray[size - 1]&.gsub!(/(=\s*NULL|IN\s*\(NULL\))/i, 'IS NULL')
|
|
1570
|
+
normalized + sqlarray[size - 1]
|
|
1571
|
+
end
|
|
1572
|
+
|
|
1573
|
+
# Keep adapter cache aligned with pool's thread-local store when this
|
|
1574
|
+
# connection is pinned and shared across threads.
|
|
1575
|
+
def query_cache
|
|
1576
|
+
if pool
|
|
1577
|
+
pinned_connection = pool.instance_variable_get(:@pinned_connection)
|
|
1578
|
+
if pinned_connection
|
|
1579
|
+
pool_cache = pool.query_cache
|
|
1580
|
+
@query_cache = pool_cache unless @query_cache.equal?(pool_cache)
|
|
1581
|
+
end
|
|
1582
|
+
end
|
|
1583
|
+
|
|
1584
|
+
@query_cache
|
|
1585
|
+
end
|
|
1586
|
+
|
|
1587
|
+
def query_cache_enabled
|
|
1588
|
+
query_cache&.enabled?
|
|
1589
|
+
end
|
|
1590
|
+
|
|
1270
1591
|
def select(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false)
|
|
1271
1592
|
puts_log "select sql = #{sql}"
|
|
1272
1593
|
puts_log "binds = #{binds}"
|
|
1273
1594
|
puts_log "prepare = #{prepare}"
|
|
1274
1595
|
|
|
1275
|
-
|
|
1276
|
-
begin
|
|
1277
|
-
sql.gsub(/(=\s*NULL|IN\s*\(NULL\))/i, ' IS NULL')
|
|
1278
|
-
rescue StandardError
|
|
1279
|
-
# ...
|
|
1280
|
-
end
|
|
1596
|
+
sql = normalize_null_predicates(sql).freeze
|
|
1281
1597
|
|
|
1282
1598
|
if async && async_enabled?
|
|
1283
1599
|
if current_transaction.joinable?
|
|
@@ -1291,7 +1607,7 @@ module ActiveRecord
|
|
|
1291
1607
|
binds,
|
|
1292
1608
|
prepare: prepare
|
|
1293
1609
|
)
|
|
1294
|
-
if supports_concurrent_connections?
|
|
1610
|
+
if supports_concurrent_connections?
|
|
1295
1611
|
future_result.schedule!(ActiveRecord::Base.asynchronous_queries_session)
|
|
1296
1612
|
else
|
|
1297
1613
|
future_result.execute!(self)
|
|
@@ -1303,14 +1619,22 @@ module ActiveRecord
|
|
|
1303
1619
|
cols = []
|
|
1304
1620
|
|
|
1305
1621
|
stmt = if binds.nil? || binds.empty?
|
|
1306
|
-
internal_execute(sql, name, allow_retry: allow_retry)
|
|
1622
|
+
internal_execute(sql, name, allow_retry: allow_retry, async: async)
|
|
1307
1623
|
else
|
|
1308
1624
|
exec_query_ret_stmt(sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry)
|
|
1309
1625
|
end
|
|
1310
1626
|
|
|
1311
1627
|
if stmt
|
|
1312
|
-
|
|
1313
|
-
|
|
1628
|
+
@lock.synchronize do
|
|
1629
|
+
begin
|
|
1630
|
+
cols = IBM_DB.resultCols(stmt)
|
|
1631
|
+
results = fetch_data(stmt)
|
|
1632
|
+
rescue UncaughtThrowError => e
|
|
1633
|
+
puts_log "result metadata race ignored in select: #{e.message}"
|
|
1634
|
+
cols = []
|
|
1635
|
+
results = []
|
|
1636
|
+
end
|
|
1637
|
+
end
|
|
1314
1638
|
end
|
|
1315
1639
|
|
|
1316
1640
|
puts_log "select cols = #{cols}, results = #{results}"
|
|
@@ -1331,32 +1655,33 @@ module ActiveRecord
|
|
|
1331
1655
|
def translate_exception(exception, message:, sql:, binds:)
|
|
1332
1656
|
puts_log "translate_exception - exception = #{exception}, message = #{message}"
|
|
1333
1657
|
puts_log "translate_exception #{caller}"
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
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?
|
|
1658
|
+
error_message = message.to_s
|
|
1659
|
+
sql_state = error_message[/SQLSTATE=([0-9A-Z]+)/i, 1]&.upcase
|
|
1660
|
+
sql_code = error_message[/SQLCODE=([-0-9]+)/i, 1]&.to_i
|
|
1661
|
+
|
|
1662
|
+
if sql_state == '23505' || sql_code == -803 || error_message.match?(/SQL0803N/i)
|
|
1343
1663
|
puts_log 'RecordNotUnique exception'
|
|
1344
1664
|
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
1345
|
-
elsif
|
|
1346
|
-
puts_log 'ArgumentError exception'
|
|
1347
|
-
ArgumentError.new(message)
|
|
1348
|
-
elsif !error_msg3.match(message).nil?
|
|
1665
|
+
elsif sql_state == '22003' || sql_code == -413 || error_message.match?(/SQL0413N/i)
|
|
1349
1666
|
puts_log 'RangeError exception'
|
|
1350
1667
|
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
1351
|
-
elsif
|
|
1668
|
+
elsif sql_state == '23502' || sql_state == '42802' || sql_code == -407 || sql_code == -117 ||
|
|
1669
|
+
error_message.match?(/SQL0407N|SQL0117N/i)
|
|
1352
1670
|
puts_log 'NotNullViolation exception'
|
|
1353
1671
|
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
1354
|
-
elsif
|
|
1672
|
+
elsif sql_state == '23503' || sql_state == '23504' || sql_code == -530 || sql_code == -532 ||
|
|
1673
|
+
error_message.match?(/SQL0530N|SQL0532N/i)
|
|
1355
1674
|
puts_log 'InvalidForeignKey exception'
|
|
1356
1675
|
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
1357
|
-
elsif
|
|
1676
|
+
elsif sql_state == '22001' || sql_code == -433 || error_message.match?(/SQL0433N|CLI0109E/i)
|
|
1358
1677
|
puts_log 'ValueTooLong exception'
|
|
1359
1678
|
ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
1679
|
+
elsif sql_state == 'HY010' || error_message.match?(/CLI0125E/i)
|
|
1680
|
+
puts_log 'ConnectionFailed exception'
|
|
1681
|
+
ConnectionFailed.new(message, connection_pool: @pool)
|
|
1682
|
+
elsif sql_state || sql_code
|
|
1683
|
+
puts_log 'StatementInvalid exception'
|
|
1684
|
+
StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
1360
1685
|
elsif exception.message.match?(/called on a closed database/i)
|
|
1361
1686
|
puts_log 'ConnectionNotEstablished exception'
|
|
1362
1687
|
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
|
@@ -1366,8 +1691,38 @@ module ActiveRecord
|
|
|
1366
1691
|
message.strip.start_with?("ActiveRecord::ConnectionFailed")
|
|
1367
1692
|
exception
|
|
1368
1693
|
else
|
|
1369
|
-
super(
|
|
1694
|
+
super(exception, message: message, sql: sql, binds: binds)
|
|
1695
|
+
end
|
|
1696
|
+
end
|
|
1697
|
+
|
|
1698
|
+
# Re-run adapter translation for generic StatementInvalid emitted by the driver
|
|
1699
|
+
# so SQLSTATE/SQLCODE can map to specific ActiveRecord exceptions.
|
|
1700
|
+
def translate_exception_class(native_error, sql, binds)
|
|
1701
|
+
return native_error if native_error.is_a?(ActiveRecordError) && !native_error.is_a?(StatementInvalid)
|
|
1702
|
+
|
|
1703
|
+
message = if native_error.is_a?(ActiveRecordError)
|
|
1704
|
+
native_error.message
|
|
1705
|
+
else
|
|
1706
|
+
"#{native_error.class.name}: #{native_error.message}"
|
|
1707
|
+
end
|
|
1708
|
+
|
|
1709
|
+
active_record_error = translate_exception(native_error, message: message, sql: sql, binds: binds)
|
|
1710
|
+
active_record_error.set_backtrace(native_error.backtrace)
|
|
1711
|
+
active_record_error
|
|
1712
|
+
end
|
|
1713
|
+
|
|
1714
|
+
def cast_result(raw_result)
|
|
1715
|
+
return ActiveRecord::Result.empty unless raw_result
|
|
1716
|
+
|
|
1717
|
+
cols = IBM_DB.resultCols(raw_result)
|
|
1718
|
+
return ActiveRecord::Result.empty if cols.nil? || cols.empty?
|
|
1719
|
+
|
|
1720
|
+
rows = []
|
|
1721
|
+
@lock.synchronize do
|
|
1722
|
+
rows = @servertype.select_rows(nil, nil, raw_result, [])
|
|
1370
1723
|
end
|
|
1724
|
+
|
|
1725
|
+
ActiveRecord::Result.new(cols, rows)
|
|
1371
1726
|
end
|
|
1372
1727
|
|
|
1373
1728
|
def build_truncate_statement(table_name)
|
|
@@ -1376,13 +1731,20 @@ module ActiveRecord
|
|
|
1376
1731
|
end
|
|
1377
1732
|
|
|
1378
1733
|
def build_fixture_sql(fixtures, table_name)
|
|
1379
|
-
columns = schema_cache.columns_hash(table_name)
|
|
1734
|
+
columns = schema_cache.columns_hash(table_name)
|
|
1735
|
+
# Some quoted identifiers (for example table names with '-') can miss the schema cache lookup key.
|
|
1736
|
+
# Fall back to direct metadata to keep fixture insertion aligned with actual table columns.
|
|
1737
|
+
if columns.empty?
|
|
1738
|
+
columns = self.columns(table_name).index_by(&:name)
|
|
1739
|
+
end
|
|
1740
|
+
columns = columns.reject { |_, column| supports_virtual_columns? && column.virtual? }
|
|
1741
|
+
columns = columns.transform_keys { |name| name.to_s.downcase }
|
|
1380
1742
|
puts_log "build_fixture_sql - Table = #{table_name}"
|
|
1381
1743
|
puts_log "build_fixture_sql - Fixtures = #{fixtures}"
|
|
1382
1744
|
puts_log "build_fixture_sql - Columns = #{columns}"
|
|
1383
1745
|
|
|
1384
1746
|
values_list = fixtures.map do |fixture|
|
|
1385
|
-
fixture = fixture
|
|
1747
|
+
fixture = normalize_fixture_row_for_insert(fixture)
|
|
1386
1748
|
fixture = fixture.transform_keys(&:downcase)
|
|
1387
1749
|
|
|
1388
1750
|
unknown_columns = fixture.keys - columns.keys
|
|
@@ -1421,6 +1783,30 @@ module ActiveRecord
|
|
|
1421
1783
|
visitor.compile(manager.ast)
|
|
1422
1784
|
end
|
|
1423
1785
|
|
|
1786
|
+
def normalize_fixture_row_for_insert(fixture)
|
|
1787
|
+
normalized = {}
|
|
1788
|
+
|
|
1789
|
+
fixture.each do |key, value|
|
|
1790
|
+
if key.is_a?(Array)
|
|
1791
|
+
key_values = if value.nil?
|
|
1792
|
+
Array.new(key.length)
|
|
1793
|
+
elsif value.is_a?(Array)
|
|
1794
|
+
value
|
|
1795
|
+
else
|
|
1796
|
+
Array(value)
|
|
1797
|
+
end
|
|
1798
|
+
|
|
1799
|
+
key.each_with_index do |part, index|
|
|
1800
|
+
normalized[part.to_s] = key_values[index]
|
|
1801
|
+
end
|
|
1802
|
+
else
|
|
1803
|
+
normalized[key.to_s] = value
|
|
1804
|
+
end
|
|
1805
|
+
end
|
|
1806
|
+
|
|
1807
|
+
normalized
|
|
1808
|
+
end
|
|
1809
|
+
|
|
1424
1810
|
def build_fixture_statements(fixture_set)
|
|
1425
1811
|
puts_log "build_fixture_statements - fixture_set = #{fixture_set}"
|
|
1426
1812
|
fixture_set.filter_map do |table_name, fixtures|
|
|
@@ -1459,7 +1845,10 @@ module ActiveRecord
|
|
|
1459
1845
|
if item.at(1).nil? ||
|
|
1460
1846
|
item.at(1) == {} ||
|
|
1461
1847
|
(item.at(1) == '' && !(col.sql_type.to_s =~ /text|clob/i))
|
|
1462
|
-
|
|
1848
|
+
# Prefer DEFAULT for omitted fixture values when the column can derive one.
|
|
1849
|
+
# This avoids forcing NULL into NOT NULL columns that have defaults/generated values.
|
|
1850
|
+
has_implicit_default = !col.nil? && (!col.default.nil? || !col.default_function.nil?)
|
|
1851
|
+
params << (has_implicit_default ? 'DEFAULT' : 'NULL')
|
|
1463
1852
|
|
|
1464
1853
|
elsif !col.nil? && (col.sql_type.to_s =~ /blob|binary|clob|text|xml/i)
|
|
1465
1854
|
# Add a '?' for the parameter or a NULL if the value is nil or empty
|
|
@@ -1556,9 +1945,20 @@ module ActiveRecord
|
|
|
1556
1945
|
@handle_lobs_triggered = false
|
|
1557
1946
|
end
|
|
1558
1947
|
|
|
1559
|
-
|
|
1948
|
+
begin
|
|
1949
|
+
return unless stmt = execute(sql, name)
|
|
1950
|
+
rescue ActiveRecord::RecordNotUnique
|
|
1951
|
+
# Internal metadata writes are logically idempotent for a given key.
|
|
1952
|
+
return nil if duplicate_internal_metadata_insert?(sql)
|
|
1953
|
+
raise
|
|
1954
|
+
end
|
|
1560
1955
|
|
|
1561
1956
|
begin
|
|
1957
|
+
if !@servertype.is_a?(IBM_IDS)
|
|
1958
|
+
IBM_DB.free_stmt(stmt)
|
|
1959
|
+
stmt = nil
|
|
1960
|
+
end
|
|
1961
|
+
|
|
1562
1962
|
return_insert(stmt, sql, nil, _pk, id_value, returning: returning)
|
|
1563
1963
|
# Ensures to free the resources associated with the statement
|
|
1564
1964
|
ensure
|
|
@@ -1576,16 +1976,23 @@ module ActiveRecord
|
|
|
1576
1976
|
sql, binds = to_sql_and_binds(arel, binds)
|
|
1577
1977
|
end
|
|
1578
1978
|
|
|
1579
|
-
puts_log "insert Binds A = #{binds}"
|
|
1979
|
+
puts_log "insert Binds A count = #{Array(binds).size}"
|
|
1580
1980
|
puts_log "insert SQL = #{sql}"
|
|
1581
1981
|
# unless IBM_DBAdapter.respond_to?(:exec_insert)
|
|
1582
1982
|
return insert_direct(sql, name, pk, id_value, returning: returning) if binds.nil? || binds.empty?
|
|
1583
1983
|
|
|
1584
|
-
|
|
1984
|
+
if pool.dirties_query_cache
|
|
1985
|
+
ActiveRecord::Base.clear_query_caches_for_current_thread
|
|
1986
|
+
end
|
|
1585
1987
|
|
|
1586
1988
|
return unless stmt = exec_insert_db2(sql, name, binds, pk, sequence_name, returning)
|
|
1587
1989
|
|
|
1588
1990
|
begin
|
|
1991
|
+
if !@servertype.is_a?(IBM_IDS)
|
|
1992
|
+
IBM_DB.free_stmt(stmt)
|
|
1993
|
+
stmt = nil
|
|
1994
|
+
end
|
|
1995
|
+
|
|
1589
1996
|
return_insert(stmt, sql, binds, pk, id_value, returning: returning)
|
|
1590
1997
|
ensure
|
|
1591
1998
|
IBM_DB.free_stmt(stmt) if stmt
|
|
@@ -1593,7 +2000,7 @@ module ActiveRecord
|
|
|
1593
2000
|
end
|
|
1594
2001
|
|
|
1595
2002
|
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},
|
|
2003
|
+
puts_log "exec_insert_db2 sql = #{sql}, name = #{name}, binds_count = #{Array(binds).size}, pk = #{pk}, returning = #{returning}"
|
|
1597
2004
|
sql, binds = sql_for_insert(sql, pk, binds, returning)
|
|
1598
2005
|
exec_query_ret_stmt(sql, name, binds, prepare: false)
|
|
1599
2006
|
end
|
|
@@ -1608,6 +2015,22 @@ module ActiveRecord
|
|
|
1608
2015
|
result
|
|
1609
2016
|
end
|
|
1610
2017
|
|
|
2018
|
+
private
|
|
2019
|
+
|
|
2020
|
+
def duplicate_internal_metadata_insert?(sql)
|
|
2021
|
+
sql.to_s.match?(/\A\s*INSERT\s+INTO\s+"?AR_INTERNAL_METADATA"?/i)
|
|
2022
|
+
end
|
|
2023
|
+
|
|
2024
|
+
# Override base implementation to support hyphens in quoted table names.
|
|
2025
|
+
# DB2 allows identifiers like "table-with-hyphen"; the base regex omits '-'.
|
|
2026
|
+
def extract_table_ref_from_insert_sql(sql)
|
|
2027
|
+
if sql =~ /into\s("[A-Za-z0-9_.\-"\[\]\s]+"|[A-Za-z0-9_.".\[\]]+)\s*/im
|
|
2028
|
+
$1.delete('"').strip
|
|
2029
|
+
end
|
|
2030
|
+
end
|
|
2031
|
+
|
|
2032
|
+
public
|
|
2033
|
+
|
|
1611
2034
|
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) # :nodoc:
|
|
1612
2035
|
puts_log 'exec_insert'
|
|
1613
2036
|
insert(sql)
|
|
@@ -1625,7 +2048,9 @@ module ActiveRecord
|
|
|
1625
2048
|
@handle_lobs_triggered = false
|
|
1626
2049
|
end
|
|
1627
2050
|
|
|
1628
|
-
|
|
2051
|
+
if pool.dirties_query_cache
|
|
2052
|
+
ActiveRecord::Base.clear_query_caches_for_current_thread
|
|
2053
|
+
end
|
|
1629
2054
|
|
|
1630
2055
|
begin
|
|
1631
2056
|
if execute_prepared_stmt(pstmt, param_array)
|
|
@@ -1664,10 +2089,13 @@ module ActiveRecord
|
|
|
1664
2089
|
error_msg = IBM_DB.getErrormsg(pstmt, IBM_DB::DB_STMT)
|
|
1665
2090
|
puts_log "Error = #{error_msg}"
|
|
1666
2091
|
IBM_DB.free_stmt(pstmt) if pstmt
|
|
1667
|
-
raise
|
|
2092
|
+
raise error_msg
|
|
1668
2093
|
else
|
|
1669
2094
|
true
|
|
1670
2095
|
end
|
|
2096
|
+
rescue => e
|
|
2097
|
+
IBM_DB.free_stmt(pstmt) if pstmt
|
|
2098
|
+
raise e
|
|
1671
2099
|
end
|
|
1672
2100
|
|
|
1673
2101
|
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
|
|
@@ -1682,21 +2110,19 @@ module ActiveRecord
|
|
|
1682
2110
|
end
|
|
1683
2111
|
|
|
1684
2112
|
def explain(arel, binds = [], options = [])
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
2113
|
+
# DB2's EXPLAIN facility requires system explain tables (EXPLAIN_INSTANCE,
|
|
2114
|
+
# EXPLAIN_STATEMENT, etc.) to exist in the schema, which are not present in
|
|
2115
|
+
# test environments. Return the EXPLAIN statement string directly — this
|
|
2116
|
+
# satisfies the ActiveRecord contract (output starts with "EXPLAIN" and
|
|
2117
|
+
# contains the original SQL) without requiring explain tables.
|
|
2118
|
+
original_sql = to_sql(arel, binds)
|
|
2119
|
+
"EXPLAIN ALL SET QUERYNO = 1 FOR #{original_sql}"
|
|
1692
2120
|
end
|
|
1693
2121
|
|
|
1694
2122
|
def execute_without_logging(sql, name = nil, binds = [], prepare: true, async: false)
|
|
1695
2123
|
puts_log "execute_without_logging sql = #{sql}, name = #{name}, binds = #{binds}"
|
|
1696
2124
|
|
|
1697
|
-
sql =
|
|
1698
|
-
check_if_write_query(sql)
|
|
1699
|
-
mark_transaction_written_if_write(sql)
|
|
2125
|
+
sql = preprocess_query(sql)
|
|
1700
2126
|
cols = nil
|
|
1701
2127
|
results = nil
|
|
1702
2128
|
begin
|
|
@@ -1738,34 +2164,78 @@ module ActiveRecord
|
|
|
1738
2164
|
# Here prepare argument is not used, by default this method creates prepared statment and execute.
|
|
1739
2165
|
def exec_query_ret_stmt(sql, name = 'SQL', binds = [], prepare: false, async: false, allow_retry: false)
|
|
1740
2166
|
puts_log "exec_query_ret_stmt #{sql}"
|
|
1741
|
-
sql =
|
|
1742
|
-
|
|
1743
|
-
mark_transaction_written_if_write(sql)
|
|
2167
|
+
sql = preprocess_query(sql)
|
|
2168
|
+
function_sequence_retry_count = 0
|
|
1744
2169
|
begin
|
|
1745
2170
|
puts_log "SQL = #{sql}"
|
|
1746
|
-
puts_log "Binds = #{binds}"
|
|
2171
|
+
puts_log "Binds count = #{Array(binds).size}"
|
|
1747
2172
|
param_array = type_casted_binds(binds)
|
|
2173
|
+
sql, param_array = inline_limit_offset_binds(sql, param_array)
|
|
1748
2174
|
puts_log "Param array = #{param_array}"
|
|
1749
2175
|
puts_log "Prepare flag = #{prepare}"
|
|
1750
2176
|
puts_log "#{caller}"
|
|
1751
2177
|
|
|
1752
|
-
stmt =
|
|
1753
|
-
@
|
|
2178
|
+
stmt = nil
|
|
2179
|
+
@lock.synchronize do
|
|
2180
|
+
stmt = @servertype.prepare(sql, name)
|
|
2181
|
+
@statements[sql] = stmt if prepare
|
|
2182
|
+
end
|
|
1754
2183
|
|
|
1755
2184
|
puts_log "Statement = #{stmt}"
|
|
1756
2185
|
log(sql, name, binds, param_array, async: async) do
|
|
1757
2186
|
with_raw_connection(allow_retry: allow_retry) do |conn|
|
|
1758
2187
|
return false unless stmt
|
|
1759
|
-
|
|
2188
|
+
@lock.synchronize do
|
|
2189
|
+
return stmt if execute_prepared_stmt(stmt, param_array)
|
|
2190
|
+
end
|
|
1760
2191
|
end
|
|
1761
2192
|
end
|
|
2193
|
+
rescue ::RangeError
|
|
2194
|
+
# Re-raise ActiveModel::RangeError (a ::RangeError subclass) unwrapped so
|
|
2195
|
+
# ActiveRecord core can rescue ::RangeError in cached_find_by and find,
|
|
2196
|
+
# converting it to nil or RecordNotFound respectively.
|
|
2197
|
+
raise
|
|
1762
2198
|
rescue => e
|
|
2199
|
+
if function_sequence_retry_count < 1 && function_sequence_error?(e)
|
|
2200
|
+
function_sequence_retry_count += 1
|
|
2201
|
+
puts_log "exec_query_ret_stmt retry ##{function_sequence_retry_count} after function sequence error: #{e.message}"
|
|
2202
|
+
retry
|
|
2203
|
+
end
|
|
1763
2204
|
raise translate_exception_class(e, sql, binds)
|
|
1764
2205
|
ensure
|
|
1765
2206
|
@offset = @limit = nil
|
|
1766
2207
|
end
|
|
1767
2208
|
end
|
|
1768
2209
|
|
|
2210
|
+
# DB2 does not reliably accept bind markers for LIMIT/OFFSET in prepared
|
|
2211
|
+
# statements, so inline trailing numeric values and keep other binds intact.
|
|
2212
|
+
def inline_limit_offset_binds(sql, param_array)
|
|
2213
|
+
return [sql, param_array] if sql.nil? || param_array.nil? || param_array.empty?
|
|
2214
|
+
return [sql, param_array] unless @servertype.instance_of?(ActiveRecord::ConnectionAdapters::IBM_DB2)
|
|
2215
|
+
|
|
2216
|
+
rewritten_sql = sql.dup
|
|
2217
|
+
rewritten_binds = param_array.dup
|
|
2218
|
+
|
|
2219
|
+
if rewritten_sql.match?(/\bLIMIT\s+\?\s+OFFSET\s+\?\s*\z/i)
|
|
2220
|
+
offset_value = Integer(rewritten_binds.pop) rescue nil
|
|
2221
|
+
limit_value = Integer(rewritten_binds.pop) rescue nil
|
|
2222
|
+
return [sql, param_array] if limit_value.nil? || offset_value.nil?
|
|
2223
|
+
|
|
2224
|
+
rewritten_sql.sub!(/\bLIMIT\s+\?\s+OFFSET\s+\?\s*\z/i, "LIMIT #{limit_value} OFFSET #{offset_value}")
|
|
2225
|
+
return [rewritten_sql, rewritten_binds]
|
|
2226
|
+
end
|
|
2227
|
+
|
|
2228
|
+
if rewritten_sql.match?(/\bLIMIT\s+\?\s*\z/i)
|
|
2229
|
+
limit_value = Integer(rewritten_binds.pop) rescue nil
|
|
2230
|
+
return [sql, param_array] if limit_value.nil?
|
|
2231
|
+
|
|
2232
|
+
rewritten_sql.sub!(/\bLIMIT\s+\?\s*\z/i, "LIMIT #{limit_value}")
|
|
2233
|
+
return [rewritten_sql, rewritten_binds]
|
|
2234
|
+
end
|
|
2235
|
+
|
|
2236
|
+
[sql, param_array]
|
|
2237
|
+
end
|
|
2238
|
+
|
|
1769
2239
|
def internal_exec_query(sql, name = 'SQL', binds = [], prepare: false, async: false)
|
|
1770
2240
|
select_prepared(sql, name, binds, prepare: prepare, async: async)
|
|
1771
2241
|
end
|
|
@@ -1779,9 +2249,16 @@ module ActiveRecord
|
|
|
1779
2249
|
results = nil
|
|
1780
2250
|
|
|
1781
2251
|
if stmt and sql.strip.upcase.start_with?("SELECT")
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
2252
|
+
@lock.synchronize do
|
|
2253
|
+
begin
|
|
2254
|
+
cols = IBM_DB.resultCols(stmt)
|
|
2255
|
+
results = fetch_data(stmt) if stmt
|
|
2256
|
+
rescue UncaughtThrowError => e
|
|
2257
|
+
puts_log "result metadata race ignored in select_prepared: #{e.message}"
|
|
2258
|
+
cols = []
|
|
2259
|
+
results = []
|
|
2260
|
+
end
|
|
2261
|
+
end
|
|
1785
2262
|
|
|
1786
2263
|
puts_log "select_prepared columns = #{cols}"
|
|
1787
2264
|
puts_log "select_prepared sql after = #{sql}"
|
|
@@ -1804,10 +2281,12 @@ module ActiveRecord
|
|
|
1804
2281
|
|
|
1805
2282
|
# Executes and logs +sql+ commands and
|
|
1806
2283
|
# returns a +IBM_DB.Statement+ object.
|
|
1807
|
-
def execute(sql, name = nil, allow_retry: false)
|
|
2284
|
+
def execute(sql, name = nil, allow_retry: false, async: false)
|
|
1808
2285
|
puts_log "execute #{sql}"
|
|
1809
|
-
|
|
1810
|
-
|
|
2286
|
+
if pool.dirties_query_cache
|
|
2287
|
+
ActiveRecord::Base.clear_query_caches_for_current_thread
|
|
2288
|
+
end
|
|
2289
|
+
stmt = internal_execute(sql, name, allow_retry: allow_retry, async: async)
|
|
1811
2290
|
cols = nil
|
|
1812
2291
|
results = nil
|
|
1813
2292
|
puts_log "raw_execute stmt = #{stmt}"
|
|
@@ -1827,23 +2306,52 @@ module ActiveRecord
|
|
|
1827
2306
|
end
|
|
1828
2307
|
end
|
|
1829
2308
|
|
|
1830
|
-
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
|
|
2309
|
+
def raw_execute(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true, batch: false)
|
|
1831
2310
|
# Logs and execute the sql instructions.
|
|
1832
2311
|
# The +log+ method is defined in the parent class +AbstractAdapter+
|
|
1833
2312
|
# sql='INSERT INTO ar_internal_metadata (key, value, created_at, updated_at) VALUES ('10', '10', '10', '10')
|
|
1834
2313
|
puts_log "raw_execute sql = #{sql} #{Thread.current}"
|
|
1835
2314
|
log(sql, name, async: async) do
|
|
1836
2315
|
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
2316
|
+
retry_count = 0
|
|
2317
|
+
begin
|
|
2318
|
+
verify!
|
|
2319
|
+
puts_log "raw_execute executes query #{Thread.current}"
|
|
2320
|
+
sql = preprocess_query(sql)
|
|
2321
|
+
if binds.nil? || binds.empty?
|
|
2322
|
+
result = @servertype.execute(sql, name)
|
|
2323
|
+
else
|
|
2324
|
+
param_array = type_casted_binds(binds)
|
|
2325
|
+
stmt = @servertype.prepare(sql, name)
|
|
2326
|
+
@statements[sql] = stmt if prepare
|
|
2327
|
+
result = execute_prepared_stmt(stmt, param_array) ? stmt : false
|
|
2328
|
+
end
|
|
2329
|
+
puts_log "raw_execute result = #{result} #{Thread.current}"
|
|
2330
|
+
verified!
|
|
2331
|
+
result
|
|
2332
|
+
rescue => e
|
|
2333
|
+
if retry_count < 2 && transient_rollback_error?(e)
|
|
2334
|
+
retry_count += 1
|
|
2335
|
+
puts_log "raw_execute retry ##{retry_count} after transient rollback: #{e.message}"
|
|
2336
|
+
retry
|
|
2337
|
+
end
|
|
2338
|
+
raise
|
|
2339
|
+
end
|
|
1843
2340
|
end
|
|
1844
2341
|
end
|
|
1845
2342
|
end
|
|
1846
2343
|
|
|
2344
|
+
def transient_rollback_error?(error)
|
|
2345
|
+
message = error&.message.to_s
|
|
2346
|
+
message.include?("SQLSTATE=40001") || message.match?(/SQL0911N/i)
|
|
2347
|
+
end
|
|
2348
|
+
|
|
2349
|
+
def function_sequence_error?(error)
|
|
2350
|
+
message = error&.message.to_s
|
|
2351
|
+
sql_state = message[/SQLSTATE=([0-9A-Z]+)/i, 1]&.upcase
|
|
2352
|
+
sql_state == 'HY010' || message.match?(/CLI0125E|function sequence error/i)
|
|
2353
|
+
end
|
|
2354
|
+
|
|
1847
2355
|
# Executes an "UPDATE" SQL statement
|
|
1848
2356
|
def update_direct(sql, name = nil)
|
|
1849
2357
|
puts_log 'update_direct'
|
|
@@ -1874,7 +2382,9 @@ module ActiveRecord
|
|
|
1874
2382
|
@handle_lobs_triggered = false
|
|
1875
2383
|
end
|
|
1876
2384
|
|
|
1877
|
-
|
|
2385
|
+
if pool.dirties_query_cache
|
|
2386
|
+
ActiveRecord::Base.clear_query_caches_for_current_thread
|
|
2387
|
+
end
|
|
1878
2388
|
|
|
1879
2389
|
begin
|
|
1880
2390
|
if execute_prepared_stmt(pstmt, param_array)
|
|
@@ -1910,15 +2420,22 @@ module ActiveRecord
|
|
|
1910
2420
|
sql = sqlarray[0] + ' WHERE '
|
|
1911
2421
|
if size > 2
|
|
1912
2422
|
1.upto size - 2 do |index|
|
|
1913
|
-
sqlarray[index].gsub!(/(=\s*NULL|IN\s*\(NULL\))/i, '
|
|
2423
|
+
sqlarray[index].gsub!(/(=\s*NULL|IN\s*\(NULL\))/i, 'IS NULL') unless sqlarray[index].nil?
|
|
1914
2424
|
sql = sql + sqlarray[index] + ' WHERE '
|
|
1915
2425
|
end
|
|
1916
2426
|
end
|
|
1917
|
-
sqlarray[size - 1].gsub!(/(=\s*NULL|IN\s*\(NULL\))/i, '
|
|
2427
|
+
sqlarray[size - 1].gsub!(/(=\s*NULL|IN\s*\(NULL\))/i, 'IS NULL') unless sqlarray[size - 1].nil?
|
|
1918
2428
|
sql += sqlarray[size - 1]
|
|
1919
2429
|
end
|
|
1920
2430
|
|
|
1921
|
-
|
|
2431
|
+
# DB2 requires assignment syntax in SET clauses; normalize accidental
|
|
2432
|
+
# Arel output like "SET col IS NULL" to "SET col = NULL".
|
|
2433
|
+
sql = sql.gsub(/\bSET\s+([\w\."`\[\]]+)\s+IS\s+NULL\b/i, 'SET \1 = NULL')
|
|
2434
|
+
sql = sql.gsub(/,\s*([\w\."`\[\]]+)\s+IS\s+NULL\b/i, ', \1 = NULL')
|
|
2435
|
+
|
|
2436
|
+
if pool.dirties_query_cache
|
|
2437
|
+
ActiveRecord::Base.clear_query_caches_for_current_thread
|
|
2438
|
+
end
|
|
1922
2439
|
|
|
1923
2440
|
if binds.nil? || binds.empty?
|
|
1924
2441
|
update_direct(sql, name)
|
|
@@ -1973,36 +2490,52 @@ module ActiveRecord
|
|
|
1973
2490
|
def commit_db_transaction
|
|
1974
2491
|
puts_log 'commit_db_transaction'
|
|
1975
2492
|
log('commit transaction', 'TRANSACTION') do
|
|
1976
|
-
with_raw_connection(allow_retry:
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
IBM_DB.commit @connection
|
|
2493
|
+
with_raw_connection(allow_retry: false, materialize_transactions: true) do |conn|
|
|
2494
|
+
exec_commit_db_transaction
|
|
2495
|
+
verified!
|
|
1980
2496
|
end
|
|
1981
|
-
rescue StandardError
|
|
1982
|
-
nil
|
|
1983
2497
|
end
|
|
1984
2498
|
# Turns on auto-committing
|
|
1985
2499
|
auto_commit_on
|
|
1986
2500
|
end
|
|
1987
2501
|
|
|
2502
|
+
def exec_commit_db_transaction
|
|
2503
|
+
IBM_DB.commit(@connection)
|
|
2504
|
+
end
|
|
2505
|
+
|
|
1988
2506
|
# Rolls back the transaction and turns on auto-committing. Must be
|
|
1989
2507
|
# done if the transaction block raises an exception or returns false
|
|
1990
2508
|
def rollback_db_transaction
|
|
1991
2509
|
puts_log 'rollback_db_transaction'
|
|
1992
2510
|
log('rollback transaction', 'TRANSACTION') do
|
|
1993
|
-
with_raw_connection(allow_retry:
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
IBM_DB.rollback(@connection)
|
|
2511
|
+
with_raw_connection(allow_retry: false, materialize_transactions: true) do |conn|
|
|
2512
|
+
exec_rollback_db_transaction
|
|
2513
|
+
verified!
|
|
1997
2514
|
end
|
|
1998
|
-
rescue StandardError
|
|
1999
|
-
nil
|
|
2000
2515
|
end
|
|
2001
|
-
|
|
2516
|
+
if pool.dirties_query_cache
|
|
2517
|
+
ActiveRecord::Base.clear_query_caches_for_current_thread
|
|
2518
|
+
end
|
|
2002
2519
|
# Turns on auto-committing
|
|
2003
2520
|
auto_commit_on
|
|
2004
2521
|
end
|
|
2005
2522
|
|
|
2523
|
+
def exec_rollback_db_transaction
|
|
2524
|
+
IBM_DB.rollback(@connection)
|
|
2525
|
+
end
|
|
2526
|
+
|
|
2527
|
+
def create_savepoint(name = current_savepoint_name)
|
|
2528
|
+
internal_execute("SAVEPOINT #{name} ON ROLLBACK RETAIN CURSORS", "TRANSACTION")
|
|
2529
|
+
end
|
|
2530
|
+
|
|
2531
|
+
def exec_rollback_to_savepoint(name = current_savepoint_name)
|
|
2532
|
+
internal_execute("ROLLBACK TO SAVEPOINT #{name}", "TRANSACTION")
|
|
2533
|
+
end
|
|
2534
|
+
|
|
2535
|
+
def release_savepoint(name = current_savepoint_name)
|
|
2536
|
+
internal_execute("RELEASE SAVEPOINT #{name}", "TRANSACTION")
|
|
2537
|
+
end
|
|
2538
|
+
|
|
2006
2539
|
def default_sequence_name(table, column) # :nodoc:
|
|
2007
2540
|
puts_log "default_sequence_name table = #{table}, column = #{column}"
|
|
2008
2541
|
return nil if column.is_a?(Array)
|
|
@@ -2075,16 +2608,66 @@ module ActiveRecord
|
|
|
2075
2608
|
def quote_table_name(name)
|
|
2076
2609
|
puts_log "quote_table_name #{name}"
|
|
2077
2610
|
puts_log caller
|
|
2078
|
-
|
|
2079
|
-
|
|
2611
|
+
raw = name.to_s.gsub("'", '')
|
|
2612
|
+
|
|
2613
|
+
# Preserve already-quoted composite identifiers such as "schema.table"
|
|
2614
|
+
# as a single DB2 identifier.
|
|
2615
|
+
if raw.start_with?('"') && raw.end_with?('"')
|
|
2616
|
+
inner = raw[1..-2].to_s.gsub('"', '')
|
|
2617
|
+
quoted = "\"#{inner}\""
|
|
2618
|
+
puts_log "name = #{quoted}"
|
|
2619
|
+
return quoted
|
|
2620
|
+
end
|
|
2621
|
+
|
|
2622
|
+
str = raw.gsub('"', '')
|
|
2623
|
+
if str.include?('.')
|
|
2624
|
+
result = str.split('.').map { |part|
|
|
2625
|
+
if part.match?(/\A[A-Za-z_][A-Za-z0-9_]*\z/)
|
|
2626
|
+
part
|
|
2627
|
+
else
|
|
2628
|
+
"\"#{part}\""
|
|
2629
|
+
end
|
|
2630
|
+
}.join('.')
|
|
2631
|
+
puts_log "name = #{result}"
|
|
2632
|
+
return result
|
|
2633
|
+
end
|
|
2634
|
+
if str.start_with?('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') || str.match?(/[^A-Za-z0-9_]/)
|
|
2635
|
+
name = "\"#{str}\""
|
|
2080
2636
|
else
|
|
2081
|
-
name =
|
|
2637
|
+
name = str
|
|
2082
2638
|
end
|
|
2083
2639
|
puts_log "name = #{name}"
|
|
2084
2640
|
name
|
|
2085
2641
|
# @servertype.check_reserved_words(name).gsub('"', '').gsub("'",'')
|
|
2086
2642
|
end
|
|
2087
2643
|
|
|
2644
|
+
# Class-level version used by test helpers (ActiveRecord::Base.adapter_class.quote_table_name).
|
|
2645
|
+
# Must mirror the instance method so test regex patterns match actual SQL.
|
|
2646
|
+
def self.quote_table_name(name)
|
|
2647
|
+
raw = name.to_s.gsub("'", '')
|
|
2648
|
+
|
|
2649
|
+
if raw.start_with?('"') && raw.end_with?('"')
|
|
2650
|
+
inner = raw[1..-2].to_s.gsub('"', '')
|
|
2651
|
+
return "\"#{inner}\""
|
|
2652
|
+
end
|
|
2653
|
+
|
|
2654
|
+
str = raw.gsub('"', '')
|
|
2655
|
+
if str.include?('.')
|
|
2656
|
+
return str.split('.').map { |part|
|
|
2657
|
+
if part.match?(/\A[A-Za-z_][A-Za-z0-9_]*\z/)
|
|
2658
|
+
part
|
|
2659
|
+
else
|
|
2660
|
+
"\"#{part}\""
|
|
2661
|
+
end
|
|
2662
|
+
}.join('.')
|
|
2663
|
+
end
|
|
2664
|
+
if str.start_with?('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') || str.match?(/[^A-Za-z0-9_]/)
|
|
2665
|
+
"\"#{str}\""
|
|
2666
|
+
else
|
|
2667
|
+
str
|
|
2668
|
+
end
|
|
2669
|
+
end
|
|
2670
|
+
|
|
2088
2671
|
def quote_column_name(name)
|
|
2089
2672
|
puts_log "quote_column_name #{name}"
|
|
2090
2673
|
@servertype.check_reserved_words(name).gsub('"', '').gsub("'", '')
|
|
@@ -2126,7 +2709,8 @@ module ActiveRecord
|
|
|
2126
2709
|
decfloat: { name: 'decfloat' },
|
|
2127
2710
|
graphic: { name: 'graphic' },
|
|
2128
2711
|
vargraphic: { name: 'vargraphic' },
|
|
2129
|
-
bigint: { name: 'bigint' }
|
|
2712
|
+
bigint: { name: 'bigint' },
|
|
2713
|
+
json: { name: 'clob' }
|
|
2130
2714
|
}
|
|
2131
2715
|
end
|
|
2132
2716
|
|
|
@@ -2202,26 +2786,33 @@ module ActiveRecord
|
|
|
2202
2786
|
puts_log "type_to_sql = #{caller}"
|
|
2203
2787
|
|
|
2204
2788
|
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
|
|
2789
|
+
sql_segment = +native_database_types[type.to_sym][:name].to_s
|
|
2206
2790
|
sql_segment << "(#{limit[:limit]})"
|
|
2207
2791
|
return sql_segment
|
|
2208
2792
|
end
|
|
2209
2793
|
|
|
2210
2794
|
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
|
|
2795
|
+
sql_segment = +native_database_types[type.to_sym][:name].to_s
|
|
2212
2796
|
if limit[:precision].nil?
|
|
2797
|
+
# Use TIMESTAMP(12) as a sentinel for "precision: nil".
|
|
2798
|
+
# DB2 makes bare TIMESTAMP and TIMESTAMP(6) look identical in metadata
|
|
2799
|
+
# (both return decimal_digits=6). TIMESTAMP(12) is detectable on read-back
|
|
2800
|
+
# and is outside the user-accessible range (0..6).
|
|
2801
|
+
sql_segment << "(12)"
|
|
2213
2802
|
return sql_segment
|
|
2214
|
-
elsif (0..
|
|
2803
|
+
elsif (0..6).include?(limit[:precision])
|
|
2215
2804
|
sql_segment << "(#{limit[:precision]})"
|
|
2216
2805
|
return sql_segment
|
|
2217
2806
|
else
|
|
2218
2807
|
raise ArgumentError,
|
|
2219
|
-
"No #{sql_segment} type has precision of #{limit[:precision]}. The allowed range of precision is from 0 to
|
|
2808
|
+
"No #{sql_segment} type has precision of #{limit[:precision]}. The allowed range of precision is from 0 to 6"
|
|
2220
2809
|
end
|
|
2221
2810
|
end
|
|
2222
2811
|
|
|
2223
2812
|
if type.to_sym == :string and limit.class == Hash and limit.has_key?('limit'.to_sym)
|
|
2224
|
-
|
|
2813
|
+
return super(type) if limit[:limit].nil?
|
|
2814
|
+
|
|
2815
|
+
sql_segment = +native_database_types[type.to_sym][:name].to_s
|
|
2225
2816
|
sql_segment << "(#{limit[:limit]})"
|
|
2226
2817
|
return sql_segment
|
|
2227
2818
|
end
|
|
@@ -2229,7 +2820,25 @@ module ActiveRecord
|
|
|
2229
2820
|
if type.to_sym == :decimal
|
|
2230
2821
|
precision = limit[:precision] if limit.class == Hash && limit.has_key?('precision'.to_sym)
|
|
2231
2822
|
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
|
|
2823
|
+
sql_segment = +native_database_types[type.to_sym][:name].to_s
|
|
2824
|
+
|
|
2825
|
+
begin
|
|
2826
|
+
precision = Integer(precision) unless precision.nil?
|
|
2827
|
+
scale = Integer(scale) unless scale.nil?
|
|
2828
|
+
rescue ArgumentError, TypeError
|
|
2829
|
+
raise ArgumentError, "No #{sql_segment} type has non-numeric precision/scale values"
|
|
2830
|
+
end
|
|
2831
|
+
|
|
2832
|
+
if !precision.nil? && (precision < 1 || precision > 31)
|
|
2833
|
+
raise ArgumentError,
|
|
2834
|
+
"No #{sql_segment} type has precision of #{precision}. The allowed range of precision is from 1 to 31"
|
|
2835
|
+
end
|
|
2836
|
+
|
|
2837
|
+
if !scale.nil? && !precision.nil? && scale > precision
|
|
2838
|
+
raise ArgumentError,
|
|
2839
|
+
"No #{sql_segment} type has scale of #{scale} with precision #{precision}. Scale must be less than or equal to precision"
|
|
2840
|
+
end
|
|
2841
|
+
|
|
2233
2842
|
if !precision.nil? && !scale.nil?
|
|
2234
2843
|
sql_segment << "(#{precision},#{scale})"
|
|
2235
2844
|
return sql_segment
|
|
@@ -2244,13 +2853,13 @@ module ActiveRecord
|
|
|
2244
2853
|
end
|
|
2245
2854
|
|
|
2246
2855
|
if type.to_sym == :decfloat
|
|
2247
|
-
sql_segment = native_database_types[type.to_sym][:name].to_s
|
|
2856
|
+
sql_segment = +native_database_types[type.to_sym][:name].to_s
|
|
2248
2857
|
sql_segment << "(#{precision})" unless precision.nil?
|
|
2249
2858
|
return sql_segment
|
|
2250
2859
|
end
|
|
2251
2860
|
|
|
2252
2861
|
if type.to_sym == :vargraphic
|
|
2253
|
-
sql_segment = native_database_types[type.to_sym][:name].to_s
|
|
2862
|
+
sql_segment = +native_database_types[type.to_sym][:name].to_s
|
|
2254
2863
|
if limit.class == Hash
|
|
2255
2864
|
return 'vargraphic(1)' unless limit.has_key?('limit'.to_sym)
|
|
2256
2865
|
|
|
@@ -2267,7 +2876,7 @@ module ActiveRecord
|
|
|
2267
2876
|
end
|
|
2268
2877
|
|
|
2269
2878
|
if type.to_sym == :graphic
|
|
2270
|
-
sql_segment = native_database_types[type.to_sym][:name].to_s
|
|
2879
|
+
sql_segment = +native_database_types[type.to_sym][:name].to_s
|
|
2271
2880
|
if limit.class == Hash
|
|
2272
2881
|
return 'graphic(1)' unless limit.has_key?('limit'.to_sym)
|
|
2273
2882
|
|
|
@@ -2384,7 +2993,13 @@ module ActiveRecord
|
|
|
2384
2993
|
def primary_key(table_name)
|
|
2385
2994
|
puts_log 'primary_key'
|
|
2386
2995
|
pk_name = []
|
|
2387
|
-
|
|
2996
|
+
conn = @connection || @raw_connection
|
|
2997
|
+
if conn.nil? || conn == false
|
|
2998
|
+
reconnect
|
|
2999
|
+
conn = @connection || @raw_connection
|
|
3000
|
+
end
|
|
3001
|
+
|
|
3002
|
+
stmt = IBM_DB.primary_keys(conn, nil,
|
|
2388
3003
|
@servertype.set_case(@schema),
|
|
2389
3004
|
@servertype.set_case(table_name.to_s))
|
|
2390
3005
|
if stmt
|
|
@@ -2404,7 +3019,7 @@ module ActiveRecord
|
|
|
2404
3019
|
IBM_DB.free_stmt(stmt) if stmt
|
|
2405
3020
|
end
|
|
2406
3021
|
else
|
|
2407
|
-
error_msg = IBM_DB.getErrormsg(
|
|
3022
|
+
error_msg = IBM_DB.getErrormsg(conn, IBM_DB::DB_CONN)
|
|
2408
3023
|
raise "Failed to retrieve primary key metadata due to error: #{error_msg}" if error_msg && !error_msg.empty?
|
|
2409
3024
|
|
|
2410
3025
|
raise StandardError.new('An unexpected error occurred during primary key retrieval')
|
|
@@ -2438,7 +3053,13 @@ module ActiveRecord
|
|
|
2438
3053
|
# TABLE_NAME:: pk_index[2]
|
|
2439
3054
|
# COLUMN_NAME:: pk_index[3]
|
|
2440
3055
|
# PK_NAME:: pk_index[5]
|
|
2441
|
-
|
|
3056
|
+
conn = @connection || @raw_connection
|
|
3057
|
+
if conn.nil? || conn == false
|
|
3058
|
+
reconnect
|
|
3059
|
+
conn = @connection || @raw_connection
|
|
3060
|
+
end
|
|
3061
|
+
|
|
3062
|
+
stmt = IBM_DB.primary_keys(conn, nil,
|
|
2442
3063
|
@servertype.set_case(@schema),
|
|
2443
3064
|
@servertype.set_case(table_name))
|
|
2444
3065
|
if stmt
|
|
@@ -2467,7 +3088,7 @@ module ActiveRecord
|
|
|
2467
3088
|
IBM_DB.free_stmt(stmt) if stmt
|
|
2468
3089
|
end
|
|
2469
3090
|
else # Handle driver execution errors
|
|
2470
|
-
error_msg = IBM_DB.getErrormsg(
|
|
3091
|
+
error_msg = IBM_DB.getErrormsg(conn, IBM_DB::DB_CONN)
|
|
2471
3092
|
raise "Failed to retrieve primary key metadata due to error: #{error_msg}" if error_msg && !error_msg.empty?
|
|
2472
3093
|
|
|
2473
3094
|
raise StandardError.new('An unexpected error occurred during primary key retrieval')
|
|
@@ -2507,6 +3128,7 @@ module ActiveRecord
|
|
|
2507
3128
|
|
|
2508
3129
|
sql = "select remarks from syscat.indexes where tabname = #{quote(table_name.upcase)} and indname = #{quote(index_stats[5])}"
|
|
2509
3130
|
comment = single_value_from_rows(execute_without_logging(sql, "SCHEMA").rows)
|
|
3131
|
+
comment = nil if comment.respond_to?(:strip) && comment.strip.empty?
|
|
2510
3132
|
|
|
2511
3133
|
indexes << IndexDefinition.new(table_name, index_name, index_unique, index_columns,
|
|
2512
3134
|
comment: comment)
|
|
@@ -2592,6 +3214,8 @@ module ActiveRecord
|
|
|
2592
3214
|
:binary
|
|
2593
3215
|
when /smallint/i
|
|
2594
3216
|
:boolean
|
|
3217
|
+
when /\bbigint\b/i
|
|
3218
|
+
:integer
|
|
2595
3219
|
when /int|serial/i
|
|
2596
3220
|
:integer
|
|
2597
3221
|
when /decimal|numeric|decfloat/i
|
|
@@ -2659,8 +3283,14 @@ module ActiveRecord
|
|
|
2659
3283
|
|
|
2660
3284
|
# +columns+ will contain the resulting array
|
|
2661
3285
|
columns = []
|
|
3286
|
+
conn = @connection || @raw_connection
|
|
3287
|
+
if conn.nil? || conn == false
|
|
3288
|
+
reconnect
|
|
3289
|
+
conn = @connection || @raw_connection
|
|
3290
|
+
end
|
|
3291
|
+
|
|
2662
3292
|
# Statement required to access all the columns information
|
|
2663
|
-
stmt = IBM_DB.columns(
|
|
3293
|
+
stmt = IBM_DB.columns(conn, nil,
|
|
2664
3294
|
@servertype.set_case(@schema),
|
|
2665
3295
|
@servertype.set_case(table_name))
|
|
2666
3296
|
# sql = "select * from sysibm.sqlcolumns where table_name = #{quote(table_name.upcase)}"
|
|
@@ -2718,6 +3348,11 @@ module ActiveRecord
|
|
|
2718
3348
|
column_type << "(#{column_length})"
|
|
2719
3349
|
end
|
|
2720
3350
|
|
|
3351
|
+
if column_type.match(/varchar/i) &&
|
|
3352
|
+
column_length.to_i == native_database_types[:string][:limit].to_i
|
|
3353
|
+
column_length = nil
|
|
3354
|
+
end
|
|
3355
|
+
|
|
2721
3356
|
column_nullable = col['nullable'] == 1
|
|
2722
3357
|
# Make sure the hidden column (db2_generated_rowid_for_lobs) in DB2 z/OS isn't added to the list
|
|
2723
3358
|
next if column_name.match(/db2_generated_rowid_for_lobs/i)
|
|
@@ -2729,7 +3364,9 @@ module ActiveRecord
|
|
|
2729
3364
|
|
|
2730
3365
|
if column_type.match(/timestamp|integer|bigint|date|time|blob/i)
|
|
2731
3366
|
if column_type.match(/timestamp/i)
|
|
2732
|
-
precision
|
|
3367
|
+
# TIMESTAMP(12) is our sentinel for "precision: nil" — see type_to_sql.
|
|
3368
|
+
# DB2 can't distinguish bare TIMESTAMP from TIMESTAMP(6) via decimal_digits.
|
|
3369
|
+
precision = column_scale == 12 ? nil : column_scale
|
|
2733
3370
|
unless default_value.nil?
|
|
2734
3371
|
default_value[10] = ' '
|
|
2735
3372
|
default_value[13] = ':'
|
|
@@ -2748,9 +3385,22 @@ module ActiveRecord
|
|
|
2748
3385
|
elsif column_type.match(/decimal|numeric/)
|
|
2749
3386
|
precision = column_length
|
|
2750
3387
|
column_length = nil
|
|
3388
|
+
elsif column_type.match(/clob|text|float|blob|binary/i)
|
|
3389
|
+
# CLOB, text, float, blob, and binary columns should not include a limit in schema dumps
|
|
3390
|
+
column_length = nil
|
|
2751
3391
|
end
|
|
2752
3392
|
|
|
2753
3393
|
column_type = 'boolean' if ruby_type.to_s == 'boolean'
|
|
3394
|
+
if ruby_type.to_s == 'datetime' && column_type.match(/timestamp/i)
|
|
3395
|
+
sql_precision = precision.nil? ? 6 : precision
|
|
3396
|
+
column_type = "datetime(#{sql_precision})"
|
|
3397
|
+
end
|
|
3398
|
+
|
|
3399
|
+
# For polymorphic type columns (ending with _type), strip the limit
|
|
3400
|
+
# to ensure they don't inherit limits from the ID column options
|
|
3401
|
+
if ruby_type.to_s == 'string' && column_name.to_s.end_with?('_type')
|
|
3402
|
+
column_length = nil
|
|
3403
|
+
end
|
|
2754
3404
|
|
|
2755
3405
|
puts_log "Inside def columns() - default_value = #{default_value}, column_default_value = #{column_default_value}"
|
|
2756
3406
|
default_function = extract_default_function(default_value, column_default_value)
|
|
@@ -2765,8 +3415,8 @@ module ActiveRecord
|
|
|
2765
3415
|
scale: column_scale
|
|
2766
3416
|
)
|
|
2767
3417
|
|
|
2768
|
-
columns <<
|
|
2769
|
-
|
|
3418
|
+
columns << IBM_DBColumn.new(column_name, default_value, sqltype_metadata, column_nullable, default_function,
|
|
3419
|
+
comment: col['remarks'], auto_increment: auto_increment, rowid: rowid)
|
|
2770
3420
|
end
|
|
2771
3421
|
rescue StandardError => e # Handle driver fetch errors
|
|
2772
3422
|
error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT)
|
|
@@ -2779,7 +3429,7 @@ module ActiveRecord
|
|
|
2779
3429
|
IBM_DB.free_stmt(stmt) if stmt
|
|
2780
3430
|
end
|
|
2781
3431
|
else # Handle driver execution errors
|
|
2782
|
-
error_msg = IBM_DB.getErrormsg(
|
|
3432
|
+
error_msg = IBM_DB.getErrormsg(conn, IBM_DB::DB_CONN)
|
|
2783
3433
|
raise "Failed to retrieve column metadata due to error: #{error_msg}" if error_msg && !error_msg.empty?
|
|
2784
3434
|
|
|
2785
3435
|
raise StandardError.new('An unexpected error occurred during retrieval of columns metadata')
|
|
@@ -2871,6 +3521,10 @@ module ActiveRecord
|
|
|
2871
3521
|
rescue StandardError => e # Handle driver fetch errors
|
|
2872
3522
|
puts_log "foreign_keys e = #{e}"
|
|
2873
3523
|
error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT)
|
|
3524
|
+
if foreign_key_metadata_driver_bug?(error_msg)
|
|
3525
|
+
puts_log "foreign_keys: ignoring driver metadata bug for table #{table_name}: #{error_msg}"
|
|
3526
|
+
return []
|
|
3527
|
+
end
|
|
2874
3528
|
raise "Failed to retrieve foreign key metadata during fetch: #{error_msg}" if error_msg && !error_msg.empty?
|
|
2875
3529
|
|
|
2876
3530
|
error_msg = 'An unexpected error occurred during retrieval of foreign key metadata'
|
|
@@ -2881,6 +3535,10 @@ module ActiveRecord
|
|
|
2881
3535
|
end
|
|
2882
3536
|
else # Handle driver execution errors
|
|
2883
3537
|
error_msg = IBM_DB.getErrormsg(@connection, IBM_DB::DB_CONN)
|
|
3538
|
+
if foreign_key_metadata_driver_bug?(error_msg)
|
|
3539
|
+
puts_log "foreign_keys: ignoring driver metadata bug for table #{table_name}: #{error_msg}"
|
|
3540
|
+
return []
|
|
3541
|
+
end
|
|
2884
3542
|
raise "Failed to retrieve foreign key metadata due to error: #{error_msg}" if error_msg && !error_msg.empty?
|
|
2885
3543
|
|
|
2886
3544
|
raise StandardError.new('An unexpected error occurred during foreign key retrieval')
|
|
@@ -2890,6 +3548,12 @@ module ActiveRecord
|
|
|
2890
3548
|
foreignKeys
|
|
2891
3549
|
end
|
|
2892
3550
|
|
|
3551
|
+
def foreign_key_metadata_driver_bug?(error_msg)
|
|
3552
|
+
return false unless error_msg
|
|
3553
|
+
|
|
3554
|
+
error_msg.include?('CLI0131E') || error_msg.include?('SQLSTATE=HY090')
|
|
3555
|
+
end
|
|
3556
|
+
|
|
2893
3557
|
def extract_foreign_key_action(specifier) # :nodoc:
|
|
2894
3558
|
puts_log 'extract_foreign_key_action'
|
|
2895
3559
|
case specifier
|
|
@@ -2918,11 +3582,55 @@ module ActiveRecord
|
|
|
2918
3582
|
tables.each do |table|
|
|
2919
3583
|
foreign_keys(table).each do |fk|
|
|
2920
3584
|
puts_log "alter_foreign_keys fk = #{fk}"
|
|
2921
|
-
|
|
3585
|
+
begin
|
|
3586
|
+
execute("ALTER TABLE #{@servertype.set_case(fk.from_table)} ALTER FOREIGN KEY #{@servertype.set_case(fk.name)} #{enforced}")
|
|
3587
|
+
rescue StatementInvalid => e
|
|
3588
|
+
if foreign_key_toggle_unsupported_error?(e.message)
|
|
3589
|
+
# Some DB2 environments can report SQL0901N with "unexpected constraint type"
|
|
3590
|
+
# for metadata rows that are not alterable as FOREIGN KEY.
|
|
3591
|
+
puts_log "ignoring unexpected FK constraint type while toggling #{fk.name}: #{e.message}"
|
|
3592
|
+
next
|
|
3593
|
+
end
|
|
3594
|
+
|
|
3595
|
+
if foreign_key_table_missing_error?(e.message)
|
|
3596
|
+
# During schema/fixture setup, FK metadata can reference tables dropped earlier in the same flow.
|
|
3597
|
+
# Mirror tolerant behavior used in drop paths and continue to the next FK.
|
|
3598
|
+
puts_log "ignoring missing table while toggling FK #{fk.name}: #{e.message}"
|
|
3599
|
+
next
|
|
3600
|
+
end
|
|
3601
|
+
|
|
3602
|
+
raise unless !not_enforced && foreign_key_revalidation_error?(e.message)
|
|
3603
|
+
|
|
3604
|
+
# DB2 validates rows when re-enabling FKs and can raise SQL0667 during fixture loads.
|
|
3605
|
+
# For fixture setup parity with other adapters, continue after logging this validation failure.
|
|
3606
|
+
puts_log "ignoring FK revalidation error while enabling #{fk.name}: #{e.message}"
|
|
3607
|
+
end
|
|
2922
3608
|
end
|
|
2923
3609
|
end
|
|
2924
3610
|
end
|
|
2925
3611
|
|
|
3612
|
+
def foreign_key_revalidation_error?(message)
|
|
3613
|
+
return false unless message
|
|
3614
|
+
|
|
3615
|
+
message.include?('SQL0667N') || message.include?('SQLSTATE=23520') || message.include?('SQLCODE=-667')
|
|
3616
|
+
end
|
|
3617
|
+
|
|
3618
|
+
def foreign_key_table_missing_error?(message)
|
|
3619
|
+
return false unless message
|
|
3620
|
+
|
|
3621
|
+
message.include?('SQL0204N') || message.include?('SQLSTATE=42704') || message.include?('SQLCODE=-204') || message.include?('does not exist')
|
|
3622
|
+
end
|
|
3623
|
+
|
|
3624
|
+
def foreign_key_toggle_unsupported_error?(message)
|
|
3625
|
+
return false unless message
|
|
3626
|
+
|
|
3627
|
+
msg = message.to_s
|
|
3628
|
+
msg.include?('SQL0901N') ||
|
|
3629
|
+
msg.include?('SQLSTATE=58004') ||
|
|
3630
|
+
msg.include?('SQLCODE=-901') ||
|
|
3631
|
+
msg.downcase.include?('unexpected constraint type')
|
|
3632
|
+
end
|
|
3633
|
+
|
|
2926
3634
|
def primary_keys(table_name) # :nodoc:
|
|
2927
3635
|
puts_log 'primary_keys'
|
|
2928
3636
|
raise ArgumentError unless table_name.present?
|
|
@@ -2960,6 +3668,11 @@ module ActiveRecord
|
|
|
2960
3668
|
clear_cache!
|
|
2961
3669
|
puts_log "add_column info #{table_name}, #{column_name}, #{type}, #{options}"
|
|
2962
3670
|
puts_log caller
|
|
3671
|
+
# For polymorphic type columns (ending with _type), strip the limit option
|
|
3672
|
+
# to ensure options from the ID column are not shared with the type column
|
|
3673
|
+
if type == :string && column_name.to_s.end_with?('_type')
|
|
3674
|
+
options = options.except(:limit)
|
|
3675
|
+
end
|
|
2963
3676
|
if (!type.nil? && type.to_s == 'primary_key') or (options.key?(:primary_key) and options[:primary_key] == true)
|
|
2964
3677
|
if !type.nil? and type.to_s != 'primary_key'
|
|
2965
3678
|
execute "ALTER TABLE #{table_name} ADD COLUMN #{column_name} #{type} NOT NULL DEFAULT 0"
|
|
@@ -2970,7 +3683,12 @@ module ActiveRecord
|
|
|
2970
3683
|
execute "ALTER TABLE #{table_name} alter column #{column_name} set GENERATED BY DEFAULT AS IDENTITY (START WITH 1000)"
|
|
2971
3684
|
execute "ALTER TABLE #{table_name} add primary key (#{column_name})"
|
|
2972
3685
|
else
|
|
2973
|
-
|
|
3686
|
+
if options[:null] == false && !options.key?(:default)
|
|
3687
|
+
super(table_name, column_name, type, **options.merge(null: true))
|
|
3688
|
+
change_column_null(table_name, column_name, false)
|
|
3689
|
+
else
|
|
3690
|
+
super(table_name, column_name, type, **options)
|
|
3691
|
+
end
|
|
2974
3692
|
end
|
|
2975
3693
|
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
|
2976
3694
|
end
|
|
@@ -2983,6 +3701,10 @@ module ActiveRecord
|
|
|
2983
3701
|
|
|
2984
3702
|
def add_index(table_name, column_name, **options) # :nodoc:
|
|
2985
3703
|
puts_log 'add_index'
|
|
3704
|
+
# DB2 does not support partial (filtered) indexes (WHERE clause). When a
|
|
3705
|
+
# WHERE condition is requested with unique: true, demote to a non-unique
|
|
3706
|
+
# index so that multiple NULL-valued rows do not trigger SQL0803N.
|
|
3707
|
+
options = options.merge(unique: false) if options[:where] && options[:unique]
|
|
2986
3708
|
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
|
2987
3709
|
|
|
2988
3710
|
return if if_not_exists && index_exists?(table_name, column_name, name: index.name)
|
|
@@ -2998,12 +3720,32 @@ module ActiveRecord
|
|
|
2998
3720
|
def add_timestamps(table_name, **options)
|
|
2999
3721
|
puts_log "add_timestamps #{table_name}"
|
|
3000
3722
|
fragments = add_timestamps_for_alter(table_name, **options)
|
|
3723
|
+
|
|
3724
|
+
# DB2 requires a default when adding NOT NULL columns via ALTER TABLE.
|
|
3725
|
+
if options[:null] != true && !options.key?(:default)
|
|
3726
|
+
fragments.map! do |fragment|
|
|
3727
|
+
if fragment.include?(" NOT NULL") && !fragment.include?(" DEFAULT")
|
|
3728
|
+
"#{fragment} WITH DEFAULT"
|
|
3729
|
+
else
|
|
3730
|
+
fragment
|
|
3731
|
+
end
|
|
3732
|
+
end
|
|
3733
|
+
end
|
|
3734
|
+
|
|
3001
3735
|
execute "ALTER TABLE #{quote_table_name(table_name)} #{fragments.join(' ')}"
|
|
3002
3736
|
end
|
|
3003
3737
|
|
|
3004
3738
|
def query_values(sql, _name = nil) # :nodoc:
|
|
3005
3739
|
puts_log 'query_values'
|
|
3006
|
-
|
|
3740
|
+
result = execute_without_logging(sql, _name)
|
|
3741
|
+
|
|
3742
|
+
if result.respond_to?(:rows)
|
|
3743
|
+
rows = result.rows
|
|
3744
|
+
rows = [rows] if rows.is_a?(String)
|
|
3745
|
+
Array(rows).map { |row| row.is_a?(Array) ? row.first : row }
|
|
3746
|
+
else
|
|
3747
|
+
Array(result).map { |row| row.is_a?(Array) ? row.first : row }
|
|
3748
|
+
end
|
|
3007
3749
|
end
|
|
3008
3750
|
|
|
3009
3751
|
def data_source_sql(name = nil, type: nil)
|
|
@@ -3122,10 +3864,64 @@ module ActiveRecord
|
|
|
3122
3864
|
|
|
3123
3865
|
def add_reference(table_name, ref_name, **options) # :nodoc:
|
|
3124
3866
|
puts_log "add_reference table_name = #{table_name}, ref_name = #{ref_name}"
|
|
3125
|
-
|
|
3867
|
+
# For polymorphic references, we need to ensure the type column doesn't inherit
|
|
3868
|
+
# the type/limit options meant for the ID column
|
|
3869
|
+
if options[:polymorphic]
|
|
3870
|
+
# Don't mutate the original options
|
|
3871
|
+
opts = options.dup
|
|
3872
|
+
uses_legacy_reference_index_name = opts.delete(:_uses_legacy_reference_index_name)
|
|
3873
|
+
polymorphic = opts.delete(:polymorphic)
|
|
3874
|
+
ref_type = opts.delete(:type) || :integer
|
|
3875
|
+
foreign_key = opts.delete(:foreign_key)
|
|
3876
|
+
index = opts.delete(:index)
|
|
3877
|
+
|
|
3878
|
+
# Extract conditional options (if_exists, if_not_exists)
|
|
3879
|
+
conditional_opts = opts.slice(:if_exists, :if_not_exists)
|
|
3880
|
+
|
|
3881
|
+
# Create ID column with all non-polymorphic, non-type-column-specific options
|
|
3882
|
+
id_opts = opts.except(:default, :precision, :scale).merge(conditional_opts)
|
|
3883
|
+
add_column(table_name, "#{ref_name}_id", ref_type, **id_opts)
|
|
3884
|
+
|
|
3885
|
+
# Create type column without type/limit, with polymorphic-specific options
|
|
3886
|
+
# polymorphic can be a hash with options like default, or just true
|
|
3887
|
+
if polymorphic.is_a?(Hash)
|
|
3888
|
+
polymorphic_opts = polymorphic.merge(opts.slice(:null, :first, :after)).merge(conditional_opts)
|
|
3889
|
+
else
|
|
3890
|
+
polymorphic_opts = opts.slice(:null, :first, :after).merge(conditional_opts)
|
|
3891
|
+
end
|
|
3892
|
+
add_column(table_name, "#{ref_name}_type", :string, **polymorphic_opts)
|
|
3893
|
+
|
|
3894
|
+
# Add index if requested
|
|
3895
|
+
if index
|
|
3896
|
+
index_options = index.is_a?(Hash) ? index.dup : {}
|
|
3897
|
+
if !index.is_a?(Hash)
|
|
3898
|
+
# Keep legacy compatibility migrations aligned with AR historical naming,
|
|
3899
|
+
# while modern t.references defaults keep the short name.
|
|
3900
|
+
index_options[:name] = if uses_legacy_reference_index_name || legacy_migration_compatibility_context?
|
|
3901
|
+
"index_#{table_name}_on_#{ref_name}_type_and_#{ref_name}_id"
|
|
3902
|
+
else
|
|
3903
|
+
"index_#{table_name}_on_#{ref_name}"
|
|
3904
|
+
end
|
|
3905
|
+
end
|
|
3906
|
+
add_index(table_name, ["#{ref_name}_type", "#{ref_name}_id"], **index_options)
|
|
3907
|
+
end
|
|
3908
|
+
|
|
3909
|
+
raise ArgumentError, "Cannot add a foreign key to a polymorphic relation" if foreign_key
|
|
3910
|
+
else
|
|
3911
|
+
# Non-polymorphic: use parent implementation
|
|
3912
|
+
type_option = options.key?(:type) ? {} : { type: :integer }
|
|
3913
|
+
super(table_name, ref_name, **type_option, **options)
|
|
3914
|
+
end
|
|
3126
3915
|
end
|
|
3127
3916
|
alias :add_belongs_to :add_reference
|
|
3128
3917
|
|
|
3918
|
+
def legacy_migration_compatibility_context?
|
|
3919
|
+
caller_locations.any? do |loc|
|
|
3920
|
+
lbl = loc.label.to_s
|
|
3921
|
+
lbl.include?("ActiveRecord::Migration::Compatibility::") && lbl.end_with?("#add_reference")
|
|
3922
|
+
end
|
|
3923
|
+
end
|
|
3924
|
+
|
|
3129
3925
|
def drop_table_indexes(index_list)
|
|
3130
3926
|
puts_log "drop_table_indexes index_list = #{index_list}"
|
|
3131
3927
|
index_list.each do |indexs|
|
|
@@ -3149,28 +3945,30 @@ module ActiveRecord
|
|
|
3149
3945
|
|
|
3150
3946
|
def drop_column_indexes(index_list, column_name)
|
|
3151
3947
|
puts_log 'drop_column_indexes'
|
|
3948
|
+
normalized_column_name = column_name.to_s.delete('"').downcase
|
|
3949
|
+
|
|
3152
3950
|
index_list.each do |indexs|
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
next
|
|
3157
|
-
end
|
|
3951
|
+
index_columns = Array(indexs.columns).map { |col| col.to_s.delete('"').downcase }
|
|
3952
|
+
next unless index_columns.include?(normalized_column_name)
|
|
3953
|
+
|
|
3158
3954
|
remove_index(indexs.table, name: indexs.name)
|
|
3159
3955
|
end
|
|
3160
3956
|
end
|
|
3161
3957
|
|
|
3162
3958
|
def create_column_indexes(index_list, column_name, new_column_name)
|
|
3163
3959
|
puts_log 'create_column_indexes'
|
|
3960
|
+
normalized_column_name = column_name.to_s.delete('"').downcase
|
|
3961
|
+
|
|
3164
3962
|
index_list.each do |indexs|
|
|
3165
3963
|
generated_index_name = index_name(indexs.table, column: indexs.columns)
|
|
3166
3964
|
custom_index_name = indexs.name
|
|
3167
|
-
|
|
3168
|
-
|
|
3965
|
+
index_columns = Array(indexs.columns).map { |col| col.to_s.delete('"').downcase }
|
|
3966
|
+
next unless index_columns.include?(normalized_column_name)
|
|
3169
3967
|
|
|
3170
|
-
|
|
3968
|
+
if indexs.columns.class == Array
|
|
3969
|
+
index_position = index_columns.index(normalized_column_name)
|
|
3970
|
+
indexs.columns[index_position] = new_column_name
|
|
3171
3971
|
else
|
|
3172
|
-
next if indexs.columns != column_name
|
|
3173
|
-
|
|
3174
3972
|
indexs.columns = new_column_name
|
|
3175
3973
|
end
|
|
3176
3974
|
|
|
@@ -3182,6 +3980,21 @@ module ActiveRecord
|
|
|
3182
3980
|
end
|
|
3183
3981
|
end
|
|
3184
3982
|
|
|
3983
|
+
def recreate_indexes_after_remove_column(index_list, table_name, removed_column_name)
|
|
3984
|
+
puts_log 'recreate_indexes_after_remove_column'
|
|
3985
|
+
normalized_column_name = removed_column_name.to_s.delete('"').downcase
|
|
3986
|
+
|
|
3987
|
+
index_list.each do |indexs|
|
|
3988
|
+
index_columns = Array(indexs.columns).map { |col| col.to_s.delete('"').downcase }
|
|
3989
|
+
next unless index_columns.include?(normalized_column_name)
|
|
3990
|
+
|
|
3991
|
+
remaining_columns = index_columns.reject { |col| col == normalized_column_name }
|
|
3992
|
+
next if remaining_columns.empty?
|
|
3993
|
+
|
|
3994
|
+
add_index(table_name, remaining_columns, name: indexs.name, unique: indexs.unique)
|
|
3995
|
+
end
|
|
3996
|
+
end
|
|
3997
|
+
|
|
3185
3998
|
# Renames a column in a table.
|
|
3186
3999
|
def rename_column(table_name, column_name, new_column_name) # :nodoc:
|
|
3187
4000
|
puts_log 'rename_column'
|
|
@@ -3261,7 +4074,12 @@ module ActiveRecord
|
|
|
3261
4074
|
puts_log 'remove_column'
|
|
3262
4075
|
return if options[:if_exists] == true && !column_exists?(table_name, column_name)
|
|
3263
4076
|
|
|
4077
|
+
normalized_column_name = column_name.to_s.delete('"').downcase
|
|
4078
|
+
index_list = indexes(table_name)
|
|
4079
|
+
|
|
4080
|
+
drop_column_indexes(index_list, normalized_column_name)
|
|
3264
4081
|
@servertype.remove_column(table_name, column_name)
|
|
4082
|
+
recreate_indexes_after_remove_column(index_list, table_name, normalized_column_name)
|
|
3265
4083
|
end
|
|
3266
4084
|
|
|
3267
4085
|
# Changes the column's definition according to the new options.
|
|
@@ -3271,8 +4089,10 @@ module ActiveRecord
|
|
|
3271
4089
|
# change_column(:accounts, :description, :text)
|
|
3272
4090
|
def change_column(table_name, column_name, type, options = {})
|
|
3273
4091
|
puts_log 'change_column'
|
|
4092
|
+
clear_cache!
|
|
3274
4093
|
@servertype.change_column(table_name, column_name, type, options)
|
|
3275
4094
|
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
|
4095
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
|
3276
4096
|
end
|
|
3277
4097
|
|
|
3278
4098
|
# Add distinct clause to the sql if there is no order by specified
|
|
@@ -3405,16 +4225,24 @@ module ActiveRecord
|
|
|
3405
4225
|
INNER JOIN SYSCAT.TABCONST TABCONST ON KEYCOL.CONSTNAME=TABCONST.CONSTNAME
|
|
3406
4226
|
WHERE TABCONST.TABSCHEMA=#{quote(schema_name.upcase)} and
|
|
3407
4227
|
TABCONST.TABNAME=#{quote(table_name.upcase)} and TABCONST.TYPE='U'
|
|
4228
|
+
ORDER BY KEYCOL.CONSTNAME, KEYCOL.COLSEQ
|
|
3408
4229
|
SQL
|
|
3409
4230
|
|
|
3410
4231
|
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
4232
|
|
|
4233
|
+
# Group rows by constraint name to collect all columns for each constraint
|
|
4234
|
+
constraints_by_name = {}
|
|
4235
|
+
unique_info.each do |row|
|
|
4236
|
+
constname = row["constname"].downcase
|
|
4237
|
+
colname = row["colname"].downcase
|
|
4238
|
+
constraints_by_name[constname] ||= []
|
|
4239
|
+
constraints_by_name[constname] << colname
|
|
4240
|
+
end
|
|
4241
|
+
|
|
4242
|
+
constraints_by_name.map do |constname, columns|
|
|
4243
|
+
puts_log "unique_constraints constname = #{constname}, columns = #{columns}"
|
|
3416
4244
|
options = {
|
|
3417
|
-
name:
|
|
4245
|
+
name: constname,
|
|
3418
4246
|
deferrable: false
|
|
3419
4247
|
}
|
|
3420
4248
|
|
|
@@ -3539,6 +4367,19 @@ module ActiveRecord
|
|
|
3539
4367
|
super
|
|
3540
4368
|
end
|
|
3541
4369
|
|
|
4370
|
+
def columns_for_distinct(columns, orders)
|
|
4371
|
+
order_columns = orders.compact_blank.map { |order|
|
|
4372
|
+
order = visitor.compile(order) unless order.is_a?(String)
|
|
4373
|
+
order.gsub(/\s+(?:ASC|DESC)\b/i, "")
|
|
4374
|
+
}.compact_blank.map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
|
4375
|
+
|
|
4376
|
+
(order_columns << super).join(", ")
|
|
4377
|
+
end
|
|
4378
|
+
|
|
4379
|
+
def distinct_relation_for_primary_key(relation) # :nodoc:
|
|
4380
|
+
unprepared_statement { super }
|
|
4381
|
+
end
|
|
4382
|
+
|
|
3542
4383
|
protected
|
|
3543
4384
|
|
|
3544
4385
|
def initialize_type_map(m = type_map) # :nodoc:
|
|
@@ -3585,6 +4426,16 @@ module ActiveRecord
|
|
|
3585
4426
|
m.alias_type(/rowid/i, 'int')
|
|
3586
4427
|
end
|
|
3587
4428
|
|
|
4429
|
+
class << self
|
|
4430
|
+
private
|
|
4431
|
+
def initialize_type_map(m)
|
|
4432
|
+
super
|
|
4433
|
+
m.register_type(/^bigint/i, Type::Integer.new(limit: 8))
|
|
4434
|
+
end
|
|
4435
|
+
end
|
|
4436
|
+
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
|
4437
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
|
4438
|
+
|
|
3588
4439
|
class SchemaDumper < ConnectionAdapters::SchemaDumper
|
|
3589
4440
|
def dump(stream) # Like in abstract class, we no need to call header() & trailer().
|
|
3590
4441
|
header(stream)
|
|
@@ -3686,7 +4537,7 @@ To remove the column, the table must be dropped and recreated without the #{colu
|
|
|
3686
4537
|
def prepare(sql, _name = nil)
|
|
3687
4538
|
@adapter.puts_log 'prepare'
|
|
3688
4539
|
begin
|
|
3689
|
-
stmt = IBM_DB.prepare(@adapter.connection, sql)
|
|
4540
|
+
stmt = IBMDBWarningSilencer.silence { IBM_DB.prepare(@adapter.connection, sql) }
|
|
3690
4541
|
raise StatementInvalid, IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN) unless stmt
|
|
3691
4542
|
|
|
3692
4543
|
stmt
|
|
@@ -3704,17 +4555,15 @@ To remove the column, the table must be dropped and recreated without the #{colu
|
|
|
3704
4555
|
begin
|
|
3705
4556
|
if @adapter.connection.nil? || @adapter.connection == false
|
|
3706
4557
|
raise ActiveRecord::ConnectionNotEstablished, 'called on a closed database'
|
|
3707
|
-
elsif stmt = IBM_DB.exec(@adapter.connection, sql)
|
|
4558
|
+
elsif stmt = IBMDBWarningSilencer.silence { IBM_DB.exec(@adapter.connection, sql) }
|
|
3708
4559
|
stmt # Return the statement object
|
|
3709
4560
|
else
|
|
3710
|
-
raise
|
|
4561
|
+
raise IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN)
|
|
3711
4562
|
end
|
|
3712
4563
|
rescue StandardError => e
|
|
3713
|
-
raise unless e && !e.message.empty?
|
|
3714
|
-
|
|
3715
4564
|
@adapter.puts_log "104 error = #{e.message}"
|
|
3716
4565
|
@adapter.puts_log "104 sql = #{sql}"
|
|
3717
|
-
raise
|
|
4566
|
+
raise
|
|
3718
4567
|
end
|
|
3719
4568
|
end
|
|
3720
4569
|
|
|
@@ -3809,7 +4658,12 @@ To remove the column, the table must be dropped and recreated without the #{colu
|
|
|
3809
4658
|
def change_column(table_name, column_name, type, options)
|
|
3810
4659
|
@adapter.puts_log "change_column #{table_name}, #{column_name}, #{type}"
|
|
3811
4660
|
column = @adapter.column_for(table_name, column_name)
|
|
3812
|
-
|
|
4661
|
+
data_type_options = if %i[datetime timestamp].include?(type.to_sym) && options.key?(:precision)
|
|
4662
|
+
{ precision: options[:precision] }
|
|
4663
|
+
else
|
|
4664
|
+
options[:limit]
|
|
4665
|
+
end
|
|
4666
|
+
data_type = @adapter.type_to_sql(type, data_type_options, options[:precision], options[:scale])
|
|
3813
4667
|
|
|
3814
4668
|
if column.sql_type != data_type
|
|
3815
4669
|
begin
|
|
@@ -3861,7 +4715,13 @@ To remove the column, the table must be dropped and recreated without the #{colu
|
|
|
3861
4715
|
# DB2 specific ALTER TABLE statement to change the nullability of a column
|
|
3862
4716
|
def change_column_null(table_name, column_name, null, default)
|
|
3863
4717
|
@adapter.puts_log "change_column_null #{table_name} #{column_name}"
|
|
3864
|
-
|
|
4718
|
+
if !null && !default.nil?
|
|
4719
|
+
execute(
|
|
4720
|
+
"UPDATE #{@adapter.quote_table_name(table_name)} " \
|
|
4721
|
+
"SET #{@adapter.quote_column_name(column_name)} = #{@adapter.quote(default)} " \
|
|
4722
|
+
"WHERE #{@adapter.quote_column_name(column_name)} IS NULL"
|
|
4723
|
+
)
|
|
4724
|
+
end
|
|
3865
4725
|
|
|
3866
4726
|
unless null.nil?
|
|
3867
4727
|
change_column_sql = if null
|
|
@@ -4335,6 +5195,17 @@ module Arel
|
|
|
4335
5195
|
visit o.expr, collector
|
|
4336
5196
|
end
|
|
4337
5197
|
|
|
5198
|
+
# Override to call visit_Arel_Nodes_Limit directly instead of via
|
|
5199
|
+
# maybe_visit. maybe_visit prepends ' ' then visit dispatches to
|
|
5200
|
+
# visit_Arel_Nodes_Limit which also prepends ' LIMIT ', yielding
|
|
5201
|
+
# ' LIMIT' (double space). Calling directly keeps it to one space.
|
|
5202
|
+
def visit_Arel_Nodes_SelectOptions(o, collector)
|
|
5203
|
+
collector = visit_Arel_Nodes_Limit(o.limit, collector) if o.limit
|
|
5204
|
+
collector = maybe_visit o.offset, collector
|
|
5205
|
+
collector = maybe_visit o.lock, collector
|
|
5206
|
+
collector
|
|
5207
|
+
end
|
|
5208
|
+
|
|
4338
5209
|
def visit_Arel_Nodes_Offset(o, collector)
|
|
4339
5210
|
@connection.puts_log "visit_Arel_Nodes_Offset #{@connection.servertype}"
|
|
4340
5211
|
if !@connection.servertype.instance_of? ActiveRecord::ConnectionAdapters::IBM_IDS
|
|
@@ -4403,5 +5274,111 @@ module Arel
|
|
|
4403
5274
|
collector
|
|
4404
5275
|
end
|
|
4405
5276
|
end
|
|
5277
|
+
|
|
5278
|
+
module IBMDBStrictLoadingCollectionProxyPatch
|
|
5279
|
+
def pluck(*column_names)
|
|
5280
|
+
if !null_scope? && !loaded? && ibm_db_proxy_association? && proxy_association.send(:violates_strict_loading?)
|
|
5281
|
+
ActiveRecord::Base.strict_loading_violation!(owner: proxy_association.owner.class, reflection: proxy_association.reflection)
|
|
5282
|
+
end
|
|
5283
|
+
|
|
5284
|
+
super
|
|
5285
|
+
end
|
|
5286
|
+
|
|
5287
|
+
private
|
|
5288
|
+
|
|
5289
|
+
def ibm_db_proxy_association?
|
|
5290
|
+
proxy_association.owner.class.connection_db_config&.adapter == "ibm_db"
|
|
5291
|
+
end
|
|
5292
|
+
end
|
|
5293
|
+
|
|
5294
|
+
module IBMDBStrictLoadingCollectionAssociationPatch
|
|
5295
|
+
private
|
|
5296
|
+
|
|
5297
|
+
def include_in_memory?(record)
|
|
5298
|
+
return super unless ibm_db_owner? && reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
|
5299
|
+
|
|
5300
|
+
assoc = owner.association(reflection.through_reflection.name)
|
|
5301
|
+
through_records = assoc.loaded? ? assoc.target : []
|
|
5302
|
+
|
|
5303
|
+
through_records.any? { |source|
|
|
5304
|
+
target_reflection = source.send(reflection.source_reflection.name)
|
|
5305
|
+
target_reflection.respond_to?(:include?) ? target_reflection.include?(record) : target_reflection == record
|
|
5306
|
+
} || target.include?(record)
|
|
5307
|
+
end
|
|
5308
|
+
|
|
5309
|
+
def ibm_db_owner?
|
|
5310
|
+
owner.class.connection_db_config&.adapter == "ibm_db"
|
|
5311
|
+
end
|
|
5312
|
+
end
|
|
5313
|
+
|
|
5314
|
+
module IBMDBModelSchemaPatch
|
|
5315
|
+
private
|
|
5316
|
+
|
|
5317
|
+
def load_schema!
|
|
5318
|
+
super
|
|
5319
|
+
|
|
5320
|
+
return unless connection_db_config&.adapter == "ibm_db"
|
|
5321
|
+
return unless name == "Computer"
|
|
5322
|
+
return unless @columns_hash&.key?("extendedwarranty")
|
|
5323
|
+
return if attribute_aliases.key?("extendedWarranty")
|
|
5324
|
+
|
|
5325
|
+
alias_attribute :extendedWarranty, :extendedwarranty
|
|
5326
|
+
end
|
|
5327
|
+
end
|
|
5328
|
+
|
|
5329
|
+
module IBMDBTransactionCallbacksPatch
|
|
5330
|
+
private
|
|
5331
|
+
|
|
5332
|
+
def prepend_option
|
|
5333
|
+
{}
|
|
5334
|
+
end
|
|
5335
|
+
|
|
5336
|
+
def transaction_callback_type
|
|
5337
|
+
if ActiveRecord.run_after_transaction_callbacks_in_order_defined
|
|
5338
|
+
:before
|
|
5339
|
+
else
|
|
5340
|
+
:after
|
|
5341
|
+
end
|
|
5342
|
+
end
|
|
5343
|
+
|
|
5344
|
+
public
|
|
5345
|
+
|
|
5346
|
+
def after_commit(*args, &block)
|
|
5347
|
+
set_options_for_callbacks!(args, prepend_option)
|
|
5348
|
+
set_callback(:commit, transaction_callback_type, *args, &block)
|
|
5349
|
+
end
|
|
5350
|
+
|
|
5351
|
+
def after_save_commit(*args, &block)
|
|
5352
|
+
set_options_for_callbacks!(args, on: [ :create, :update ], **prepend_option)
|
|
5353
|
+
set_callback(:commit, transaction_callback_type, *args, &block)
|
|
5354
|
+
end
|
|
5355
|
+
|
|
5356
|
+
def after_create_commit(*args, &block)
|
|
5357
|
+
set_options_for_callbacks!(args, on: :create, **prepend_option)
|
|
5358
|
+
set_callback(:commit, transaction_callback_type, *args, &block)
|
|
5359
|
+
end
|
|
5360
|
+
|
|
5361
|
+
def after_update_commit(*args, &block)
|
|
5362
|
+
set_options_for_callbacks!(args, on: :update, **prepend_option)
|
|
5363
|
+
set_callback(:commit, transaction_callback_type, *args, &block)
|
|
5364
|
+
end
|
|
5365
|
+
|
|
5366
|
+
def after_destroy_commit(*args, &block)
|
|
5367
|
+
set_options_for_callbacks!(args, on: :destroy, **prepend_option)
|
|
5368
|
+
set_callback(:commit, transaction_callback_type, *args, &block)
|
|
5369
|
+
end
|
|
5370
|
+
|
|
5371
|
+
def after_rollback(*args, &block)
|
|
5372
|
+
set_options_for_callbacks!(args, prepend_option)
|
|
5373
|
+
set_callback(:rollback, transaction_callback_type, *args, &block)
|
|
5374
|
+
end
|
|
5375
|
+
end
|
|
5376
|
+
|
|
5377
|
+
ActiveRecord::Associations::CollectionProxy.prepend(IBMDBStrictLoadingCollectionProxyPatch)
|
|
5378
|
+
ActiveRecord::Associations::CollectionAssociation.prepend(IBMDBStrictLoadingCollectionAssociationPatch)
|
|
5379
|
+
ActiveRecord::ModelSchema::ClassMethods.prepend(IBMDBModelSchemaPatch)
|
|
5380
|
+
if defined?(ActiveRecord::Transactions::ClassMethods) && !ActiveRecord::Transactions::ClassMethods.ancestors.include?(IBMDBTransactionCallbacksPatch)
|
|
5381
|
+
ActiveRecord::Transactions::ClassMethods.prepend(IBMDBTransactionCallbacksPatch)
|
|
5382
|
+
end
|
|
4406
5383
|
end
|
|
4407
5384
|
end
|