activerecord 6.1.7 → 7.1.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/CHANGELOG.md +1516 -1019
- data/MIT-LICENSE +1 -1
- data/README.rdoc +17 -18
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +1 -11
- data/lib/active_record/associations/association.rb +50 -19
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +28 -9
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +40 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +35 -31
- data/lib/active_record/associations/collection_proxy.rb +30 -15
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +28 -18
- data/lib/active_record/associations/has_many_through_association.rb +12 -7
- data/lib/active_record/associations/has_one_association.rb +20 -10
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +26 -16
- data/lib/active_record/associations/preloader/association.rb +207 -52
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -14
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +9 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +423 -289
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -3
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/dirty.rb +61 -14
- data/lib/active_record/attribute_methods/primary_key.rb +78 -26
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +25 -10
- data/lib/active_record/attribute_methods/serialization.rb +194 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +10 -13
- data/lib/active_record/attribute_methods.rb +121 -40
- data/lib/active_record/attributes.rb +27 -38
- data/lib/active_record/autosave_association.rb +61 -30
- data/lib/active_record/base.rb +25 -2
- data/lib/active_record/callbacks.rb +18 -34
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -46
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +96 -590
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +171 -51
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +77 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +360 -136
- data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
- data/lib/active_record/connection_adapters/abstract_adapter.rb +622 -149
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +285 -156
- data/lib/active_record/connection_adapters/column.rb +13 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
- data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +104 -53
- data/lib/active_record/connection_adapters/pool_config.rb +20 -11
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +18 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -52
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +381 -69
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +492 -230
- data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +65 -53
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -102
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
- data/lib/active_record/connection_adapters.rb +9 -6
- data/lib/active_record/connection_handling.rb +107 -136
- data/lib/active_record/core.rb +194 -224
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
- data/lib/active_record/database_configurations/database_config.rb +21 -12
- data/lib/active_record/database_configurations/hash_config.rb +84 -16
- data/lib/active_record/database_configurations/url_config.rb +18 -12
- data/lib/active_record/database_configurations.rb +95 -59
- data/lib/active_record/delegated_type.rb +61 -15
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +224 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +151 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +172 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +53 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +92 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +96 -0
- data/lib/active_record/encryption.rb +56 -0
- data/lib/active_record/enum.rb +156 -62
- data/lib/active_record/errors.rb +171 -15
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +70 -14
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +131 -86
- data/lib/active_record/future_result.rb +164 -0
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +81 -29
- data/lib/active_record/insert_all.rb +133 -20
- data/lib/active_record/integration.rb +11 -10
- data/lib/active_record/internal_metadata.rb +117 -33
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +36 -21
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +52 -19
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
- data/lib/active_record/middleware/database_selector.rb +23 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +108 -13
- data/lib/active_record/migration/compatibility.rb +221 -48
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +355 -171
- data/lib/active_record/model_schema.rb +116 -97
- data/lib/active_record/nested_attributes.rb +36 -15
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +159 -0
- data/lib/active_record/persistence.rb +405 -85
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +174 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +29 -6
- data/lib/active_record/railtie.rb +219 -43
- data/lib/active_record/railties/controller_runtime.rb +13 -9
- data/lib/active_record/railties/databases.rake +185 -249
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +41 -3
- data/lib/active_record/reflection.rb +229 -80
- data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
- data/lib/active_record/relation/batches.rb +192 -63
- data/lib/active_record/relation/calculations.rb +211 -90
- data/lib/active_record/relation/delegation.rb +27 -13
- data/lib/active_record/relation/finder_methods.rb +108 -51
- data/lib/active_record/relation/merger.rb +22 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +27 -20
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +654 -127
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +20 -3
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +262 -120
- data/lib/active_record/result.rb +37 -11
- data/lib/active_record/runtime_registry.rb +18 -13
- data/lib/active_record/sanitization.rb +65 -20
- data/lib/active_record/schema.rb +36 -22
- data/lib/active_record/schema_dumper.rb +73 -24
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +5 -13
- data/lib/active_record/scoping.rb +65 -34
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +10 -8
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +225 -136
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
- data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +116 -96
- data/lib/active_record/timestamp.rb +28 -17
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +48 -27
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +32 -14
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -5
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +4 -4
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +51 -6
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +335 -32
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/and.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/homogeneous_in.rb +0 -8
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +5 -0
- data/lib/arel/predications.rb +13 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +16 -3
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +139 -19
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +18 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +92 -13
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -1,62 +1,80 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_record/connection_adapters/abstract_mysql_adapter"
|
4
|
-
require "active_record/connection_adapters/
|
4
|
+
require "active_record/connection_adapters/mysql2/database_statements"
|
5
5
|
|
6
6
|
gem "mysql2", "~> 0.5"
|
7
7
|
require "mysql2"
|
8
8
|
|
9
9
|
module ActiveRecord
|
10
10
|
module ConnectionHandling # :nodoc:
|
11
|
+
def mysql2_adapter_class
|
12
|
+
ConnectionAdapters::Mysql2Adapter
|
13
|
+
end
|
14
|
+
|
11
15
|
# Establishes a connection to the database that's used by all Active Record objects.
|
12
16
|
def mysql2_connection(config)
|
13
|
-
config
|
14
|
-
config[:flags] ||= 0
|
15
|
-
|
16
|
-
if config[:flags].kind_of? Array
|
17
|
-
config[:flags].push "FOUND_ROWS"
|
18
|
-
else
|
19
|
-
config[:flags] |= Mysql2::Client::FOUND_ROWS
|
20
|
-
end
|
21
|
-
|
22
|
-
ConnectionAdapters::Mysql2Adapter.new(
|
23
|
-
ConnectionAdapters::Mysql2Adapter.new_client(config),
|
24
|
-
logger,
|
25
|
-
nil,
|
26
|
-
config,
|
27
|
-
)
|
17
|
+
mysql2_adapter_class.new(config)
|
28
18
|
end
|
29
19
|
end
|
30
20
|
|
31
21
|
module ConnectionAdapters
|
22
|
+
# = Active Record MySQL2 Adapter
|
32
23
|
class Mysql2Adapter < AbstractMysqlAdapter
|
33
|
-
ER_BAD_DB_ERROR
|
24
|
+
ER_BAD_DB_ERROR = 1049
|
25
|
+
ER_DBACCESS_DENIED_ERROR = 1044
|
26
|
+
ER_ACCESS_DENIED_ERROR = 1045
|
27
|
+
ER_CONN_HOST_ERROR = 2003
|
28
|
+
ER_UNKNOWN_HOST_ERROR = 2005
|
29
|
+
|
34
30
|
ADAPTER_NAME = "Mysql2"
|
35
31
|
|
36
|
-
include
|
32
|
+
include Mysql2::DatabaseStatements
|
37
33
|
|
38
34
|
class << self
|
39
35
|
def new_client(config)
|
40
|
-
Mysql2::Client.new(config)
|
41
|
-
rescue Mysql2::Error => error
|
42
|
-
|
43
|
-
|
36
|
+
::Mysql2::Client.new(config)
|
37
|
+
rescue ::Mysql2::Error => error
|
38
|
+
case error.error_number
|
39
|
+
when ER_BAD_DB_ERROR
|
40
|
+
raise ActiveRecord::NoDatabaseError.db_error(config[:database])
|
41
|
+
when ER_DBACCESS_DENIED_ERROR, ER_ACCESS_DENIED_ERROR
|
42
|
+
raise ActiveRecord::DatabaseConnectionError.username_error(config[:username])
|
43
|
+
when ER_CONN_HOST_ERROR, ER_UNKNOWN_HOST_ERROR
|
44
|
+
raise ActiveRecord::DatabaseConnectionError.hostname_error(config[:host])
|
44
45
|
else
|
45
46
|
raise ActiveRecord::ConnectionNotEstablished, error.message
|
46
47
|
end
|
47
48
|
end
|
48
|
-
end
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
50
|
+
private
|
51
|
+
def initialize_type_map(m)
|
52
|
+
super
|
53
|
+
|
54
|
+
m.register_type(%r(char)i) do |sql_type|
|
55
|
+
limit = extract_limit(sql_type)
|
56
|
+
Type.lookup(:string, adapter: :mysql2, limit: limit)
|
57
|
+
end
|
58
|
+
|
59
|
+
m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
|
60
|
+
m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
|
61
|
+
end
|
54
62
|
end
|
55
63
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
64
|
+
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
65
|
+
|
66
|
+
def initialize(...)
|
67
|
+
super
|
68
|
+
|
69
|
+
@config[:flags] ||= 0
|
70
|
+
|
71
|
+
if @config[:flags].kind_of? Array
|
72
|
+
@config[:flags].push "FOUND_ROWS"
|
73
|
+
else
|
74
|
+
@config[:flags] |= ::Mysql2::Client::FOUND_ROWS
|
75
|
+
end
|
76
|
+
|
77
|
+
@connection_parameters ||= @config
|
60
78
|
end
|
61
79
|
|
62
80
|
def supports_json?
|
@@ -75,17 +93,19 @@ module ActiveRecord
|
|
75
93
|
true
|
76
94
|
end
|
77
95
|
|
96
|
+
def savepoint_errors_invalidate_transactions?
|
97
|
+
true
|
98
|
+
end
|
99
|
+
|
78
100
|
def supports_lazy_transactions?
|
79
101
|
true
|
80
102
|
end
|
81
103
|
|
82
104
|
# HELPER METHODS ===========================================
|
83
105
|
|
84
|
-
def each_hash(result) # :nodoc:
|
106
|
+
def each_hash(result, &block) # :nodoc:
|
85
107
|
if block_given?
|
86
|
-
result.each(as: :hash, symbolize_keys: true)
|
87
|
-
yield row
|
88
|
-
end
|
108
|
+
result.each(as: :hash, symbolize_keys: true, &block)
|
89
109
|
else
|
90
110
|
to_enum(:each_hash, result)
|
91
111
|
end
|
@@ -99,10 +119,11 @@ module ActiveRecord
|
|
99
119
|
# QUOTING ==================================================
|
100
120
|
#++
|
101
121
|
|
122
|
+
# Quotes strings for use in SQL input.
|
102
123
|
def quote_string(string)
|
103
|
-
|
104
|
-
|
105
|
-
|
124
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |connection|
|
125
|
+
connection.escape(string)
|
126
|
+
end
|
106
127
|
end
|
107
128
|
|
108
129
|
#--
|
@@ -110,37 +131,45 @@ module ActiveRecord
|
|
110
131
|
#++
|
111
132
|
|
112
133
|
def active?
|
113
|
-
|
134
|
+
!!@raw_connection&.ping
|
114
135
|
end
|
115
136
|
|
116
|
-
def reconnect!
|
117
|
-
super
|
118
|
-
disconnect!
|
119
|
-
connect
|
120
|
-
end
|
121
137
|
alias :reset! :reconnect!
|
122
138
|
|
123
139
|
# Disconnects from the database if already connected.
|
124
140
|
# Otherwise, this method does nothing.
|
125
141
|
def disconnect!
|
126
142
|
super
|
127
|
-
@
|
143
|
+
@raw_connection&.close
|
144
|
+
@raw_connection = nil
|
128
145
|
end
|
129
146
|
|
130
147
|
def discard! # :nodoc:
|
131
148
|
super
|
132
|
-
@
|
133
|
-
@
|
149
|
+
@raw_connection&.automatic_close = false
|
150
|
+
@raw_connection = nil
|
134
151
|
end
|
135
152
|
|
136
153
|
private
|
154
|
+
def text_type?(type)
|
155
|
+
TYPE_MAP.lookup(type).is_a?(Type::String) || TYPE_MAP.lookup(type).is_a?(Type::Text)
|
156
|
+
end
|
157
|
+
|
137
158
|
def connect
|
138
|
-
@
|
139
|
-
|
159
|
+
@raw_connection = self.class.new_client(@connection_parameters)
|
160
|
+
rescue ConnectionNotEstablished => ex
|
161
|
+
raise ex.set_pool(@pool)
|
162
|
+
end
|
163
|
+
|
164
|
+
def reconnect
|
165
|
+
@raw_connection&.close
|
166
|
+
@raw_connection = nil
|
167
|
+
connect
|
140
168
|
end
|
141
169
|
|
142
170
|
def configure_connection
|
143
|
-
@
|
171
|
+
@raw_connection.query_options[:as] = :array
|
172
|
+
@raw_connection.query_options[:database_timezone] = default_timezone
|
144
173
|
super
|
145
174
|
end
|
146
175
|
|
@@ -149,16 +178,38 @@ module ActiveRecord
|
|
149
178
|
end
|
150
179
|
|
151
180
|
def get_full_version
|
152
|
-
|
181
|
+
any_raw_connection.server_info[:version]
|
153
182
|
end
|
154
183
|
|
155
184
|
def translate_exception(exception, message:, sql:, binds:)
|
156
|
-
if exception.is_a?(Mysql2::Error::TimeoutError) && !exception.error_number
|
157
|
-
ActiveRecord::AdapterTimeout.new(message, sql: sql, binds: binds)
|
185
|
+
if exception.is_a?(::Mysql2::Error::TimeoutError) && !exception.error_number
|
186
|
+
ActiveRecord::AdapterTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
187
|
+
elsif exception.is_a?(::Mysql2::Error::ConnectionError)
|
188
|
+
if exception.message.match?(/MySQL client is not connected/i)
|
189
|
+
ActiveRecord::ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
190
|
+
else
|
191
|
+
ActiveRecord::ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
192
|
+
end
|
158
193
|
else
|
159
194
|
super
|
160
195
|
end
|
161
196
|
end
|
197
|
+
|
198
|
+
def default_prepared_statements
|
199
|
+
false
|
200
|
+
end
|
201
|
+
|
202
|
+
ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
|
203
|
+
Type::ImmutableString.new(true: "1", false: "0", **args)
|
204
|
+
end
|
205
|
+
|
206
|
+
ActiveRecord::Type.register(:string, adapter: :mysql2) do |_, **args|
|
207
|
+
Type::String.new(true: "1", false: "0", **args)
|
208
|
+
end
|
209
|
+
|
210
|
+
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
162
211
|
end
|
212
|
+
|
213
|
+
ActiveSupport.run_load_hooks(:active_record_mysql2adapter, Mysql2Adapter)
|
163
214
|
end
|
164
215
|
end
|
@@ -5,8 +5,13 @@ module ActiveRecord
|
|
5
5
|
class PoolConfig # :nodoc:
|
6
6
|
include Mutex_m
|
7
7
|
|
8
|
-
attr_reader :db_config, :
|
9
|
-
|
8
|
+
attr_reader :db_config, :role, :shard
|
9
|
+
attr_writer :schema_reflection
|
10
|
+
attr_accessor :connection_class
|
11
|
+
|
12
|
+
def schema_reflection
|
13
|
+
@schema_reflection ||= SchemaReflection.new(db_config.lazy_schema_cache_path)
|
14
|
+
end
|
10
15
|
|
11
16
|
INSTANCES = ObjectSpace::WeakMap.new
|
12
17
|
private_constant :INSTANCES
|
@@ -15,27 +20,31 @@ module ActiveRecord
|
|
15
20
|
def discard_pools!
|
16
21
|
INSTANCES.each_key(&:discard_pool!)
|
17
22
|
end
|
23
|
+
|
24
|
+
def disconnect_all!
|
25
|
+
INSTANCES.each_key { |c| c.disconnect!(automatic_reconnect: true) }
|
26
|
+
end
|
18
27
|
end
|
19
28
|
|
20
|
-
def initialize(
|
29
|
+
def initialize(connection_class, db_config, role, shard)
|
21
30
|
super()
|
22
|
-
@
|
31
|
+
@connection_class = connection_class
|
23
32
|
@db_config = db_config
|
33
|
+
@role = role
|
34
|
+
@shard = shard
|
24
35
|
@pool = nil
|
25
36
|
INSTANCES[self] = self
|
26
37
|
end
|
27
38
|
|
28
|
-
def
|
29
|
-
if
|
30
|
-
connection_klass
|
31
|
-
elsif connection_klass.primary_class?
|
39
|
+
def connection_name
|
40
|
+
if connection_class.primary_class?
|
32
41
|
"ActiveRecord::Base"
|
33
42
|
else
|
34
|
-
|
43
|
+
connection_class.name
|
35
44
|
end
|
36
45
|
end
|
37
46
|
|
38
|
-
def disconnect!
|
47
|
+
def disconnect!(automatic_reconnect: false)
|
39
48
|
ActiveSupport::ForkTracker.check!
|
40
49
|
|
41
50
|
return unless @pool
|
@@ -43,7 +52,7 @@ module ActiveRecord
|
|
43
52
|
synchronize do
|
44
53
|
return unless @pool
|
45
54
|
|
46
|
-
@pool.automatic_reconnect =
|
55
|
+
@pool.automatic_reconnect = automatic_reconnect
|
47
56
|
@pool.disconnect!
|
48
57
|
end
|
49
58
|
|
@@ -4,40 +4,50 @@ module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
5
5
|
class PoolManager # :nodoc:
|
6
6
|
def initialize
|
7
|
-
@
|
7
|
+
@role_to_shard_mapping = Hash.new { |h, k| h[k] = {} }
|
8
8
|
end
|
9
9
|
|
10
10
|
def shard_names
|
11
|
-
@
|
11
|
+
@role_to_shard_mapping.values.flat_map { |shard_map| shard_map.keys }.uniq
|
12
12
|
end
|
13
13
|
|
14
14
|
def role_names
|
15
|
-
@
|
15
|
+
@role_to_shard_mapping.keys
|
16
16
|
end
|
17
17
|
|
18
18
|
def pool_configs(role = nil)
|
19
19
|
if role
|
20
|
-
@
|
20
|
+
@role_to_shard_mapping[role].values
|
21
21
|
else
|
22
|
-
@
|
22
|
+
@role_to_shard_mapping.flat_map { |_, shard_map| shard_map.values }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def each_pool_config(role = nil, &block)
|
27
|
+
if role
|
28
|
+
@role_to_shard_mapping[role].each_value(&block)
|
29
|
+
else
|
30
|
+
@role_to_shard_mapping.each_value do |shard_map|
|
31
|
+
shard_map.each_value(&block)
|
32
|
+
end
|
23
33
|
end
|
24
34
|
end
|
25
35
|
|
26
36
|
def remove_role(role)
|
27
|
-
@
|
37
|
+
@role_to_shard_mapping.delete(role)
|
28
38
|
end
|
29
39
|
|
30
40
|
def remove_pool_config(role, shard)
|
31
|
-
@
|
41
|
+
@role_to_shard_mapping[role].delete(shard)
|
32
42
|
end
|
33
43
|
|
34
44
|
def get_pool_config(role, shard)
|
35
|
-
@
|
45
|
+
@role_to_shard_mapping[role][shard]
|
36
46
|
end
|
37
47
|
|
38
48
|
def set_pool_config(role, shard, pool_config)
|
39
49
|
if pool_config
|
40
|
-
@
|
50
|
+
@role_to_shard_mapping[role][shard] = pool_config
|
41
51
|
else
|
42
52
|
raise ArgumentError, "The `pool_config` for the :#{role} role and :#{shard} shard was `nil`. Please check your configuration. If you want your writing role to be something other than `:writing` set `config.active_record.writing_role` in your application configuration. The same setting should be applied for the `reading_role` if applicable."
|
43
53
|
end
|
@@ -6,31 +6,48 @@ module ActiveRecord
|
|
6
6
|
class Column < ConnectionAdapters::Column # :nodoc:
|
7
7
|
delegate :oid, :fmod, to: :sql_type_metadata
|
8
8
|
|
9
|
-
def initialize(*, serial: nil, **)
|
9
|
+
def initialize(*, serial: nil, generated: nil, **)
|
10
10
|
super
|
11
11
|
@serial = serial
|
12
|
+
@generated = generated
|
12
13
|
end
|
13
14
|
|
14
15
|
def serial?
|
15
16
|
@serial
|
16
17
|
end
|
18
|
+
alias_method :auto_incremented_by_db?, :serial?
|
19
|
+
|
20
|
+
def virtual?
|
21
|
+
# We assume every generated column is virtual, no matter the concrete type
|
22
|
+
@generated.present?
|
23
|
+
end
|
24
|
+
|
25
|
+
def has_default?
|
26
|
+
super && !virtual?
|
27
|
+
end
|
17
28
|
|
18
29
|
def array
|
19
30
|
sql_type_metadata.sql_type.end_with?("[]")
|
20
31
|
end
|
21
32
|
alias :array? :array
|
22
33
|
|
34
|
+
def enum?
|
35
|
+
type == :enum
|
36
|
+
end
|
37
|
+
|
23
38
|
def sql_type
|
24
39
|
super.delete_suffix("[]")
|
25
40
|
end
|
26
41
|
|
27
42
|
def init_with(coder)
|
28
43
|
@serial = coder["serial"]
|
44
|
+
@generated = coder["generated"]
|
29
45
|
super
|
30
46
|
end
|
31
47
|
|
32
48
|
def encode_with(coder)
|
33
49
|
coder["serial"] = @serial
|
50
|
+
coder["generated"] = @generated
|
34
51
|
super
|
35
52
|
end
|
36
53
|
|
@@ -4,19 +4,19 @@ module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
5
5
|
module PostgreSQL
|
6
6
|
module DatabaseStatements
|
7
|
-
def explain(arel, binds = [])
|
8
|
-
sql
|
9
|
-
|
7
|
+
def explain(arel, binds = [], options = [])
|
8
|
+
sql = build_explain_clause(options) + " " + to_sql(arel, binds)
|
9
|
+
result = internal_exec_query(sql, "EXPLAIN", binds)
|
10
|
+
PostgreSQL::ExplainPrettyPrinter.new.pp(result)
|
10
11
|
end
|
11
12
|
|
12
13
|
# Queries the database and returns the results in an Array-like object
|
13
|
-
def query(sql, name = nil)
|
14
|
-
materialize_transactions
|
14
|
+
def query(sql, name = nil) # :nodoc:
|
15
15
|
mark_transaction_written_if_write(sql)
|
16
16
|
|
17
17
|
log(sql, name) do
|
18
|
-
|
19
|
-
|
18
|
+
with_raw_connection do |conn|
|
19
|
+
conn.async_exec(sql).map_types!(@type_map_for_results).values
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -34,65 +34,52 @@ module ActiveRecord
|
|
34
34
|
|
35
35
|
# Executes an SQL statement, returning a PG::Result object on success
|
36
36
|
# or raising a PG::Error exception otherwise.
|
37
|
+
#
|
38
|
+
# Setting +allow_retry+ to true causes the db to reconnect and retry
|
39
|
+
# executing the SQL statement in case of a connection-related exception.
|
40
|
+
# This option should only be enabled for known idempotent queries.
|
41
|
+
#
|
37
42
|
# Note: the PG::Result object is manually memory managed; if you don't
|
38
43
|
# need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
|
39
|
-
def execute(
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
materialize_transactions
|
45
|
-
mark_transaction_written_if_write(sql)
|
44
|
+
def execute(...) # :nodoc:
|
45
|
+
super
|
46
|
+
ensure
|
47
|
+
@notice_receiver_sql_warnings = []
|
48
|
+
end
|
46
49
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
+
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
|
51
|
+
log(sql, name, async: async) do
|
52
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
53
|
+
result = conn.async_exec(sql)
|
54
|
+
handle_warnings(result)
|
55
|
+
result
|
50
56
|
end
|
51
57
|
end
|
52
58
|
end
|
53
59
|
|
54
|
-
def
|
55
|
-
execute_and_clear(sql, name, binds, prepare: prepare) do |result|
|
60
|
+
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true) # :nodoc:
|
61
|
+
execute_and_clear(sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |result|
|
56
62
|
types = {}
|
57
63
|
fields = result.fields
|
58
64
|
fields.each_with_index do |fname, i|
|
59
65
|
ftype = result.ftype i
|
60
66
|
fmod = result.fmod i
|
61
|
-
|
62
|
-
when Type::Integer, Type::Float, OID::Decimal, Type::String, Type::DateTime, Type::Boolean
|
63
|
-
# skip if a column has already been type casted by pg decoders
|
64
|
-
else types[fname] = type
|
65
|
-
end
|
67
|
+
types[fname] = types[i] = get_oid_type(ftype, fmod, fname)
|
66
68
|
end
|
67
69
|
build_result(columns: fields, rows: result.values, column_types: types)
|
68
70
|
end
|
69
71
|
end
|
70
72
|
|
71
|
-
def exec_delete(sql, name = nil, binds = [])
|
73
|
+
def exec_delete(sql, name = nil, binds = []) # :nodoc:
|
72
74
|
execute_and_clear(sql, name, binds) { |result| result.cmd_tuples }
|
73
75
|
end
|
74
76
|
alias :exec_update :exec_delete
|
75
77
|
|
76
|
-
def
|
77
|
-
if pk.nil?
|
78
|
-
# Extract the table from the insert sql. Yuck.
|
79
|
-
table_ref = extract_table_ref_from_insert_sql(sql)
|
80
|
-
pk = primary_key(table_ref) if table_ref
|
81
|
-
end
|
82
|
-
|
83
|
-
if pk = suppress_composite_primary_key(pk)
|
84
|
-
sql = "#{sql} RETURNING #{quote_column_name(pk)}"
|
85
|
-
end
|
86
|
-
|
87
|
-
super
|
88
|
-
end
|
89
|
-
private :sql_for_insert
|
90
|
-
|
91
|
-
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
|
78
|
+
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) # :nodoc:
|
92
79
|
if use_insert_returning? || pk == false
|
93
80
|
super
|
94
81
|
else
|
95
|
-
result =
|
82
|
+
result = internal_exec_query(sql, name, binds)
|
96
83
|
unless sequence_name
|
97
84
|
table_ref = extract_table_ref_from_insert_sql(sql)
|
98
85
|
if table_ref
|
@@ -107,26 +94,56 @@ module ActiveRecord
|
|
107
94
|
end
|
108
95
|
|
109
96
|
# Begins a transaction.
|
110
|
-
def begin_db_transaction
|
111
|
-
|
97
|
+
def begin_db_transaction # :nodoc:
|
98
|
+
internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
112
99
|
end
|
113
100
|
|
114
|
-
def begin_isolated_db_transaction(isolation)
|
115
|
-
|
116
|
-
execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
|
101
|
+
def begin_isolated_db_transaction(isolation) # :nodoc:
|
102
|
+
internal_execute("BEGIN ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
117
103
|
end
|
118
104
|
|
119
105
|
# Commits a transaction.
|
120
|
-
def commit_db_transaction
|
121
|
-
|
106
|
+
def commit_db_transaction # :nodoc:
|
107
|
+
internal_execute("COMMIT", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
122
108
|
end
|
123
109
|
|
124
110
|
# Aborts a transaction.
|
125
|
-
def exec_rollback_db_transaction
|
126
|
-
|
111
|
+
def exec_rollback_db_transaction # :nodoc:
|
112
|
+
cancel_any_running_query
|
113
|
+
internal_execute("ROLLBACK", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
114
|
+
end
|
115
|
+
|
116
|
+
def exec_restart_db_transaction # :nodoc:
|
117
|
+
cancel_any_running_query
|
118
|
+
internal_execute("ROLLBACK AND CHAIN", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
119
|
+
end
|
120
|
+
|
121
|
+
# From https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
|
122
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP").freeze # :nodoc:
|
123
|
+
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
124
|
+
|
125
|
+
def high_precision_current_timestamp
|
126
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP
|
127
|
+
end
|
128
|
+
|
129
|
+
def build_explain_clause(options = [])
|
130
|
+
return "EXPLAIN" if options.empty?
|
131
|
+
|
132
|
+
"EXPLAIN (#{options.join(", ").upcase})"
|
127
133
|
end
|
128
134
|
|
129
135
|
private
|
136
|
+
IDLE_TRANSACTION_STATUSES = [PG::PQTRANS_IDLE, PG::PQTRANS_INTRANS, PG::PQTRANS_INERROR]
|
137
|
+
private_constant :IDLE_TRANSACTION_STATUSES
|
138
|
+
|
139
|
+
def cancel_any_running_query
|
140
|
+
return if @raw_connection.nil? || IDLE_TRANSACTION_STATUSES.include?(@raw_connection.transaction_status)
|
141
|
+
|
142
|
+
@raw_connection.cancel
|
143
|
+
@raw_connection.block
|
144
|
+
rescue PG::Error
|
145
|
+
end
|
146
|
+
|
130
147
|
def execute_batch(statements, name = nil)
|
131
148
|
execute(combine_multi_statements(statements))
|
132
149
|
end
|
@@ -137,12 +154,29 @@ module ActiveRecord
|
|
137
154
|
|
138
155
|
# Returns the current ID of a table's sequence.
|
139
156
|
def last_insert_id_result(sequence_name)
|
140
|
-
|
157
|
+
internal_exec_query("SELECT currval(#{quote(sequence_name)})", "SQL")
|
158
|
+
end
|
159
|
+
|
160
|
+
def returning_column_values(result)
|
161
|
+
result.rows.first
|
141
162
|
end
|
142
163
|
|
143
164
|
def suppress_composite_primary_key(pk)
|
144
165
|
pk unless pk.is_a?(Array)
|
145
166
|
end
|
167
|
+
|
168
|
+
def handle_warnings(sql)
|
169
|
+
@notice_receiver_sql_warnings.each do |warning|
|
170
|
+
next if warning_ignored?(warning)
|
171
|
+
|
172
|
+
warning.sql = sql
|
173
|
+
ActiveRecord.db_warnings_action.call(warning)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def warning_ignored?(warning)
|
178
|
+
["WARNING", "ERROR", "FATAL", "PANIC"].exclude?(warning.level) || super
|
179
|
+
end
|
146
180
|
end
|
147
181
|
end
|
148
182
|
end
|
@@ -16,6 +16,14 @@ module ActiveRecord
|
|
16
16
|
super
|
17
17
|
end
|
18
18
|
end
|
19
|
+
|
20
|
+
def type_cast_for_schema(value)
|
21
|
+
case value
|
22
|
+
when ::Float::INFINITY then "::Float::INFINITY"
|
23
|
+
when -::Float::INFINITY then "-::Float::INFINITY"
|
24
|
+
else super
|
25
|
+
end
|
26
|
+
end
|
19
27
|
end
|
20
28
|
end
|
21
29
|
end
|