activerecord 7.1.5.1 → 8.0.2
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 +369 -2484
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +2 -1
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +43 -12
- data/lib/active_record/associations/belongs_to_association.rb +21 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- 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/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- 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/singular_association.rb +14 -3
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +92 -295
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/primary_key.rb +25 -61
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -18
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +63 -49
- data/lib/active_record/autosave_association.rb +92 -57
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +48 -122
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +286 -77
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +119 -55
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +197 -76
- data/lib/active_record/connection_adapters/abstract/quoting.rb +66 -92
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -12
- data/lib/active_record/connection_adapters/abstract/transaction.rb +140 -67
- data/lib/active_record/connection_adapters/abstract_adapter.rb +85 -90
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +71 -52
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +56 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +92 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +13 -31
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -41
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +36 -20
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +75 -28
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +73 -113
- data/lib/active_record/connection_adapters/schema_cache.rb +124 -131
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +81 -97
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +29 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +35 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +183 -87
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +39 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +65 -0
- data/lib/active_record/connection_handling.rb +74 -37
- data/lib/active_record/core.rb +132 -51
- data/lib/active_record/counter_cache.rb +19 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +23 -4
- data/lib/active_record/database_configurations/hash_config.rb +46 -34
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +41 -17
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +7 -7
- data/lib/active_record/encryption/encrypted_attribute_type.rb +33 -4
- data/lib/active_record/encryption/encryptor.rb +28 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- 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 +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +8 -1
- data/lib/active_record/enum.rb +20 -16
- data/lib/active_record/errors.rb +54 -20
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -33
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +19 -16
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/log_subscriber.rb +5 -32
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +33 -14
- data/lib/active_record/migration/compatibility.rb +8 -3
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +104 -98
- data/lib/active_record/model_schema.rb +32 -70
- data/lib/active_record/nested_attributes.rb +15 -9
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +127 -451
- data/lib/active_record/query_cache.rb +19 -8
- data/lib/active_record/query_logs.rb +104 -37
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +24 -12
- data/lib/active_record/railtie.rb +26 -68
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +43 -61
- data/lib/active_record/reflection.rb +112 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +138 -72
- data/lib/active_record/relation/calculations.rb +122 -82
- data/lib/active_record/relation/delegation.rb +30 -22
- data/lib/active_record/relation/finder_methods.rb +32 -18
- data/lib/active_record/relation/merger.rb +12 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +10 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +16 -3
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +317 -101
- data/lib/active_record/relation/spawn_methods.rb +3 -19
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +561 -119
- data/lib/active_record/result.rb +95 -46
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +31 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +53 -20
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/signed_id.rb +24 -4
- data/lib/active_record/statement_cache.rb +19 -19
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/table_metadata.rb +2 -13
- data/lib/active_record/tasks/database_tasks.rb +87 -58
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -3
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +4 -3
- data/lib/active_record/test_fixtures.rb +98 -89
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +72 -17
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +138 -57
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +4 -2
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +2 -2
- data/lib/arel/collectors/substitute_binds.rb +3 -3
- data/lib/arel/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +5 -4
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +29 -16
- data/lib/arel.rb +7 -3
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +18 -16
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
@@ -6,67 +6,26 @@ module ActiveRecord
|
|
6
6
|
module DatabaseStatements
|
7
7
|
# Returns an ActiveRecord::Result instance.
|
8
8
|
def select_all(*, **) # :nodoc:
|
9
|
-
|
10
|
-
|
11
|
-
result = if ExplainRegistry.collect? && prepared_statements
|
12
|
-
unprepared_statement { super }
|
13
|
-
else
|
14
|
-
super
|
15
|
-
end
|
16
|
-
conn.abandon_results!
|
17
|
-
end
|
18
|
-
result
|
19
|
-
end
|
20
|
-
|
21
|
-
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
|
22
|
-
if without_prepared_statement?(binds)
|
23
|
-
execute_and_free(sql, name, async: async) do |result|
|
24
|
-
if result
|
25
|
-
build_result(columns: result.fields, rows: result.to_a)
|
26
|
-
else
|
27
|
-
build_result(columns: [], rows: [])
|
28
|
-
end
|
29
|
-
end
|
9
|
+
if ExplainRegistry.collect? && prepared_statements
|
10
|
+
unprepared_statement { super }
|
30
11
|
else
|
31
|
-
|
32
|
-
if result
|
33
|
-
build_result(columns: result.fields, rows: result.to_a)
|
34
|
-
else
|
35
|
-
build_result(columns: [], rows: [])
|
36
|
-
end
|
37
|
-
end
|
12
|
+
super
|
38
13
|
end
|
39
14
|
end
|
40
15
|
|
41
|
-
def exec_delete(sql, name = nil, binds = []) # :nodoc:
|
42
|
-
if without_prepared_statement?(binds)
|
43
|
-
with_raw_connection do |conn|
|
44
|
-
@affected_rows_before_warnings = nil
|
45
|
-
execute_and_free(sql, name) { @affected_rows_before_warnings || conn.affected_rows }
|
46
|
-
end
|
47
|
-
else
|
48
|
-
exec_stmt_and_free(sql, name, binds) { |stmt| stmt.affected_rows }
|
49
|
-
end
|
50
|
-
end
|
51
|
-
alias :exec_update :exec_delete
|
52
|
-
|
53
16
|
private
|
54
|
-
def
|
55
|
-
raw_connection.query_options[:database_timezone] = default_timezone
|
56
|
-
end
|
57
|
-
|
58
|
-
def execute_batch(statements, name = nil)
|
59
|
-
statements = statements.map { |sql| transform_query(sql) }
|
17
|
+
def execute_batch(statements, name = nil, **kwargs)
|
60
18
|
combine_multi_statements(statements).each do |statement|
|
61
|
-
|
62
|
-
raw_execute(statement, name)
|
63
|
-
conn.abandon_results!
|
64
|
-
end
|
19
|
+
raw_execute(statement, name, batch: true, **kwargs)
|
65
20
|
end
|
66
21
|
end
|
67
22
|
|
68
23
|
def last_inserted_id(result)
|
69
|
-
|
24
|
+
if supports_insert_returning?
|
25
|
+
super
|
26
|
+
else
|
27
|
+
@raw_connection&.last_id
|
28
|
+
end
|
70
29
|
end
|
71
30
|
|
72
31
|
def multi_statements_enabled?
|
@@ -79,70 +38,102 @@ module ActiveRecord
|
|
79
38
|
end
|
80
39
|
end
|
81
40
|
|
82
|
-
def
|
83
|
-
if multi_statements_enabled?
|
84
|
-
|
41
|
+
def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false)
|
42
|
+
reset_multi_statement = if batch && !multi_statements_enabled?
|
43
|
+
raw_connection.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
|
44
|
+
true
|
85
45
|
end
|
86
46
|
|
87
|
-
|
88
|
-
|
47
|
+
# Make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
48
|
+
# made since we established the connection
|
49
|
+
raw_connection.query_options[:database_timezone] = default_timezone
|
50
|
+
|
51
|
+
result = nil
|
52
|
+
if binds.nil? || binds.empty?
|
53
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
54
|
+
result = raw_connection.query(sql)
|
55
|
+
# Ref: https://github.com/brianmario/mysql2/pull/1383
|
56
|
+
# As of mysql2 0.5.6 `#affected_rows` might raise Mysql2::Error if a prepared statement
|
57
|
+
# from that same connection was GCed while `#query` released the GVL.
|
58
|
+
# By avoiding to call `#affected_rows` when we have a result, we reduce the likeliness
|
59
|
+
# of hitting the bug.
|
60
|
+
@affected_rows_before_warnings = result&.size || raw_connection.affected_rows
|
61
|
+
end
|
62
|
+
elsif prepare
|
63
|
+
stmt = @statements[sql] ||= raw_connection.prepare(sql)
|
64
|
+
begin
|
65
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
66
|
+
result = stmt.execute(*type_casted_binds)
|
67
|
+
@affected_rows_before_warnings = stmt.affected_rows
|
68
|
+
end
|
69
|
+
rescue ::Mysql2::Error
|
70
|
+
@statements.delete(sql)
|
71
|
+
raise
|
72
|
+
end
|
73
|
+
else
|
74
|
+
stmt = raw_connection.prepare(sql)
|
89
75
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
76
|
+
begin
|
77
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
78
|
+
result = stmt.execute(*type_casted_binds)
|
79
|
+
@affected_rows_before_warnings = stmt.affected_rows
|
80
|
+
end
|
95
81
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
result
|
101
|
-
|
102
|
-
|
103
|
-
|
82
|
+
# Ref: https://github.com/brianmario/mysql2/pull/1383
|
83
|
+
# by eagerly closing uncached prepared statements, we also reduce the chances of
|
84
|
+
# that bug happening. It can still happen if `#execute` is used as we have no callback
|
85
|
+
# to eagerly close the statement.
|
86
|
+
if result
|
87
|
+
result.instance_variable_set(:@_ar_stmt_to_close, stmt)
|
88
|
+
else
|
89
|
+
stmt.close
|
90
|
+
end
|
91
|
+
rescue ::Mysql2::Error
|
92
|
+
stmt.close
|
93
|
+
raise
|
104
94
|
end
|
105
95
|
end
|
96
|
+
|
97
|
+
notification_payload[:affected_rows] = @affected_rows_before_warnings
|
98
|
+
notification_payload[:row_count] = result&.size || 0
|
99
|
+
|
100
|
+
raw_connection.abandon_results!
|
101
|
+
|
102
|
+
verified!
|
103
|
+
handle_warnings(sql)
|
104
|
+
result
|
105
|
+
ensure
|
106
|
+
if reset_multi_statement && active?
|
107
|
+
raw_connection.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
|
108
|
+
end
|
106
109
|
end
|
107
110
|
|
108
|
-
def
|
109
|
-
|
110
|
-
check_if_write_query(sql)
|
111
|
+
def cast_result(raw_result)
|
112
|
+
return ActiveRecord::Result.empty if raw_result.nil?
|
111
113
|
|
112
|
-
|
114
|
+
fields = raw_result.fields
|
113
115
|
|
114
|
-
|
116
|
+
result = if fields.empty?
|
117
|
+
ActiveRecord::Result.empty
|
118
|
+
else
|
119
|
+
ActiveRecord::Result.new(fields, raw_result.to_a)
|
120
|
+
end
|
115
121
|
|
116
|
-
|
117
|
-
with_raw_connection do |conn|
|
118
|
-
sync_timezone_changes(conn)
|
122
|
+
free_raw_result(raw_result)
|
119
123
|
|
120
|
-
|
121
|
-
|
122
|
-
else
|
123
|
-
stmt = conn.prepare(sql)
|
124
|
-
end
|
124
|
+
result
|
125
|
+
end
|
125
126
|
|
126
|
-
|
127
|
-
|
128
|
-
stmt.execute(*type_casted_binds)
|
129
|
-
end
|
130
|
-
verified!
|
131
|
-
result
|
132
|
-
rescue ::Mysql2::Error => e
|
133
|
-
if cache_stmt
|
134
|
-
@statements.delete(sql)
|
135
|
-
else
|
136
|
-
stmt.close
|
137
|
-
end
|
138
|
-
raise e
|
139
|
-
end
|
127
|
+
def affected_rows(raw_result)
|
128
|
+
free_raw_result(raw_result) if raw_result
|
140
129
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
130
|
+
@affected_rows_before_warnings
|
131
|
+
end
|
132
|
+
|
133
|
+
def free_raw_result(raw_result)
|
134
|
+
raw_result.free
|
135
|
+
if stmt = raw_result.instance_variable_get(:@_ar_stmt_to_close)
|
136
|
+
stmt.close
|
146
137
|
end
|
147
138
|
end
|
148
139
|
end
|
@@ -7,17 +7,6 @@ gem "mysql2", "~> 0.5"
|
|
7
7
|
require "mysql2"
|
8
8
|
|
9
9
|
module ActiveRecord
|
10
|
-
module ConnectionHandling # :nodoc:
|
11
|
-
def mysql2_adapter_class
|
12
|
-
ConnectionAdapters::Mysql2Adapter
|
13
|
-
end
|
14
|
-
|
15
|
-
# Establishes a connection to the database that's used by all Active Record objects.
|
16
|
-
def mysql2_connection(config)
|
17
|
-
mysql2_adapter_class.new(config)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
10
|
module ConnectionAdapters
|
22
11
|
# = Active Record MySQL2 Adapter
|
23
12
|
class Mysql2Adapter < AbstractMysqlAdapter
|
@@ -66,6 +55,7 @@ module ActiveRecord
|
|
66
55
|
def initialize(...)
|
67
56
|
super
|
68
57
|
|
58
|
+
@affected_rows_before_warnings = nil
|
69
59
|
@config[:flags] ||= 0
|
70
60
|
|
71
61
|
if @config[:flags].kind_of? Array
|
@@ -103,35 +93,27 @@ module ActiveRecord
|
|
103
93
|
|
104
94
|
# HELPER METHODS ===========================================
|
105
95
|
|
106
|
-
def each_hash(result, &block) # :nodoc:
|
107
|
-
if block_given?
|
108
|
-
result.each(as: :hash, symbolize_keys: true, &block)
|
109
|
-
else
|
110
|
-
to_enum(:each_hash, result)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
96
|
def error_number(exception)
|
115
97
|
exception.error_number if exception.respond_to?(:error_number)
|
116
98
|
end
|
117
99
|
|
118
100
|
#--
|
119
|
-
#
|
101
|
+
# CONNECTION MANAGEMENT ====================================
|
120
102
|
#++
|
121
103
|
|
122
|
-
|
123
|
-
|
124
|
-
with_raw_connection(allow_retry: true, materialize_transactions: false) do |connection|
|
125
|
-
connection.escape(string)
|
126
|
-
end
|
104
|
+
def connected?
|
105
|
+
!(@raw_connection.nil? || @raw_connection.closed?)
|
127
106
|
end
|
128
107
|
|
129
|
-
#--
|
130
|
-
# CONNECTION MANAGEMENT ====================================
|
131
|
-
#++
|
132
|
-
|
133
108
|
def active?
|
134
|
-
|
109
|
+
if connected?
|
110
|
+
@lock.synchronize do
|
111
|
+
if @raw_connection&.ping
|
112
|
+
verified!
|
113
|
+
true
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end || false
|
135
117
|
end
|
136
118
|
|
137
119
|
alias :reset! :reconnect!
|
@@ -180,7 +162,7 @@ module ActiveRecord
|
|
180
162
|
end
|
181
163
|
|
182
164
|
def full_version
|
183
|
-
|
165
|
+
database_version.full_version_string
|
184
166
|
end
|
185
167
|
|
186
168
|
def get_full_version
|
@@ -3,11 +3,10 @@
|
|
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, :role, :shard
|
9
|
-
attr_writer :schema_reflection
|
10
|
-
attr_accessor :connection_class
|
8
|
+
attr_reader :db_config, :role, :shard, :connection_descriptor
|
9
|
+
attr_writer :schema_reflection, :server_version
|
11
10
|
|
12
11
|
def schema_reflection
|
13
12
|
@schema_reflection ||= SchemaReflection.new(db_config.lazy_schema_cache_path)
|
@@ -28,7 +27,8 @@ module ActiveRecord
|
|
28
27
|
|
29
28
|
def initialize(connection_class, db_config, role, shard)
|
30
29
|
super()
|
31
|
-
@
|
30
|
+
@server_version = nil
|
31
|
+
self.connection_descriptor = connection_class
|
32
32
|
@db_config = db_config
|
33
33
|
@role = role
|
34
34
|
@shard = shard
|
@@ -36,17 +36,20 @@ module ActiveRecord
|
|
36
36
|
INSTANCES[self] = self
|
37
37
|
end
|
38
38
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
39
|
+
def server_version(connection)
|
40
|
+
@server_version || synchronize { @server_version ||= connection.get_database_version }
|
41
|
+
end
|
42
|
+
|
43
|
+
def connection_descriptor=(connection_descriptor)
|
44
|
+
case connection_descriptor
|
45
|
+
when ConnectionHandler::ConnectionDescriptor
|
46
|
+
@connection_descriptor = connection_descriptor
|
42
47
|
else
|
43
|
-
|
48
|
+
@connection_descriptor = ConnectionHandler::ConnectionDescriptor.new(connection_descriptor.name, connection_descriptor.primary_class?)
|
44
49
|
end
|
45
50
|
end
|
46
51
|
|
47
52
|
def disconnect!(automatic_reconnect: false)
|
48
|
-
ActiveSupport::ForkTracker.check!
|
49
|
-
|
50
53
|
return unless @pool
|
51
54
|
|
52
55
|
synchronize do
|
@@ -60,8 +63,6 @@ module ActiveRecord
|
|
60
63
|
end
|
61
64
|
|
62
65
|
def pool
|
63
|
-
ActiveSupport::ForkTracker.check!
|
64
|
-
|
65
66
|
@pool || synchronize { @pool ||= ConnectionAdapters::ConnectionPool.new(self) }
|
66
67
|
end
|
67
68
|
|
@@ -12,15 +12,8 @@ module ActiveRecord
|
|
12
12
|
|
13
13
|
# Queries the database and returns the results in an Array-like object
|
14
14
|
def query(sql, name = nil) # :nodoc:
|
15
|
-
|
16
|
-
|
17
|
-
log(sql, name) do
|
18
|
-
with_raw_connection do |conn|
|
19
|
-
result = conn.async_exec(sql).map_types!(@type_map_for_results).values
|
20
|
-
verified!
|
21
|
-
result
|
22
|
-
end
|
23
|
-
end
|
15
|
+
result = internal_execute(sql, name)
|
16
|
+
result.map_types!(@type_map_for_results).values
|
24
17
|
end
|
25
18
|
|
26
19
|
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
|
@@ -49,35 +42,6 @@ module ActiveRecord
|
|
49
42
|
@notice_receiver_sql_warnings = []
|
50
43
|
end
|
51
44
|
|
52
|
-
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
|
53
|
-
log(sql, name, async: async) do
|
54
|
-
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
55
|
-
result = conn.async_exec(sql)
|
56
|
-
verified!
|
57
|
-
handle_warnings(result)
|
58
|
-
result
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true) # :nodoc:
|
64
|
-
execute_and_clear(sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |result|
|
65
|
-
types = {}
|
66
|
-
fields = result.fields
|
67
|
-
fields.each_with_index do |fname, i|
|
68
|
-
ftype = result.ftype i
|
69
|
-
fmod = result.fmod i
|
70
|
-
types[fname] = types[i] = get_oid_type(ftype, fmod, fname)
|
71
|
-
end
|
72
|
-
build_result(columns: fields, rows: result.values, column_types: types)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def exec_delete(sql, name = nil, binds = []) # :nodoc:
|
77
|
-
execute_and_clear(sql, name, binds) { |result| result.cmd_tuples }
|
78
|
-
end
|
79
|
-
alias :exec_update :exec_delete
|
80
|
-
|
81
45
|
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) # :nodoc:
|
82
46
|
if use_insert_returning? || pk == false
|
83
47
|
super
|
@@ -122,7 +86,7 @@ module ActiveRecord
|
|
122
86
|
end
|
123
87
|
|
124
88
|
# From https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
|
125
|
-
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP").freeze # :nodoc:
|
89
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP", retryable: true).freeze # :nodoc:
|
126
90
|
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
127
91
|
|
128
92
|
def high_precision_current_timestamp
|
@@ -135,6 +99,27 @@ module ActiveRecord
|
|
135
99
|
"EXPLAIN (#{options.join(", ").upcase})"
|
136
100
|
end
|
137
101
|
|
102
|
+
# Set when constraints will be checked for the current transaction.
|
103
|
+
#
|
104
|
+
# Not passing any specific constraint names will set the value for all deferrable constraints.
|
105
|
+
#
|
106
|
+
# [<tt>deferred</tt>]
|
107
|
+
# Valid values are +:deferred+ or +:immediate+.
|
108
|
+
#
|
109
|
+
# See https://www.postgresql.org/docs/current/sql-set-constraints.html
|
110
|
+
def set_constraints(deferred, *constraints)
|
111
|
+
unless %i[deferred immediate].include?(deferred)
|
112
|
+
raise ArgumentError, "deferred must be :deferred or :immediate"
|
113
|
+
end
|
114
|
+
|
115
|
+
constraints = if constraints.empty?
|
116
|
+
"ALL"
|
117
|
+
else
|
118
|
+
constraints.map { |c| quote_table_name(c) }.join(", ")
|
119
|
+
end
|
120
|
+
execute("SET CONSTRAINTS #{constraints} #{deferred.to_s.upcase}")
|
121
|
+
end
|
122
|
+
|
138
123
|
private
|
139
124
|
IDLE_TRANSACTION_STATUSES = [PG::PQTRANS_IDLE, PG::PQTRANS_INTRANS, PG::PQTRANS_INERROR]
|
140
125
|
private_constant :IDLE_TRANSACTION_STATUSES
|
@@ -147,8 +132,68 @@ module ActiveRecord
|
|
147
132
|
rescue PG::Error
|
148
133
|
end
|
149
134
|
|
150
|
-
def
|
151
|
-
|
135
|
+
def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false)
|
136
|
+
update_typemap_for_default_timezone
|
137
|
+
result = if prepare
|
138
|
+
begin
|
139
|
+
stmt_key = prepare_statement(sql, binds, raw_connection)
|
140
|
+
notification_payload[:statement_name] = stmt_key
|
141
|
+
raw_connection.exec_prepared(stmt_key, type_casted_binds)
|
142
|
+
rescue PG::FeatureNotSupported => error
|
143
|
+
if is_cached_plan_failure?(error)
|
144
|
+
# Nothing we can do if we are in a transaction because all commands
|
145
|
+
# will raise InFailedSQLTransaction
|
146
|
+
if in_transaction?
|
147
|
+
raise PreparedStatementCacheExpired.new(error.message, connection_pool: @pool)
|
148
|
+
else
|
149
|
+
@lock.synchronize do
|
150
|
+
# outside of transactions we can simply flush this query and retry
|
151
|
+
@statements.delete sql_key(sql)
|
152
|
+
end
|
153
|
+
retry
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
raise
|
158
|
+
end
|
159
|
+
elsif binds.nil? || binds.empty?
|
160
|
+
raw_connection.async_exec(sql)
|
161
|
+
else
|
162
|
+
raw_connection.exec_params(sql, type_casted_binds)
|
163
|
+
end
|
164
|
+
|
165
|
+
verified!
|
166
|
+
handle_warnings(result)
|
167
|
+
notification_payload[:row_count] = result.count
|
168
|
+
result
|
169
|
+
end
|
170
|
+
|
171
|
+
def cast_result(result)
|
172
|
+
if result.fields.empty?
|
173
|
+
result.clear
|
174
|
+
return ActiveRecord::Result.empty
|
175
|
+
end
|
176
|
+
|
177
|
+
types = {}
|
178
|
+
fields = result.fields
|
179
|
+
fields.each_with_index do |fname, i|
|
180
|
+
ftype = result.ftype i
|
181
|
+
fmod = result.fmod i
|
182
|
+
types[fname] = types[i] = get_oid_type(ftype, fmod, fname)
|
183
|
+
end
|
184
|
+
ar_result = ActiveRecord::Result.new(fields, result.values, types.freeze)
|
185
|
+
result.clear
|
186
|
+
ar_result
|
187
|
+
end
|
188
|
+
|
189
|
+
def affected_rows(result)
|
190
|
+
affected_rows = result.cmd_tuples
|
191
|
+
result.clear
|
192
|
+
affected_rows
|
193
|
+
end
|
194
|
+
|
195
|
+
def execute_batch(statements, name = nil, **kwargs)
|
196
|
+
raw_execute(combine_multi_statements(statements), name, batch: true, **kwargs)
|
152
197
|
end
|
153
198
|
|
154
199
|
def build_truncate_statements(table_names)
|
@@ -33,7 +33,7 @@ module ActiveRecord
|
|
33
33
|
when ::Numeric
|
34
34
|
# Sometimes operations on Times returns just float number of seconds so we need to handle that.
|
35
35
|
# Example: Time.current - (Time.current + 1.hour) # => -3600.000001776 (Float)
|
36
|
-
value.
|
36
|
+
ActiveSupport::Duration.build(value).iso8601(precision: self.precision)
|
37
37
|
else
|
38
38
|
super
|
39
39
|
end
|
@@ -25,6 +25,10 @@ module ActiveRecord
|
|
25
25
|
build_point(x, y)
|
26
26
|
when ::Array
|
27
27
|
build_point(*value)
|
28
|
+
when ::Hash
|
29
|
+
return if value.blank?
|
30
|
+
|
31
|
+
build_point(*values_array_from_hash(value))
|
28
32
|
else
|
29
33
|
value
|
30
34
|
end
|
@@ -36,6 +40,8 @@ module ActiveRecord
|
|
36
40
|
"(#{number_for_point(value.x)},#{number_for_point(value.y)})"
|
37
41
|
when ::Array
|
38
42
|
serialize(build_point(*value))
|
43
|
+
when ::Hash
|
44
|
+
serialize(build_point(*values_array_from_hash(value)))
|
39
45
|
else
|
40
46
|
super
|
41
47
|
end
|
@@ -57,6 +63,10 @@ module ActiveRecord
|
|
57
63
|
def build_point(x, y)
|
58
64
|
ActiveRecord::Point.new(Float(x), Float(y))
|
59
65
|
end
|
66
|
+
|
67
|
+
def values_array_from_hash(value)
|
68
|
+
[value.values_at(:x, "x").compact.first, value.values_at(:y, "y").compact.first]
|
69
|
+
end
|
60
70
|
end
|
61
71
|
end
|
62
72
|
end
|
@@ -6,6 +6,7 @@ module ActiveRecord
|
|
6
6
|
module OID # :nodoc:
|
7
7
|
class Uuid < Type::Value # :nodoc:
|
8
8
|
ACCEPTABLE_UUID = %r{\A(\{)?([a-fA-F0-9]{4}-?){8}(?(1)\}|)\z}
|
9
|
+
CANONICAL_UUID = %r{\A[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}\z}
|
9
10
|
|
10
11
|
alias :serialize :deserialize
|
11
12
|
|
@@ -15,18 +16,27 @@ module ActiveRecord
|
|
15
16
|
|
16
17
|
def changed?(old_value, new_value, _new_value_before_type_cast)
|
17
18
|
old_value.class != new_value.class ||
|
18
|
-
new_value
|
19
|
+
new_value != old_value
|
19
20
|
end
|
20
21
|
|
21
22
|
def changed_in_place?(raw_old_value, new_value)
|
22
23
|
raw_old_value.class != new_value.class ||
|
23
|
-
new_value
|
24
|
+
new_value != raw_old_value
|
24
25
|
end
|
25
26
|
|
26
27
|
private
|
27
28
|
def cast_value(value)
|
28
|
-
|
29
|
-
|
29
|
+
value = value.to_s
|
30
|
+
format_uuid(value) if value.match?(ACCEPTABLE_UUID)
|
31
|
+
end
|
32
|
+
|
33
|
+
def format_uuid(uuid)
|
34
|
+
if uuid.match?(CANONICAL_UUID)
|
35
|
+
uuid
|
36
|
+
else
|
37
|
+
uuid = uuid.delete("{}-").downcase
|
38
|
+
"#{uuid[..7]}-#{uuid[8..11]}-#{uuid[12..15]}-#{uuid[16..19]}-#{uuid[20..]}"
|
39
|
+
end
|
30
40
|
end
|
31
41
|
end
|
32
42
|
end
|