activerecord 7.0.8.6 → 7.2.2.1
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 +631 -1939
- data/MIT-LICENSE +1 -1
- data/README.rdoc +29 -29
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +35 -12
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +23 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +22 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +26 -14
- data/lib/active_record/associations/collection_proxy.rb +29 -11
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +21 -14
- data/lib/active_record/associations/has_many_through_association.rb +17 -7
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
- data/lib/active_record/associations/join_dependency.rb +10 -10
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +33 -8
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +354 -485
- data/lib/active_record/attribute_assignment.rb +0 -4
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +45 -25
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +8 -7
- data/lib/active_record/attribute_methods/serialization.rb +131 -32
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +148 -33
- data/lib/active_record/attributes.rb +64 -50
- data/lib/active_record/autosave_association.rb +69 -37
- data/lib/active_record/base.rb +9 -5
- data/lib/active_record/callbacks.rb +11 -25
- 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 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +323 -88
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +217 -63
- data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +307 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +278 -125
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
- data/lib/active_record/connection_adapters/mysql/quoting.rb +53 -54
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
- data/lib/active_record/connection_adapters/pool_config.rb +20 -10
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +370 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
- data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +50 -8
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -110
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +124 -1
- data/lib/active_record/connection_handling.rb +96 -104
- data/lib/active_record/core.rb +251 -176
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
- data/lib/active_record/database_configurations/database_config.rb +26 -5
- data/lib/active_record/database_configurations/hash_config.rb +52 -34
- data/lib/active_record/database_configurations/url_config.rb +37 -12
- data/lib/active_record/database_configurations.rb +87 -34
- data/lib/active_record/delegated_type.rb +39 -10
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +45 -21
- data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
- data/lib/active_record/encryption/encryptor.rb +18 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +6 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +22 -21
- data/lib/active_record/encryption.rb +3 -0
- data/lib/active_record/enum.rb +129 -28
- data/lib/active_record/errors.rb +151 -31
- data/lib/active_record/explain.rb +21 -12
- 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 +29 -8
- data/lib/active_record/fixtures.rb +167 -97
- data/lib/active_record/future_result.rb +47 -8
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +34 -18
- data/lib/active_record/insert_all.rb +72 -22
- data/lib/active_record/integration.rb +11 -8
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +18 -22
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +106 -8
- data/lib/active_record/migration/compatibility.rb +147 -5
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +234 -117
- data/lib/active_record/model_schema.rb +90 -102
- data/lib/active_record/nested_attributes.rb +48 -11
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +168 -339
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -25
- data/lib/active_record/query_logs.rb +92 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +33 -8
- data/lib/active_record/railtie.rb +129 -85
- data/lib/active_record/railties/controller_runtime.rb +22 -7
- data/lib/active_record/railties/databases.rake +145 -154
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +267 -69
- data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +250 -93
- data/lib/active_record/relation/delegation.rb +30 -19
- data/lib/active_record/relation/finder_methods.rb +93 -18
- data/lib/active_record/relation/merger.rb +6 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +18 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +28 -16
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +576 -107
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +5 -4
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +580 -90
- data/lib/active_record/result.rb +49 -48
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +70 -25
- data/lib/active_record/schema.rb +8 -7
- data/lib/active_record/schema_dumper.rb +63 -14
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +27 -6
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +190 -118
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_fixtures.rb +170 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +31 -17
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +106 -24
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +9 -3
- 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 +61 -11
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +247 -33
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +17 -5
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +112 -34
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +21 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -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
- metadata +56 -14
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -1,70 +1,69 @@
|
|
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
|
-
module ConnectionHandling # :nodoc:
|
11
|
-
# Establishes a connection to the database that's used by all Active Record objects.
|
12
|
-
def mysql2_connection(config)
|
13
|
-
config = config.symbolize_keys
|
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
|
-
)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
10
|
module ConnectionAdapters
|
11
|
+
# = Active Record MySQL2 Adapter
|
32
12
|
class Mysql2Adapter < AbstractMysqlAdapter
|
33
|
-
ER_BAD_DB_ERROR
|
34
|
-
|
35
|
-
|
36
|
-
|
13
|
+
ER_BAD_DB_ERROR = 1049
|
14
|
+
ER_DBACCESS_DENIED_ERROR = 1044
|
15
|
+
ER_ACCESS_DENIED_ERROR = 1045
|
16
|
+
ER_CONN_HOST_ERROR = 2003
|
17
|
+
ER_UNKNOWN_HOST_ERROR = 2005
|
37
18
|
|
38
19
|
ADAPTER_NAME = "Mysql2"
|
39
20
|
|
40
|
-
include
|
21
|
+
include Mysql2::DatabaseStatements
|
41
22
|
|
42
23
|
class << self
|
43
24
|
def new_client(config)
|
44
|
-
Mysql2::Client.new(config)
|
45
|
-
rescue Mysql2::Error => error
|
46
|
-
|
25
|
+
::Mysql2::Client.new(config)
|
26
|
+
rescue ::Mysql2::Error => error
|
27
|
+
case error.error_number
|
28
|
+
when ER_BAD_DB_ERROR
|
47
29
|
raise ActiveRecord::NoDatabaseError.db_error(config[:database])
|
48
|
-
|
30
|
+
when ER_DBACCESS_DENIED_ERROR, ER_ACCESS_DENIED_ERROR
|
49
31
|
raise ActiveRecord::DatabaseConnectionError.username_error(config[:username])
|
50
|
-
|
32
|
+
when ER_CONN_HOST_ERROR, ER_UNKNOWN_HOST_ERROR
|
51
33
|
raise ActiveRecord::DatabaseConnectionError.hostname_error(config[:host])
|
52
34
|
else
|
53
35
|
raise ActiveRecord::ConnectionNotEstablished, error.message
|
54
36
|
end
|
55
37
|
end
|
56
|
-
end
|
57
38
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
39
|
+
private
|
40
|
+
def initialize_type_map(m)
|
41
|
+
super
|
42
|
+
|
43
|
+
m.register_type(%r(char)i) do |sql_type|
|
44
|
+
limit = extract_limit(sql_type)
|
45
|
+
Type.lookup(:string, adapter: :mysql2, limit: limit)
|
46
|
+
end
|
47
|
+
|
48
|
+
m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
|
49
|
+
m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
|
50
|
+
end
|
62
51
|
end
|
63
52
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
53
|
+
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
54
|
+
|
55
|
+
def initialize(...)
|
56
|
+
super
|
57
|
+
|
58
|
+
@config[:flags] ||= 0
|
59
|
+
|
60
|
+
if @config[:flags].kind_of? Array
|
61
|
+
@config[:flags].push "FOUND_ROWS"
|
62
|
+
else
|
63
|
+
@config[:flags] |= ::Mysql2::Client::FOUND_ROWS
|
64
|
+
end
|
65
|
+
|
66
|
+
@connection_parameters ||= @config
|
68
67
|
end
|
69
68
|
|
70
69
|
def supports_json?
|
@@ -83,6 +82,10 @@ module ActiveRecord
|
|
83
82
|
true
|
84
83
|
end
|
85
84
|
|
85
|
+
def savepoint_errors_invalidate_transactions?
|
86
|
+
true
|
87
|
+
end
|
88
|
+
|
86
89
|
def supports_lazy_transactions?
|
87
90
|
true
|
88
91
|
end
|
@@ -102,69 +105,99 @@ module ActiveRecord
|
|
102
105
|
end
|
103
106
|
|
104
107
|
#--
|
105
|
-
#
|
108
|
+
# CONNECTION MANAGEMENT ====================================
|
106
109
|
#++
|
107
110
|
|
108
|
-
def
|
109
|
-
@
|
110
|
-
rescue Mysql2::Error => error
|
111
|
-
raise translate_exception(error, message: error.message, sql: "<escape>", binds: [])
|
111
|
+
def connected?
|
112
|
+
!(@raw_connection.nil? || @raw_connection.closed?)
|
112
113
|
end
|
113
114
|
|
114
|
-
#--
|
115
|
-
# CONNECTION MANAGEMENT ====================================
|
116
|
-
#++
|
117
|
-
|
118
115
|
def active?
|
119
|
-
@
|
116
|
+
connected? && @lock.synchronize { @raw_connection&.ping } || false
|
120
117
|
end
|
121
118
|
|
122
|
-
def reconnect!
|
123
|
-
super
|
124
|
-
disconnect!
|
125
|
-
connect
|
126
|
-
end
|
127
119
|
alias :reset! :reconnect!
|
128
120
|
|
129
121
|
# Disconnects from the database if already connected.
|
130
122
|
# Otherwise, this method does nothing.
|
131
123
|
def disconnect!
|
132
|
-
|
133
|
-
|
124
|
+
@lock.synchronize do
|
125
|
+
super
|
126
|
+
@raw_connection&.close
|
127
|
+
@raw_connection = nil
|
128
|
+
end
|
134
129
|
end
|
135
130
|
|
136
131
|
def discard! # :nodoc:
|
137
|
-
|
138
|
-
|
139
|
-
|
132
|
+
@lock.synchronize do
|
133
|
+
super
|
134
|
+
@raw_connection&.automatic_close = false
|
135
|
+
@raw_connection = nil
|
136
|
+
end
|
140
137
|
end
|
141
138
|
|
142
139
|
private
|
140
|
+
def text_type?(type)
|
141
|
+
TYPE_MAP.lookup(type).is_a?(Type::String) || TYPE_MAP.lookup(type).is_a?(Type::Text)
|
142
|
+
end
|
143
|
+
|
143
144
|
def connect
|
144
|
-
@
|
145
|
-
|
145
|
+
@raw_connection = self.class.new_client(@connection_parameters)
|
146
|
+
rescue ConnectionNotEstablished => ex
|
147
|
+
raise ex.set_pool(@pool)
|
148
|
+
end
|
149
|
+
|
150
|
+
def reconnect
|
151
|
+
@lock.synchronize do
|
152
|
+
@raw_connection&.close
|
153
|
+
@raw_connection = nil
|
154
|
+
connect
|
155
|
+
end
|
146
156
|
end
|
147
157
|
|
148
158
|
def configure_connection
|
149
|
-
@
|
159
|
+
@raw_connection.query_options[:as] = :array
|
160
|
+
@raw_connection.query_options[:database_timezone] = default_timezone
|
150
161
|
super
|
151
162
|
end
|
152
163
|
|
153
164
|
def full_version
|
154
|
-
|
165
|
+
database_version.full_version_string
|
155
166
|
end
|
156
167
|
|
157
168
|
def get_full_version
|
158
|
-
|
169
|
+
any_raw_connection.server_info[:version]
|
159
170
|
end
|
160
171
|
|
161
172
|
def translate_exception(exception, message:, sql:, binds:)
|
162
|
-
if exception.is_a?(Mysql2::Error::TimeoutError) && !exception.error_number
|
163
|
-
ActiveRecord::AdapterTimeout.new(message, sql: sql, binds: binds)
|
173
|
+
if exception.is_a?(::Mysql2::Error::TimeoutError) && !exception.error_number
|
174
|
+
ActiveRecord::AdapterTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
175
|
+
elsif exception.is_a?(::Mysql2::Error::ConnectionError)
|
176
|
+
if exception.message.match?(/MySQL client is not connected/i)
|
177
|
+
ActiveRecord::ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
178
|
+
else
|
179
|
+
ActiveRecord::ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
180
|
+
end
|
164
181
|
else
|
165
182
|
super
|
166
183
|
end
|
167
184
|
end
|
185
|
+
|
186
|
+
def default_prepared_statements
|
187
|
+
false
|
188
|
+
end
|
189
|
+
|
190
|
+
ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
|
191
|
+
Type::ImmutableString.new(true: "1", false: "0", **args)
|
192
|
+
end
|
193
|
+
|
194
|
+
ActiveRecord::Type.register(:string, adapter: :mysql2) do |_, **args|
|
195
|
+
Type::String.new(true: "1", false: "0", **args)
|
196
|
+
end
|
197
|
+
|
198
|
+
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
168
199
|
end
|
200
|
+
|
201
|
+
ActiveSupport.run_load_hooks(:active_record_mysql2adapter, Mysql2Adapter)
|
169
202
|
end
|
170
203
|
end
|
@@ -3,10 +3,15 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module ConnectionAdapters
|
5
5
|
class PoolConfig # :nodoc:
|
6
|
-
include
|
6
|
+
include MonitorMixin
|
7
7
|
|
8
|
-
attr_reader :db_config, :
|
9
|
-
|
8
|
+
attr_reader :db_config, :role, :shard
|
9
|
+
attr_writer :schema_reflection, :server_version
|
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,10 +20,15 @@ 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
29
|
def initialize(connection_class, db_config, role, shard)
|
21
30
|
super()
|
31
|
+
@server_version = nil
|
22
32
|
@connection_class = connection_class
|
23
33
|
@db_config = db_config
|
24
34
|
@role = role
|
@@ -27,7 +37,11 @@ module ActiveRecord
|
|
27
37
|
INSTANCES[self] = self
|
28
38
|
end
|
29
39
|
|
30
|
-
def
|
40
|
+
def server_version(connection)
|
41
|
+
@server_version || synchronize { @server_version ||= connection.get_database_version }
|
42
|
+
end
|
43
|
+
|
44
|
+
def connection_name
|
31
45
|
if connection_class.primary_class?
|
32
46
|
"ActiveRecord::Base"
|
33
47
|
else
|
@@ -35,15 +49,13 @@ module ActiveRecord
|
|
35
49
|
end
|
36
50
|
end
|
37
51
|
|
38
|
-
def disconnect!
|
39
|
-
ActiveSupport::ForkTracker.check!
|
40
|
-
|
52
|
+
def disconnect!(automatic_reconnect: false)
|
41
53
|
return unless @pool
|
42
54
|
|
43
55
|
synchronize do
|
44
56
|
return unless @pool
|
45
57
|
|
46
|
-
@pool.automatic_reconnect =
|
58
|
+
@pool.automatic_reconnect = automatic_reconnect
|
47
59
|
@pool.disconnect!
|
48
60
|
end
|
49
61
|
|
@@ -51,8 +63,6 @@ module ActiveRecord
|
|
51
63
|
end
|
52
64
|
|
53
65
|
def pool
|
54
|
-
ActiveSupport::ForkTracker.check!
|
55
|
-
|
56
66
|
@pool || synchronize { @pool ||= ConnectionAdapters::ConnectionPool.new(self) }
|
57
67
|
end
|
58
68
|
|
@@ -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
|
@@ -1,23 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/object/blank"
|
4
|
-
|
5
3
|
module ActiveRecord
|
6
4
|
module ConnectionAdapters
|
7
5
|
module PostgreSQL
|
8
6
|
class Column < ConnectionAdapters::Column # :nodoc:
|
9
7
|
delegate :oid, :fmod, to: :sql_type_metadata
|
10
8
|
|
11
|
-
def initialize(*, serial: nil, generated: nil, **)
|
9
|
+
def initialize(*, serial: nil, identity: nil, generated: nil, **)
|
12
10
|
super
|
13
11
|
@serial = serial
|
12
|
+
@identity = identity
|
14
13
|
@generated = generated
|
15
14
|
end
|
16
15
|
|
16
|
+
def identity?
|
17
|
+
@identity
|
18
|
+
end
|
19
|
+
|
17
20
|
def serial?
|
18
21
|
@serial
|
19
22
|
end
|
20
23
|
|
24
|
+
def auto_incremented_by_db?
|
25
|
+
serial? || identity?
|
26
|
+
end
|
27
|
+
|
21
28
|
def virtual?
|
22
29
|
# We assume every generated column is virtual, no matter the concrete type
|
23
30
|
@generated.present?
|
@@ -42,12 +49,14 @@ module ActiveRecord
|
|
42
49
|
|
43
50
|
def init_with(coder)
|
44
51
|
@serial = coder["serial"]
|
52
|
+
@identity = coder["identity"]
|
45
53
|
@generated = coder["generated"]
|
46
54
|
super
|
47
55
|
end
|
48
56
|
|
49
57
|
def encode_with(coder)
|
50
58
|
coder["serial"] = @serial
|
59
|
+
coder["identity"] = @identity
|
51
60
|
coder["generated"] = @generated
|
52
61
|
super
|
53
62
|
end
|
@@ -55,6 +64,7 @@ module ActiveRecord
|
|
55
64
|
def ==(other)
|
56
65
|
other.is_a?(Column) &&
|
57
66
|
super &&
|
67
|
+
identity? == other.identity? &&
|
58
68
|
serial? == other.serial?
|
59
69
|
end
|
60
70
|
alias :eql? :==
|
@@ -62,6 +72,7 @@ module ActiveRecord
|
|
62
72
|
def hash
|
63
73
|
Column.hash ^
|
64
74
|
super.hash ^
|
75
|
+
identity?.hash ^
|
65
76
|
serial?.hash
|
66
77
|
end
|
67
78
|
end
|
@@ -4,19 +4,22 @@ 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
14
|
def query(sql, name = nil) # :nodoc:
|
14
|
-
materialize_transactions
|
15
15
|
mark_transaction_written_if_write(sql)
|
16
16
|
|
17
|
-
log(sql, name) do
|
18
|
-
|
19
|
-
|
17
|
+
log(sql, name) do |notification_payload|
|
18
|
+
with_raw_connection do |conn|
|
19
|
+
result = conn.async_exec(sql).map_types!(@type_map_for_results).values
|
20
|
+
verified!
|
21
|
+
notification_payload[:row_count] = result.count
|
22
|
+
result
|
20
23
|
end
|
21
24
|
end
|
22
25
|
end
|
@@ -34,24 +37,33 @@ module ActiveRecord
|
|
34
37
|
|
35
38
|
# Executes an SQL statement, returning a PG::Result object on success
|
36
39
|
# or raising a PG::Error exception otherwise.
|
40
|
+
#
|
41
|
+
# Setting +allow_retry+ to true causes the db to reconnect and retry
|
42
|
+
# executing the SQL statement in case of a connection-related exception.
|
43
|
+
# This option should only be enabled for known idempotent queries.
|
44
|
+
#
|
37
45
|
# Note: the PG::Result object is manually memory managed; if you don't
|
38
46
|
# need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
|
39
|
-
def execute(
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
mark_transaction_written_if_write(sql)
|
47
|
+
def execute(...) # :nodoc:
|
48
|
+
super
|
49
|
+
ensure
|
50
|
+
@notice_receiver_sql_warnings = []
|
51
|
+
end
|
45
52
|
|
46
|
-
|
47
|
-
|
48
|
-
|
53
|
+
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
|
54
|
+
log(sql, name, async: async) do |notification_payload|
|
55
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
56
|
+
result = conn.async_exec(sql)
|
57
|
+
verified!
|
58
|
+
handle_warnings(result)
|
59
|
+
notification_payload[:row_count] = result.count
|
60
|
+
result
|
49
61
|
end
|
50
62
|
end
|
51
63
|
end
|
52
64
|
|
53
|
-
def
|
54
|
-
execute_and_clear(sql, name, binds, prepare: prepare, async: async) do |result|
|
65
|
+
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true) # :nodoc:
|
66
|
+
execute_and_clear(sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |result|
|
55
67
|
types = {}
|
56
68
|
fields = result.fields
|
57
69
|
fields.each_with_index do |fname, i|
|
@@ -59,7 +71,7 @@ module ActiveRecord
|
|
59
71
|
fmod = result.fmod i
|
60
72
|
types[fname] = types[i] = get_oid_type(ftype, fmod, fname)
|
61
73
|
end
|
62
|
-
build_result(columns: fields, rows: result.values, column_types: types)
|
74
|
+
build_result(columns: fields, rows: result.values, column_types: types.freeze)
|
63
75
|
end
|
64
76
|
end
|
65
77
|
|
@@ -68,26 +80,11 @@ module ActiveRecord
|
|
68
80
|
end
|
69
81
|
alias :exec_update :exec_delete
|
70
82
|
|
71
|
-
def
|
72
|
-
if pk.nil?
|
73
|
-
# Extract the table from the insert sql. Yuck.
|
74
|
-
table_ref = extract_table_ref_from_insert_sql(sql)
|
75
|
-
pk = primary_key(table_ref) if table_ref
|
76
|
-
end
|
77
|
-
|
78
|
-
if pk = suppress_composite_primary_key(pk)
|
79
|
-
sql = "#{sql} RETURNING #{quote_column_name(pk)}"
|
80
|
-
end
|
81
|
-
|
82
|
-
super
|
83
|
-
end
|
84
|
-
private :sql_for_insert
|
85
|
-
|
86
|
-
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil) # :nodoc:
|
83
|
+
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) # :nodoc:
|
87
84
|
if use_insert_returning? || pk == false
|
88
85
|
super
|
89
86
|
else
|
90
|
-
result =
|
87
|
+
result = internal_exec_query(sql, name, binds)
|
91
88
|
unless sequence_name
|
92
89
|
table_ref = extract_table_ref_from_insert_sql(sql)
|
93
90
|
if table_ref
|
@@ -103,33 +100,76 @@ module ActiveRecord
|
|
103
100
|
|
104
101
|
# Begins a transaction.
|
105
102
|
def begin_db_transaction # :nodoc:
|
106
|
-
|
103
|
+
internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
107
104
|
end
|
108
105
|
|
109
106
|
def begin_isolated_db_transaction(isolation) # :nodoc:
|
110
|
-
|
111
|
-
execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
|
107
|
+
internal_execute("BEGIN ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
112
108
|
end
|
113
109
|
|
114
110
|
# Commits a transaction.
|
115
111
|
def commit_db_transaction # :nodoc:
|
116
|
-
|
112
|
+
internal_execute("COMMIT", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
117
113
|
end
|
118
114
|
|
119
115
|
# Aborts a transaction.
|
120
116
|
def exec_rollback_db_transaction # :nodoc:
|
121
|
-
|
117
|
+
cancel_any_running_query
|
118
|
+
internal_execute("ROLLBACK", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
119
|
+
end
|
120
|
+
|
121
|
+
def exec_restart_db_transaction # :nodoc:
|
122
|
+
cancel_any_running_query
|
123
|
+
internal_execute("ROLLBACK AND CHAIN", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
122
124
|
end
|
123
125
|
|
124
126
|
# From https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
|
125
|
-
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP").freeze # :nodoc:
|
127
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP", retryable: true).freeze # :nodoc:
|
126
128
|
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
127
129
|
|
128
130
|
def high_precision_current_timestamp
|
129
131
|
HIGH_PRECISION_CURRENT_TIMESTAMP
|
130
132
|
end
|
131
133
|
|
134
|
+
def build_explain_clause(options = [])
|
135
|
+
return "EXPLAIN" if options.empty?
|
136
|
+
|
137
|
+
"EXPLAIN (#{options.join(", ").upcase})"
|
138
|
+
end
|
139
|
+
|
140
|
+
# Set when constraints will be checked for the current transaction.
|
141
|
+
#
|
142
|
+
# Not passing any specific constraint names will set the value for all deferrable constraints.
|
143
|
+
#
|
144
|
+
# [<tt>deferred</tt>]
|
145
|
+
# Valid values are +:deferred+ or +:immediate+.
|
146
|
+
#
|
147
|
+
# See https://www.postgresql.org/docs/current/sql-set-constraints.html
|
148
|
+
def set_constraints(deferred, *constraints)
|
149
|
+
unless %i[deferred immediate].include?(deferred)
|
150
|
+
raise ArgumentError, "deferred must be :deferred or :immediate"
|
151
|
+
end
|
152
|
+
|
153
|
+
constraints = if constraints.empty?
|
154
|
+
"ALL"
|
155
|
+
else
|
156
|
+
constraints.map { |c| quote_table_name(c) }.join(", ")
|
157
|
+
end
|
158
|
+
execute("SET CONSTRAINTS #{constraints} #{deferred.to_s.upcase}")
|
159
|
+
end
|
160
|
+
|
132
161
|
private
|
162
|
+
IDLE_TRANSACTION_STATUSES = [PG::PQTRANS_IDLE, PG::PQTRANS_INTRANS, PG::PQTRANS_INERROR]
|
163
|
+
private_constant :IDLE_TRANSACTION_STATUSES
|
164
|
+
|
165
|
+
def cancel_any_running_query
|
166
|
+
return if @raw_connection.nil? || IDLE_TRANSACTION_STATUSES.include?(@raw_connection.transaction_status)
|
167
|
+
|
168
|
+
@raw_connection.cancel
|
169
|
+
@raw_connection.block
|
170
|
+
rescue PG::Error
|
171
|
+
end
|
172
|
+
|
133
173
|
def execute_batch(statements, name = nil)
|
134
174
|
execute(combine_multi_statements(statements))
|
135
175
|
end
|
@@ -140,12 +180,29 @@ module ActiveRecord
|
|
140
180
|
|
141
181
|
# Returns the current ID of a table's sequence.
|
142
182
|
def last_insert_id_result(sequence_name)
|
143
|
-
|
183
|
+
internal_exec_query("SELECT currval(#{quote(sequence_name)})", "SQL")
|
184
|
+
end
|
185
|
+
|
186
|
+
def returning_column_values(result)
|
187
|
+
result.rows.first
|
144
188
|
end
|
145
189
|
|
146
190
|
def suppress_composite_primary_key(pk)
|
147
191
|
pk unless pk.is_a?(Array)
|
148
192
|
end
|
193
|
+
|
194
|
+
def handle_warnings(sql)
|
195
|
+
@notice_receiver_sql_warnings.each do |warning|
|
196
|
+
next if warning_ignored?(warning)
|
197
|
+
|
198
|
+
warning.sql = sql
|
199
|
+
ActiveRecord.db_warnings_action.call(warning)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def warning_ignored?(warning)
|
204
|
+
["WARNING", "ERROR", "FATAL", "PANIC"].exclude?(warning.level) || super
|
205
|
+
end
|
149
206
|
end
|
150
207
|
end
|
151
208
|
end
|
@@ -28,6 +28,12 @@ module ActiveRecord
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
# TODO: Remove when IPAddr#== compares IPAddr#prefix. See
|
32
|
+
# https://github.com/ruby/ipaddr/issues/21
|
33
|
+
def changed?(old_value, new_value, _new_value_before_type_cast)
|
34
|
+
!old_value.eql?(new_value) || !old_value.nil? && old_value.prefix != new_value.prefix
|
35
|
+
end
|
36
|
+
|
31
37
|
def cast_value(value)
|
32
38
|
if value.nil?
|
33
39
|
nil
|