activerecord 5.2.1.1 → 6.0.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +738 -445
- data/MIT-LICENSE +3 -1
- data/README.rdoc +4 -2
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +9 -2
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/association_relation.rb +18 -9
- data/lib/active_record/associations.rb +20 -15
- data/lib/active_record/associations/association.rb +69 -20
- data/lib/active_record/associations/association_scope.rb +4 -6
- data/lib/active_record/associations/belongs_to_association.rb +36 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +19 -52
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +15 -29
- data/lib/active_record/associations/collection_proxy.rb +19 -48
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +11 -10
- data/lib/active_record/associations/has_many_through_association.rb +42 -25
- data/lib/active_record/associations/has_one_association.rb +28 -30
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +28 -28
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -7
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +39 -31
- data/lib/active_record/associations/preloader/association.rb +38 -36
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods.rb +28 -100
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +114 -38
- data/lib/active_record/attribute_methods/primary_key.rb +15 -22
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +15 -53
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods/write.rb +17 -24
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +27 -13
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +6 -20
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +140 -27
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +22 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +116 -127
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +26 -11
- data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +135 -56
- data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
- data/lib/active_record/connection_adapters/abstract_adapter.rb +189 -43
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +151 -198
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +55 -45
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +9 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +75 -13
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +65 -77
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
- data/lib/active_record/connection_adapters/postgresql/utils.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +172 -74
- data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +131 -143
- data/lib/active_record/connection_handling.rb +155 -26
- data/lib/active_record/core.rb +104 -59
- data/lib/active_record/counter_cache.rb +4 -29
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +38 -7
- data/lib/active_record/errors.rb +30 -16
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +145 -472
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +13 -3
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +68 -16
- data/lib/active_record/internal_metadata.rb +10 -2
- data/lib/active_record/locking/optimistic.rb +5 -6
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +100 -81
- data/lib/active_record/migration/command_recorder.rb +50 -6
- data/lib/active_record/migration/compatibility.rb +91 -64
- data/lib/active_record/model_schema.rb +34 -10
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +233 -28
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +33 -21
- data/lib/active_record/railtie.rb +81 -46
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +196 -46
- data/lib/active_record/reflection.rb +42 -44
- data/lib/active_record/relation.rb +320 -70
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +67 -57
- data/lib/active_record/relation/delegation.rb +48 -35
- data/lib/active_record/relation/finder_methods.rb +30 -30
- data/lib/active_record/relation/merger.rb +19 -25
- data/lib/active_record/relation/predicate_builder.rb +18 -15
- data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -6
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/query_attribute.rb +17 -10
- data/lib/active_record/relation/query_methods.rb +236 -73
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +14 -10
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/result.rb +30 -11
- data/lib/active_record/sanitization.rb +32 -40
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +22 -7
- data/lib/active_record/schema_migration.rb +5 -1
- data/lib/active_record/scoping.rb +8 -8
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +21 -15
- data/lib/active_record/statement_cache.rb +32 -5
- data/lib/active_record/store.rb +87 -8
- data/lib/active_record/table_metadata.rb +10 -17
- data/lib/active_record/tasks/database_tasks.rb +195 -26
- data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +39 -25
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +57 -66
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type.rb +3 -4
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type_caster/connection.rb +15 -14
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +15 -27
- data/lib/arel.rb +58 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +111 -27
- data/lib/active_record/collection_cache_key.rb +0 -53
@@ -40,24 +40,6 @@ module ActiveRecord
|
|
40
40
|
committed? || rolledback?
|
41
41
|
end
|
42
42
|
|
43
|
-
def set_state(state)
|
44
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
45
|
-
The set_state method is deprecated and will be removed in
|
46
|
-
Rails 6.0. Please use rollback! or commit! to set transaction
|
47
|
-
state directly.
|
48
|
-
MSG
|
49
|
-
case state
|
50
|
-
when :rolledback
|
51
|
-
rollback!
|
52
|
-
when :committed
|
53
|
-
commit!
|
54
|
-
when nil
|
55
|
-
nullify!
|
56
|
-
else
|
57
|
-
raise ArgumentError, "Invalid transaction state: #{state}"
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
43
|
def rollback!
|
62
44
|
@children.each { |c| c.rollback! }
|
63
45
|
@state = :rolledback
|
@@ -91,13 +73,14 @@ module ActiveRecord
|
|
91
73
|
end
|
92
74
|
|
93
75
|
class Transaction #:nodoc:
|
94
|
-
attr_reader :connection, :state, :records, :savepoint_name
|
95
|
-
attr_writer :joinable
|
76
|
+
attr_reader :connection, :state, :records, :savepoint_name, :isolation_level
|
96
77
|
|
97
78
|
def initialize(connection, options, run_commit_callbacks: false)
|
98
79
|
@connection = connection
|
99
80
|
@state = TransactionState.new
|
100
81
|
@records = []
|
82
|
+
@isolation_level = options[:isolation]
|
83
|
+
@materialized = false
|
101
84
|
@joinable = options.fetch(:joinable, true)
|
102
85
|
@run_commit_callbacks = run_commit_callbacks
|
103
86
|
end
|
@@ -106,10 +89,22 @@ module ActiveRecord
|
|
106
89
|
records << record
|
107
90
|
end
|
108
91
|
|
92
|
+
def materialize!
|
93
|
+
@materialized = true
|
94
|
+
end
|
95
|
+
|
96
|
+
def materialized?
|
97
|
+
@materialized
|
98
|
+
end
|
99
|
+
|
109
100
|
def rollback_records
|
110
|
-
ite = records.uniq
|
101
|
+
ite = records.uniq(&:object_id)
|
102
|
+
already_run_callbacks = {}
|
111
103
|
while record = ite.shift
|
112
|
-
record.
|
104
|
+
trigger_callbacks = record.trigger_transactional_callbacks?
|
105
|
+
should_run_callbacks = !already_run_callbacks[record] && trigger_callbacks
|
106
|
+
already_run_callbacks[record] ||= trigger_callbacks
|
107
|
+
record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
|
113
108
|
end
|
114
109
|
ensure
|
115
110
|
ite.each do |i|
|
@@ -122,13 +117,17 @@ module ActiveRecord
|
|
122
117
|
end
|
123
118
|
|
124
119
|
def commit_records
|
125
|
-
ite = records.uniq
|
120
|
+
ite = records.uniq(&:object_id)
|
121
|
+
already_run_callbacks = {}
|
126
122
|
while record = ite.shift
|
127
123
|
if @run_commit_callbacks
|
128
|
-
record.
|
124
|
+
trigger_callbacks = record.trigger_transactional_callbacks?
|
125
|
+
should_run_callbacks = !already_run_callbacks[record] && trigger_callbacks
|
126
|
+
already_run_callbacks[record] ||= trigger_callbacks
|
127
|
+
record.committed!(should_run_callbacks: should_run_callbacks)
|
129
128
|
else
|
130
129
|
# if not running callbacks, only adds the record to the parent transaction
|
131
|
-
record
|
130
|
+
connection.add_transaction_record(record)
|
132
131
|
end
|
133
132
|
end
|
134
133
|
ensure
|
@@ -142,24 +141,30 @@ module ActiveRecord
|
|
142
141
|
end
|
143
142
|
|
144
143
|
class SavepointTransaction < Transaction
|
145
|
-
def initialize(connection, savepoint_name, parent_transaction,
|
146
|
-
super(connection,
|
144
|
+
def initialize(connection, savepoint_name, parent_transaction, *args)
|
145
|
+
super(connection, *args)
|
147
146
|
|
148
147
|
parent_transaction.state.add_child(@state)
|
149
148
|
|
150
|
-
if
|
149
|
+
if isolation_level
|
151
150
|
raise ActiveRecord::TransactionIsolationError, "cannot set transaction isolation in a nested transaction"
|
152
151
|
end
|
153
|
-
|
152
|
+
|
153
|
+
@savepoint_name = savepoint_name
|
154
|
+
end
|
155
|
+
|
156
|
+
def materialize!
|
157
|
+
connection.create_savepoint(savepoint_name)
|
158
|
+
super
|
154
159
|
end
|
155
160
|
|
156
161
|
def rollback
|
157
|
-
connection.rollback_to_savepoint(savepoint_name)
|
162
|
+
connection.rollback_to_savepoint(savepoint_name) if materialized?
|
158
163
|
@state.rollback!
|
159
164
|
end
|
160
165
|
|
161
166
|
def commit
|
162
|
-
connection.release_savepoint(savepoint_name)
|
167
|
+
connection.release_savepoint(savepoint_name) if materialized?
|
163
168
|
@state.commit!
|
164
169
|
end
|
165
170
|
|
@@ -167,22 +172,23 @@ module ActiveRecord
|
|
167
172
|
end
|
168
173
|
|
169
174
|
class RealTransaction < Transaction
|
170
|
-
def
|
171
|
-
|
172
|
-
|
173
|
-
connection.begin_isolated_db_transaction(options[:isolation])
|
175
|
+
def materialize!
|
176
|
+
if isolation_level
|
177
|
+
connection.begin_isolated_db_transaction(isolation_level)
|
174
178
|
else
|
175
179
|
connection.begin_db_transaction
|
176
180
|
end
|
181
|
+
|
182
|
+
super
|
177
183
|
end
|
178
184
|
|
179
185
|
def rollback
|
180
|
-
connection.rollback_db_transaction
|
186
|
+
connection.rollback_db_transaction if materialized?
|
181
187
|
@state.full_rollback!
|
182
188
|
end
|
183
189
|
|
184
190
|
def commit
|
185
|
-
connection.commit_db_transaction
|
191
|
+
connection.commit_db_transaction if materialized?
|
186
192
|
@state.full_commit!
|
187
193
|
end
|
188
194
|
end
|
@@ -191,6 +197,9 @@ module ActiveRecord
|
|
191
197
|
def initialize(connection)
|
192
198
|
@stack = []
|
193
199
|
@connection = connection
|
200
|
+
@has_unmaterialized_transactions = false
|
201
|
+
@materializing_transactions = false
|
202
|
+
@lazy_transactions_enabled = true
|
194
203
|
end
|
195
204
|
|
196
205
|
def begin_transaction(options = {})
|
@@ -204,11 +213,44 @@ module ActiveRecord
|
|
204
213
|
run_commit_callbacks: run_commit_callbacks)
|
205
214
|
end
|
206
215
|
|
216
|
+
if @connection.supports_lazy_transactions? && lazy_transactions_enabled? && options[:_lazy] != false
|
217
|
+
@has_unmaterialized_transactions = true
|
218
|
+
else
|
219
|
+
transaction.materialize!
|
220
|
+
end
|
207
221
|
@stack.push(transaction)
|
208
222
|
transaction
|
209
223
|
end
|
210
224
|
end
|
211
225
|
|
226
|
+
def disable_lazy_transactions!
|
227
|
+
materialize_transactions
|
228
|
+
@lazy_transactions_enabled = false
|
229
|
+
end
|
230
|
+
|
231
|
+
def enable_lazy_transactions!
|
232
|
+
@lazy_transactions_enabled = true
|
233
|
+
end
|
234
|
+
|
235
|
+
def lazy_transactions_enabled?
|
236
|
+
@lazy_transactions_enabled
|
237
|
+
end
|
238
|
+
|
239
|
+
def materialize_transactions
|
240
|
+
return if @materializing_transactions
|
241
|
+
return unless @has_unmaterialized_transactions
|
242
|
+
|
243
|
+
@connection.lock.synchronize do
|
244
|
+
begin
|
245
|
+
@materializing_transactions = true
|
246
|
+
@stack.each { |t| t.materialize! unless t.materialized? }
|
247
|
+
ensure
|
248
|
+
@materializing_transactions = false
|
249
|
+
end
|
250
|
+
@has_unmaterialized_transactions = false
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
212
254
|
def commit_transaction
|
213
255
|
@connection.lock.synchronize do
|
214
256
|
transaction = @stack.last
|
@@ -234,26 +276,24 @@ module ActiveRecord
|
|
234
276
|
|
235
277
|
def within_new_transaction(options = {})
|
236
278
|
@connection.lock.synchronize do
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
279
|
+
transaction = begin_transaction options
|
280
|
+
yield
|
281
|
+
rescue Exception => error
|
282
|
+
if transaction
|
283
|
+
rollback_transaction
|
284
|
+
after_failure_actions(transaction, error)
|
285
|
+
end
|
286
|
+
raise
|
287
|
+
ensure
|
288
|
+
if !error && transaction
|
289
|
+
if Thread.current.status == "aborting"
|
242
290
|
rollback_transaction
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
rollback_transaction if transaction
|
250
|
-
else
|
251
|
-
begin
|
252
|
-
commit_transaction if transaction
|
253
|
-
rescue Exception
|
254
|
-
rollback_transaction(transaction) unless transaction.state.completed?
|
255
|
-
raise
|
256
|
-
end
|
291
|
+
else
|
292
|
+
begin
|
293
|
+
commit_transaction
|
294
|
+
rescue Exception
|
295
|
+
rollback_transaction(transaction) unless transaction.state.completed?
|
296
|
+
raise
|
257
297
|
end
|
258
298
|
end
|
259
299
|
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "set"
|
3
4
|
require "active_record/connection_adapters/determine_if_preparable_visitor"
|
4
5
|
require "active_record/connection_adapters/schema_cache"
|
5
6
|
require "active_record/connection_adapters/sql_type_metadata"
|
6
7
|
require "active_record/connection_adapters/abstract/schema_dumper"
|
7
8
|
require "active_record/connection_adapters/abstract/schema_creation"
|
8
9
|
require "active_support/concurrency/load_interlock_aware_monitor"
|
10
|
+
require "active_support/deprecation"
|
9
11
|
require "arel/collectors/bind"
|
10
12
|
require "arel/collectors/composite"
|
11
13
|
require "arel/collectors/sql_string"
|
@@ -65,7 +67,7 @@ module ActiveRecord
|
|
65
67
|
# Most of the methods in the adapter are useful during migrations. Most
|
66
68
|
# notably, the instance methods provided by SchemaStatements are very useful.
|
67
69
|
class AbstractAdapter
|
68
|
-
ADAPTER_NAME = "Abstract"
|
70
|
+
ADAPTER_NAME = "Abstract"
|
69
71
|
include ActiveSupport::Callbacks
|
70
72
|
define_callbacks :checkout, :checkin
|
71
73
|
|
@@ -76,12 +78,16 @@ module ActiveRecord
|
|
76
78
|
|
77
79
|
SIMPLE_INT = /\A\d+\z/
|
78
80
|
|
79
|
-
attr_accessor :
|
80
|
-
attr_reader :
|
81
|
+
attr_accessor :pool
|
82
|
+
attr_reader :visitor, :owner, :logger, :lock
|
81
83
|
alias :in_use? :owner
|
82
84
|
|
85
|
+
set_callback :checkin, :after, :enable_lazy_transactions!
|
86
|
+
|
83
87
|
def self.type_cast_config_to_integer(config)
|
84
|
-
if config
|
88
|
+
if config.is_a?(Integer)
|
89
|
+
config
|
90
|
+
elsif SIMPLE_INT.match?(config)
|
85
91
|
config.to_i
|
86
92
|
else
|
87
93
|
config
|
@@ -96,6 +102,19 @@ module ActiveRecord
|
|
96
102
|
end
|
97
103
|
end
|
98
104
|
|
105
|
+
def self.build_read_query_regexp(*parts) # :nodoc:
|
106
|
+
parts = parts.map { |part| /\A[\(\s]*#{part}/i }
|
107
|
+
Regexp.union(*parts)
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.quoted_column_names # :nodoc:
|
111
|
+
@quoted_column_names ||= {}
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.quoted_table_names # :nodoc:
|
115
|
+
@quoted_table_names ||= {}
|
116
|
+
end
|
117
|
+
|
99
118
|
def initialize(connection, logger = nil, config = {}) # :nodoc:
|
100
119
|
super()
|
101
120
|
|
@@ -104,11 +123,10 @@ module ActiveRecord
|
|
104
123
|
@instrumenter = ActiveSupport::Notifications.instrumenter
|
105
124
|
@logger = logger
|
106
125
|
@config = config
|
107
|
-
@pool =
|
126
|
+
@pool = ActiveRecord::ConnectionAdapters::NullPool.new
|
108
127
|
@idle_since = Concurrent.monotonic_time
|
109
|
-
@schema_cache = SchemaCache.new self
|
110
|
-
@quoted_column_names, @quoted_table_names = {}, {}
|
111
128
|
@visitor = arel_visitor
|
129
|
+
@statements = build_statement_pool
|
112
130
|
@lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
|
113
131
|
|
114
132
|
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
|
@@ -117,6 +135,22 @@ module ActiveRecord
|
|
117
135
|
else
|
118
136
|
@prepared_statements = false
|
119
137
|
end
|
138
|
+
|
139
|
+
@advisory_locks_enabled = self.class.type_cast_config_to_boolean(
|
140
|
+
config.fetch(:advisory_locks, true)
|
141
|
+
)
|
142
|
+
end
|
143
|
+
|
144
|
+
def replica?
|
145
|
+
@config[:replica] || false
|
146
|
+
end
|
147
|
+
|
148
|
+
# Determines whether writes are currently being prevents.
|
149
|
+
#
|
150
|
+
# Returns true if the connection is a replica, or if +prevent_writes+
|
151
|
+
# is set to true.
|
152
|
+
def preventing_writes?
|
153
|
+
replica? || ActiveRecord::Base.connection_handler.prevent_writes
|
120
154
|
end
|
121
155
|
|
122
156
|
def migrations_paths # :nodoc:
|
@@ -124,19 +158,49 @@ module ActiveRecord
|
|
124
158
|
end
|
125
159
|
|
126
160
|
def migration_context # :nodoc:
|
127
|
-
MigrationContext.new(migrations_paths)
|
161
|
+
MigrationContext.new(migrations_paths, schema_migration)
|
162
|
+
end
|
163
|
+
|
164
|
+
def schema_migration # :nodoc:
|
165
|
+
@schema_migration ||= begin
|
166
|
+
conn = self
|
167
|
+
spec_name = conn.pool.spec.name
|
168
|
+
name = "#{spec_name}::SchemaMigration"
|
169
|
+
|
170
|
+
Class.new(ActiveRecord::SchemaMigration) do
|
171
|
+
define_singleton_method(:name) { name }
|
172
|
+
define_singleton_method(:to_s) { name }
|
173
|
+
|
174
|
+
self.connection_specification_name = spec_name
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def prepared_statements
|
180
|
+
@prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
|
181
|
+
end
|
182
|
+
|
183
|
+
def prepared_statements_disabled_cache # :nodoc:
|
184
|
+
Thread.current[:ar_prepared_statements_disabled_cache] ||= Set.new
|
128
185
|
end
|
129
186
|
|
130
187
|
class Version
|
131
188
|
include Comparable
|
132
189
|
|
133
|
-
|
190
|
+
attr_reader :full_version_string
|
191
|
+
|
192
|
+
def initialize(version_string, full_version_string = nil)
|
134
193
|
@version = version_string.split(".").map(&:to_i)
|
194
|
+
@full_version_string = full_version_string
|
135
195
|
end
|
136
196
|
|
137
197
|
def <=>(version_string)
|
138
198
|
@version <=> version_string.split(".").map(&:to_i)
|
139
199
|
end
|
200
|
+
|
201
|
+
def to_s
|
202
|
+
@version.join(".")
|
203
|
+
end
|
140
204
|
end
|
141
205
|
|
142
206
|
def valid_type?(type) # :nodoc:
|
@@ -146,7 +210,7 @@ module ActiveRecord
|
|
146
210
|
# this method must only be called while holding connection pool's mutex
|
147
211
|
def lease
|
148
212
|
if in_use?
|
149
|
-
msg = "Cannot lease connection, "
|
213
|
+
msg = +"Cannot lease connection, "
|
150
214
|
if @owner == Thread.current
|
151
215
|
msg << "it is already leased by the current thread."
|
152
216
|
else
|
@@ -159,9 +223,13 @@ module ActiveRecord
|
|
159
223
|
@owner = Thread.current
|
160
224
|
end
|
161
225
|
|
226
|
+
def schema_cache
|
227
|
+
@pool.get_schema_cache(self)
|
228
|
+
end
|
229
|
+
|
162
230
|
def schema_cache=(cache)
|
163
231
|
cache.connection = self
|
164
|
-
@
|
232
|
+
@pool.set_schema_cache(cache)
|
165
233
|
end
|
166
234
|
|
167
235
|
# this method must only be called while holding connection pool's mutex
|
@@ -200,10 +268,10 @@ module ActiveRecord
|
|
200
268
|
end
|
201
269
|
|
202
270
|
def unprepared_statement
|
203
|
-
|
271
|
+
cache = prepared_statements_disabled_cache.add(object_id) if @prepared_statements
|
204
272
|
yield
|
205
273
|
ensure
|
206
|
-
|
274
|
+
cache&.delete(object_id)
|
207
275
|
end
|
208
276
|
|
209
277
|
# Returns the human-readable name of the adapter. Use mixed case - one
|
@@ -212,6 +280,11 @@ module ActiveRecord
|
|
212
280
|
self.class::ADAPTER_NAME
|
213
281
|
end
|
214
282
|
|
283
|
+
# Does the database for this adapter exist?
|
284
|
+
def self.database_exists?(config)
|
285
|
+
raise NotImplementedError
|
286
|
+
end
|
287
|
+
|
215
288
|
# Does this adapter support DDL rollbacks in transactions? That is, would
|
216
289
|
# CREATE TABLE or ALTER TABLE get rolled back by a transaction?
|
217
290
|
def supports_ddl_transactions?
|
@@ -290,12 +363,18 @@ module ActiveRecord
|
|
290
363
|
def supports_foreign_keys_in_create?
|
291
364
|
supports_foreign_keys?
|
292
365
|
end
|
366
|
+
deprecate :supports_foreign_keys_in_create?
|
293
367
|
|
294
368
|
# Does this adapter support views?
|
295
369
|
def supports_views?
|
296
370
|
false
|
297
371
|
end
|
298
372
|
|
373
|
+
# Does this adapter support materialized views?
|
374
|
+
def supports_materialized_views?
|
375
|
+
false
|
376
|
+
end
|
377
|
+
|
299
378
|
# Does this adapter support datetime with precision?
|
300
379
|
def supports_datetime_with_precision?
|
301
380
|
false
|
@@ -320,6 +399,7 @@ module ActiveRecord
|
|
320
399
|
def supports_multi_insert?
|
321
400
|
true
|
322
401
|
end
|
402
|
+
deprecate :supports_multi_insert?
|
323
403
|
|
324
404
|
# Does this adapter support virtual columns?
|
325
405
|
def supports_virtual_columns?
|
@@ -331,6 +411,35 @@ module ActiveRecord
|
|
331
411
|
false
|
332
412
|
end
|
333
413
|
|
414
|
+
# Does this adapter support optimizer hints?
|
415
|
+
def supports_optimizer_hints?
|
416
|
+
false
|
417
|
+
end
|
418
|
+
|
419
|
+
def supports_common_table_expressions?
|
420
|
+
false
|
421
|
+
end
|
422
|
+
|
423
|
+
def supports_lazy_transactions?
|
424
|
+
false
|
425
|
+
end
|
426
|
+
|
427
|
+
def supports_insert_returning?
|
428
|
+
false
|
429
|
+
end
|
430
|
+
|
431
|
+
def supports_insert_on_duplicate_skip?
|
432
|
+
false
|
433
|
+
end
|
434
|
+
|
435
|
+
def supports_insert_on_duplicate_update?
|
436
|
+
false
|
437
|
+
end
|
438
|
+
|
439
|
+
def supports_insert_conflict_target?
|
440
|
+
false
|
441
|
+
end
|
442
|
+
|
334
443
|
# This is meant to be implemented by the adapters that support extensions
|
335
444
|
def disable_extension(name)
|
336
445
|
end
|
@@ -339,6 +448,10 @@ module ActiveRecord
|
|
339
448
|
def enable_extension(name)
|
340
449
|
end
|
341
450
|
|
451
|
+
def advisory_locks_enabled? # :nodoc:
|
452
|
+
supports_advisory_locks? && @advisory_locks_enabled
|
453
|
+
end
|
454
|
+
|
342
455
|
# This is meant to be implemented by the adapters that support advisory
|
343
456
|
# locks
|
344
457
|
#
|
@@ -404,6 +517,9 @@ module ActiveRecord
|
|
404
517
|
#
|
405
518
|
# Prevent @connection's finalizer from touching the socket, or
|
406
519
|
# otherwise communicating with its server, when it is collected.
|
520
|
+
if schema_cache.connection == self
|
521
|
+
schema_cache.connection = nil
|
522
|
+
end
|
407
523
|
end
|
408
524
|
|
409
525
|
# Reset the state of this connection, directing the DBMS to clear
|
@@ -416,11 +532,9 @@ module ActiveRecord
|
|
416
532
|
# this should be overridden by concrete adapters
|
417
533
|
end
|
418
534
|
|
419
|
-
|
420
|
-
# Clear any caching the database adapter may be doing, for example
|
421
|
-
# clearing the prepared statement cache. This is database specific.
|
535
|
+
# Clear any caching the database adapter may be doing.
|
422
536
|
def clear_cache!
|
423
|
-
|
537
|
+
@lock.synchronize { @statements.clear } if @statements
|
424
538
|
end
|
425
539
|
|
426
540
|
# Returns true if its required to reload the connection between requests for development mode.
|
@@ -442,18 +556,25 @@ module ActiveRecord
|
|
442
556
|
# This is useful for when you need to call a proprietary method such as
|
443
557
|
# PostgreSQL's lo_* methods.
|
444
558
|
def raw_connection
|
559
|
+
disable_lazy_transactions!
|
445
560
|
@connection
|
446
561
|
end
|
447
562
|
|
448
|
-
def
|
449
|
-
|
563
|
+
def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
|
564
|
+
attribute.eq(value)
|
565
|
+
end
|
566
|
+
|
567
|
+
def case_sensitive_comparison(attribute, value) # :nodoc:
|
568
|
+
attribute.eq(value)
|
450
569
|
end
|
451
570
|
|
452
|
-
def case_insensitive_comparison(
|
571
|
+
def case_insensitive_comparison(attribute, value) # :nodoc:
|
572
|
+
column = column_for_attribute(attribute)
|
573
|
+
|
453
574
|
if can_perform_case_insensitive_comparison_for?(column)
|
454
|
-
|
575
|
+
attribute.lower.eq(attribute.relation.lower(value))
|
455
576
|
else
|
456
|
-
|
577
|
+
attribute.eq(value)
|
457
578
|
end
|
458
579
|
end
|
459
580
|
|
@@ -468,18 +589,38 @@ module ActiveRecord
|
|
468
589
|
end
|
469
590
|
|
470
591
|
def column_name_for_operation(operation, node) # :nodoc:
|
471
|
-
|
472
|
-
end
|
473
|
-
|
474
|
-
def column_name_from_arel_node(node) # :nodoc:
|
475
|
-
visitor.accept(node, Arel::Collectors::SQLString.new).value
|
592
|
+
visitor.compile(node)
|
476
593
|
end
|
477
594
|
|
478
595
|
def default_index_type?(index) # :nodoc:
|
479
596
|
index.using.nil?
|
480
597
|
end
|
481
598
|
|
599
|
+
# Called by ActiveRecord::InsertAll,
|
600
|
+
# Passed an instance of ActiveRecord::InsertAll::Builder,
|
601
|
+
# This method implements standard bulk inserts for all databases, but
|
602
|
+
# should be overridden by adapters to implement common features with
|
603
|
+
# non-standard syntax like handling duplicates or returning values.
|
604
|
+
def build_insert_sql(insert) # :nodoc:
|
605
|
+
if insert.skip_duplicates? || insert.update_duplicates?
|
606
|
+
raise NotImplementedError, "#{self.class} should define `build_insert_sql` to implement adapter-specific logic for handling duplicates during INSERT"
|
607
|
+
end
|
608
|
+
|
609
|
+
"INSERT #{insert.into} #{insert.values_list}"
|
610
|
+
end
|
611
|
+
|
612
|
+
def get_database_version # :nodoc:
|
613
|
+
end
|
614
|
+
|
615
|
+
def database_version # :nodoc:
|
616
|
+
schema_cache.database_version
|
617
|
+
end
|
618
|
+
|
619
|
+
def check_version # :nodoc:
|
620
|
+
end
|
621
|
+
|
482
622
|
private
|
623
|
+
|
483
624
|
def type_map
|
484
625
|
@type_map ||= Type::TypeMap.new.tap do |mapping|
|
485
626
|
initialize_type_map(mapping)
|
@@ -553,14 +694,12 @@ module ActiveRecord
|
|
553
694
|
$1.to_i if sql_type =~ /\((.*)\)/
|
554
695
|
end
|
555
696
|
|
556
|
-
def translate_exception_class(e, sql)
|
557
|
-
|
558
|
-
message = "#{e.class.name}: #{e.message}: #{sql}"
|
559
|
-
rescue Encoding::CompatibilityError
|
560
|
-
message = "#{e.class.name}: #{e.message.force_encoding sql.encoding}: #{sql}"
|
561
|
-
end
|
697
|
+
def translate_exception_class(e, sql, binds)
|
698
|
+
message = "#{e.class.name}: #{e.message}"
|
562
699
|
|
563
|
-
exception = translate_exception(
|
700
|
+
exception = translate_exception(
|
701
|
+
e, message: message, sql: sql, binds: binds
|
702
|
+
)
|
564
703
|
exception.set_backtrace e.backtrace
|
565
704
|
exception
|
566
705
|
end
|
@@ -573,24 +712,23 @@ module ActiveRecord
|
|
573
712
|
binds: binds,
|
574
713
|
type_casted_binds: type_casted_binds,
|
575
714
|
statement_name: statement_name,
|
576
|
-
connection_id: object_id
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
end
|
581
|
-
rescue => e
|
582
|
-
raise translate_exception_class(e, sql)
|
715
|
+
connection_id: object_id,
|
716
|
+
connection: self) do
|
717
|
+
@lock.synchronize do
|
718
|
+
yield
|
583
719
|
end
|
720
|
+
rescue => e
|
721
|
+
raise translate_exception_class(e, sql, binds)
|
584
722
|
end
|
585
723
|
end
|
586
724
|
|
587
|
-
def translate_exception(exception, message)
|
725
|
+
def translate_exception(exception, message:, sql:, binds:)
|
588
726
|
# override in derived class
|
589
727
|
case exception
|
590
728
|
when RuntimeError
|
591
729
|
exception
|
592
730
|
else
|
593
|
-
ActiveRecord::StatementInvalid.new(message)
|
731
|
+
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
|
594
732
|
end
|
595
733
|
end
|
596
734
|
|
@@ -604,6 +742,11 @@ module ActiveRecord
|
|
604
742
|
raise(ActiveRecordError, "No such column: #{table_name}.#{column_name}")
|
605
743
|
end
|
606
744
|
|
745
|
+
def column_for_attribute(attribute)
|
746
|
+
table_name = attribute.relation.name
|
747
|
+
schema_cache.columns_hash(table_name)[attribute.name.to_s]
|
748
|
+
end
|
749
|
+
|
607
750
|
def collector
|
608
751
|
if prepared_statements
|
609
752
|
Arel::Collectors::Composite.new(
|
@@ -621,6 +764,9 @@ module ActiveRecord
|
|
621
764
|
def arel_visitor
|
622
765
|
Arel::Visitors::ToSql.new(self)
|
623
766
|
end
|
767
|
+
|
768
|
+
def build_statement_pool
|
769
|
+
end
|
624
770
|
end
|
625
771
|
end
|
626
772
|
end
|