activerecord 5.2.7 → 6.0.0.beta1
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 +299 -778
- data/MIT-LICENSE +3 -1
- data/README.rdoc +1 -1
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/associations/association.rb +35 -19
- 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/belongs_to.rb +14 -50
- data/lib/active_record/associations/builder/collection_association.rb +3 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- data/lib/active_record/associations/collection_association.rb +11 -25
- data/lib/active_record/associations/collection_proxy.rb +32 -6
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +25 -18
- 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/join_association.rb +11 -26
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/join_dependency.rb +15 -20
- data/lib/active_record/associations/preloader/association.rb +1 -2
- data/lib/active_record/associations/preloader.rb +32 -29
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/associations.rb +16 -12
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods/dirty.rb +64 -26
- data/lib/active_record/attribute_methods/primary_key.rb +8 -7
- data/lib/active_record/attribute_methods/read.rb +16 -48
- 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 +15 -16
- data/lib/active_record/attribute_methods.rb +34 -56
- data/lib/active_record/autosave_association.rb +7 -21
- data/lib/active_record/base.rb +2 -2
- data/lib/active_record/callbacks.rb +3 -17
- data/lib/active_record/collection_cache_key.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +13 -36
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +25 -84
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -14
- data/lib/active_record/connection_adapters/abstract/quoting.rb +5 -11
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -11
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +0 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -27
- data/lib/active_record/connection_adapters/abstract/transaction.rb +81 -52
- data/lib/active_record/connection_adapters/abstract_adapter.rb +95 -31
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +5 -9
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -7
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +65 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -4
- data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +16 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- 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/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
- data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +95 -62
- data/lib/active_record/connection_handling.rb +132 -26
- data/lib/active_record/core.rb +76 -43
- data/lib/active_record/counter_cache.rb +4 -29
- 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 +74 -0
- data/lib/active_record/database_configurations.rb +184 -0
- data/lib/active_record/enum.rb +22 -7
- data/lib/active_record/errors.rb +24 -21
- 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 +140 -472
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +12 -2
- data/lib/active_record/integration.rb +56 -16
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +2 -2
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/migration/command_recorder.rb +35 -5
- data/lib/active_record/migration/compatibility.rb +34 -16
- data/lib/active_record/migration.rb +38 -37
- data/lib/active_record/model_schema.rb +30 -9
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +18 -7
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +19 -11
- data/lib/active_record/railtie.rb +71 -42
- 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 +94 -43
- data/lib/active_record/reflection.rb +60 -44
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +38 -28
- data/lib/active_record/relation/delegation.rb +4 -13
- data/lib/active_record/relation/finder_methods.rb +12 -25
- data/lib/active_record/relation/merger.rb +2 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
- 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/predicate_builder.rb +4 -6
- data/lib/active_record/relation/query_attribute.rb +15 -12
- data/lib/active_record/relation/query_methods.rb +29 -52
- data/lib/active_record/relation/where_clause.rb +4 -0
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/relation.rb +150 -69
- data/lib/active_record/result.rb +30 -11
- data/lib/active_record/sanitization.rb +2 -39
- data/lib/active_record/schema.rb +1 -10
- data/lib/active_record/schema_dumper.rb +12 -6
- data/lib/active_record/schema_migration.rb +4 -0
- data/lib/active_record/scoping/default.rb +10 -3
- data/lib/active_record/scoping/named.rb +10 -14
- data/lib/active_record/scoping.rb +9 -8
- data/lib/active_record/statement_cache.rb +32 -5
- data/lib/active_record/store.rb +39 -8
- data/lib/active_record/table_metadata.rb +1 -4
- data/lib/active_record/tasks/database_tasks.rb +89 -23
- data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
- 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 +38 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/transactions.rb +3 -22
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type.rb +3 -4
- data/lib/active_record/type_caster/connection.rb +1 -6
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations/uniqueness.rb +13 -25
- data/lib/active_record.rb +2 -1
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/attributes.rb +22 -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/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/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 +63 -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 +44 -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.rb +16 -0
- data/lib/arel/nodes/values_list.rb +24 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +67 -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/depth_first.rb +199 -0
- data/lib/arel/visitors/dot.rb +292 -0
- data/lib/arel/visitors/ibm_db.rb +21 -0
- data/lib/arel/visitors/informix.rb +56 -0
- data/lib/arel/visitors/mssql.rb +143 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +67 -0
- data/lib/arel/visitors/postgresql.rb +116 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +913 -0
- data/lib/arel/visitors/visitor.rb +42 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +44 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- metadata +104 -26
@@ -57,9 +57,7 @@ module ActiveRecord
|
|
57
57
|
|
58
58
|
private
|
59
59
|
|
60
|
-
|
61
|
-
@uri
|
62
|
-
end
|
60
|
+
attr_reader :uri
|
63
61
|
|
64
62
|
def uri_parser
|
65
63
|
@uri_parser ||= URI::Parser.new
|
@@ -116,8 +114,7 @@ module ActiveRecord
|
|
116
114
|
class Resolver # :nodoc:
|
117
115
|
attr_reader :configurations
|
118
116
|
|
119
|
-
# Accepts a
|
120
|
-
# environments such as "production". Keys must be strings.
|
117
|
+
# Accepts a list of db config objects.
|
121
118
|
def initialize(configurations)
|
122
119
|
@configurations = configurations
|
123
120
|
end
|
@@ -138,34 +135,14 @@ module ActiveRecord
|
|
138
135
|
# Resolver.new(configurations).resolve(:production)
|
139
136
|
# # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
|
140
137
|
#
|
141
|
-
def resolve(
|
142
|
-
if
|
143
|
-
resolve_connection
|
144
|
-
elsif env = ActiveRecord::ConnectionHandling::RAILS_ENV.call
|
145
|
-
resolve_symbol_connection env.to_sym
|
138
|
+
def resolve(config_or_env, pool_name = nil)
|
139
|
+
if config_or_env
|
140
|
+
resolve_connection config_or_env, pool_name
|
146
141
|
else
|
147
142
|
raise AdapterNotSpecified
|
148
143
|
end
|
149
144
|
end
|
150
145
|
|
151
|
-
# Expands each key in @configurations hash into fully resolved hash
|
152
|
-
def resolve_all
|
153
|
-
config = configurations.dup
|
154
|
-
|
155
|
-
if env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
|
156
|
-
env_config = config[env] if config[env].is_a?(Hash) && !(config[env].key?("adapter") || config[env].key?("url"))
|
157
|
-
end
|
158
|
-
|
159
|
-
config.reject! { |k, v| v.is_a?(Hash) && !(v.key?("adapter") || v.key?("url")) }
|
160
|
-
config.merge! env_config if env_config
|
161
|
-
|
162
|
-
config.each do |key, value|
|
163
|
-
config[key] = resolve(value) if value
|
164
|
-
end
|
165
|
-
|
166
|
-
config
|
167
|
-
end
|
168
|
-
|
169
146
|
# Returns an instance of ConnectionSpecification for a given adapter.
|
170
147
|
# Accepts a hash one layer deep that contains all connection information.
|
171
148
|
#
|
@@ -179,7 +156,9 @@ module ActiveRecord
|
|
179
156
|
# # => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" }
|
180
157
|
#
|
181
158
|
def spec(config)
|
182
|
-
|
159
|
+
pool_name = config if config.is_a?(Symbol)
|
160
|
+
|
161
|
+
spec = resolve(config, pool_name).symbolize_keys
|
183
162
|
|
184
163
|
raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter)
|
185
164
|
|
@@ -214,7 +193,6 @@ module ActiveRecord
|
|
214
193
|
end
|
215
194
|
|
216
195
|
private
|
217
|
-
|
218
196
|
# Returns fully resolved connection, accepts hash, string or symbol.
|
219
197
|
# Always returns a hash.
|
220
198
|
#
|
@@ -235,32 +213,64 @@ module ActiveRecord
|
|
235
213
|
# Resolver.new({}).resolve_connection("postgresql://localhost/foo")
|
236
214
|
# # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
|
237
215
|
#
|
238
|
-
def resolve_connection(
|
239
|
-
case
|
216
|
+
def resolve_connection(config_or_env, pool_name = nil)
|
217
|
+
case config_or_env
|
240
218
|
when Symbol
|
241
|
-
resolve_symbol_connection
|
219
|
+
resolve_symbol_connection config_or_env, pool_name
|
242
220
|
when String
|
243
|
-
resolve_url_connection
|
221
|
+
resolve_url_connection config_or_env
|
244
222
|
when Hash
|
245
|
-
resolve_hash_connection
|
223
|
+
resolve_hash_connection config_or_env
|
224
|
+
else
|
225
|
+
resolve_connection config_or_env
|
246
226
|
end
|
247
227
|
end
|
248
228
|
|
249
|
-
# Takes the environment such as +:production+ or +:development
|
229
|
+
# Takes the environment such as +:production+ or +:development+ and a
|
230
|
+
# pool name the corresponds to the name given by the connection pool
|
231
|
+
# to the connection. That pool name is merged into the hash with the
|
232
|
+
# name key.
|
233
|
+
#
|
250
234
|
# This requires that the @configurations was initialized with a key that
|
251
235
|
# matches.
|
252
236
|
#
|
253
|
-
#
|
254
|
-
#
|
237
|
+
# configurations = #<ActiveRecord::DatabaseConfigurations:0x00007fd9fdace3e0
|
238
|
+
# @configurations=[
|
239
|
+
# #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd9fdace250
|
240
|
+
# @env_name="production", @spec_name="primary", @config={"database"=>"my_db"}>
|
241
|
+
# ]>
|
255
242
|
#
|
256
|
-
|
257
|
-
|
258
|
-
|
243
|
+
# Resolver.new(configurations).resolve_symbol_connection(:production, "primary")
|
244
|
+
# # => { "database" => "my_db" }
|
245
|
+
def resolve_symbol_connection(env_name, pool_name)
|
246
|
+
db_config = configurations.find_db_config(env_name)
|
247
|
+
|
248
|
+
if db_config
|
249
|
+
resolve_connection(db_config.config).merge("name" => pool_name.to_s)
|
259
250
|
else
|
260
|
-
raise
|
251
|
+
raise AdapterNotSpecified, <<~MSG
|
252
|
+
The `#{env_name}` database is not configured for the `#{ActiveRecord::ConnectionHandling::DEFAULT_ENV.call}` environment.
|
253
|
+
|
254
|
+
Available databases configurations are:
|
255
|
+
|
256
|
+
#{build_configuration_sentence}
|
257
|
+
MSG
|
261
258
|
end
|
262
259
|
end
|
263
260
|
|
261
|
+
def build_configuration_sentence # :nodoc:
|
262
|
+
configs = configurations.configs_for(include_replicas: true)
|
263
|
+
|
264
|
+
configs.group_by(&:env_name).map do |env, config|
|
265
|
+
namespaces = config.map(&:spec_name)
|
266
|
+
if namespaces.size > 1
|
267
|
+
"#{env}: #{namespaces.join(", ")}"
|
268
|
+
else
|
269
|
+
env
|
270
|
+
end
|
271
|
+
end.join("\n")
|
272
|
+
end
|
273
|
+
|
264
274
|
# Accepts a hash. Expands the "url" key that contains a
|
265
275
|
# URL database connection to a full connection
|
266
276
|
# hash and merges with the rest of the hash.
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module ConnectionAdapters
|
5
5
|
module DetermineIfPreparableVisitor
|
6
|
-
|
6
|
+
attr_reader :preparable
|
7
7
|
|
8
8
|
def accept(*)
|
9
9
|
@preparable = true
|
@@ -12,15 +12,11 @@ module ActiveRecord
|
|
12
12
|
|
13
13
|
def visit_Arel_Nodes_In(o, collector)
|
14
14
|
@preparable = false
|
15
|
+
super
|
16
|
+
end
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
-
if Arel::Nodes::BindParam === bind && Relation::QueryAttribute === bind.value
|
19
|
-
!bind.value.boundable?
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
18
|
+
def visit_Arel_Nodes_NotIn(o, collector)
|
19
|
+
@preparable = false
|
24
20
|
super
|
25
21
|
end
|
26
22
|
|
@@ -19,8 +19,19 @@ module ActiveRecord
|
|
19
19
|
execute(sql, name).to_a
|
20
20
|
end
|
21
21
|
|
22
|
+
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :explain, :select, :set, :show, :release, :savepoint, :rollback) # :nodoc:
|
23
|
+
private_constant :READ_QUERY
|
24
|
+
|
25
|
+
def write_query?(sql) # :nodoc:
|
26
|
+
!READ_QUERY.match?(sql)
|
27
|
+
end
|
28
|
+
|
22
29
|
# Executes the SQL statement in the context of this connection.
|
23
30
|
def execute(sql, name = nil)
|
31
|
+
if preventing_writes? && write_query?(sql)
|
32
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
33
|
+
end
|
34
|
+
|
24
35
|
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
25
36
|
# made since we established the connection
|
26
37
|
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
@@ -31,11 +42,19 @@ module ActiveRecord
|
|
31
42
|
def exec_query(sql, name = "SQL", binds = [], prepare: false)
|
32
43
|
if without_prepared_statement?(binds)
|
33
44
|
execute_and_free(sql, name) do |result|
|
34
|
-
|
45
|
+
if result
|
46
|
+
ActiveRecord::Result.new(result.fields, result.to_a)
|
47
|
+
else
|
48
|
+
ActiveRecord::Result.new([], [])
|
49
|
+
end
|
35
50
|
end
|
36
51
|
else
|
37
52
|
exec_stmt_and_free(sql, name, binds, cache_stmt: prepare) do |_, result|
|
38
|
-
|
53
|
+
if result
|
54
|
+
ActiveRecord::Result.new(result.fields, result.to_a)
|
55
|
+
else
|
56
|
+
ActiveRecord::Result.new([], [])
|
57
|
+
end
|
39
58
|
end
|
40
59
|
end
|
41
60
|
end
|
@@ -59,7 +78,7 @@ module ActiveRecord
|
|
59
78
|
end
|
60
79
|
|
61
80
|
def discard_remaining_results
|
62
|
-
@connection.
|
81
|
+
@connection.abandon_results!
|
63
82
|
end
|
64
83
|
|
65
84
|
def supports_set_server_option?
|
@@ -99,6 +118,12 @@ module ActiveRecord
|
|
99
118
|
end
|
100
119
|
|
101
120
|
def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
|
121
|
+
if preventing_writes? && write_query?(sql)
|
122
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
123
|
+
end
|
124
|
+
|
125
|
+
materialize_transactions
|
126
|
+
|
102
127
|
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
103
128
|
# made since we established the connection
|
104
129
|
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
@@ -107,10 +132,7 @@ module ActiveRecord
|
|
107
132
|
|
108
133
|
log(sql, name, binds, type_casted_binds) do
|
109
134
|
if cache_stmt
|
110
|
-
|
111
|
-
stmt: @connection.prepare(sql)
|
112
|
-
}
|
113
|
-
stmt = cache[:stmt]
|
135
|
+
stmt = @statements[sql] ||= @connection.prepare(sql)
|
114
136
|
else
|
115
137
|
stmt = @connection.prepare(sql)
|
116
138
|
end
|
@@ -4,8 +4,7 @@ module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
5
5
|
module MySQL
|
6
6
|
class SchemaCreation < AbstractAdapter::SchemaCreation # :nodoc:
|
7
|
-
delegate :add_sql_comment!, :mariadb?, to: :@conn
|
8
|
-
private :add_sql_comment!, :mariadb?
|
7
|
+
delegate :add_sql_comment!, :mariadb?, to: :@conn, private: true
|
9
8
|
|
10
9
|
private
|
11
10
|
|
@@ -18,7 +17,7 @@ module ActiveRecord
|
|
18
17
|
end
|
19
18
|
|
20
19
|
def visit_ChangeColumnDefinition(o)
|
21
|
-
change_column_sql = "CHANGE #{quote_column_name(o.name)} #{accept(o.column)}"
|
20
|
+
change_column_sql = +"CHANGE #{quote_column_name(o.name)} #{accept(o.column)}"
|
22
21
|
add_column_position!(change_column_sql, column_options(o.column))
|
23
22
|
end
|
24
23
|
|
@@ -65,7 +64,7 @@ module ActiveRecord
|
|
65
64
|
|
66
65
|
def index_in_create(table_name, column_name, options)
|
67
66
|
index_name, index_type, index_columns, _, _, index_using, comment = @conn.add_index_options(table_name, column_name, options)
|
68
|
-
add_sql_comment!("#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})"
|
67
|
+
add_sql_comment!((+"#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})"), comment)
|
69
68
|
end
|
70
69
|
end
|
71
70
|
end
|
@@ -35,13 +35,39 @@ module ActiveRecord
|
|
35
35
|
]
|
36
36
|
end
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
if row[:Expression]
|
39
|
+
expression = row[:Expression]
|
40
|
+
expression = +"(#{expression})" unless expression.start_with?("(")
|
41
|
+
indexes.last[-2] << expression
|
42
|
+
indexes.last[-1][:expressions] ||= {}
|
43
|
+
indexes.last[-1][:expressions][expression] = expression
|
44
|
+
indexes.last[-1][:orders][expression] = :desc if row[:Collation] == "D"
|
45
|
+
else
|
46
|
+
indexes.last[-2] << row[:Column_name]
|
47
|
+
indexes.last[-1][:lengths][row[:Column_name]] = row[:Sub_part].to_i if row[:Sub_part]
|
48
|
+
indexes.last[-1][:orders][row[:Column_name]] = :desc if row[:Collation] == "D"
|
49
|
+
end
|
41
50
|
end
|
42
51
|
end
|
43
52
|
|
44
|
-
indexes.map
|
53
|
+
indexes.map do |index|
|
54
|
+
options = index.last
|
55
|
+
|
56
|
+
if expressions = options.delete(:expressions)
|
57
|
+
orders = options.delete(:orders)
|
58
|
+
lengths = options.delete(:lengths)
|
59
|
+
|
60
|
+
columns = index[-2].map { |name|
|
61
|
+
[ name.to_sym, expressions[name] || +quote_column_name(name) ]
|
62
|
+
}.to_h
|
63
|
+
|
64
|
+
index[-2] = add_options_for_index_columns(
|
65
|
+
columns, order: orders, length: lengths
|
66
|
+
).values.join(", ")
|
67
|
+
end
|
68
|
+
|
69
|
+
IndexDefinition.new(*index)
|
70
|
+
end
|
45
71
|
end
|
46
72
|
|
47
73
|
def remove_column(table_name, column_name, type = nil, options = {})
|
@@ -51,9 +77,13 @@ module ActiveRecord
|
|
51
77
|
super
|
52
78
|
end
|
53
79
|
|
80
|
+
def create_table(table_name, options: default_row_format, **)
|
81
|
+
super
|
82
|
+
end
|
83
|
+
|
54
84
|
def internal_string_options_for_primary_key
|
55
85
|
super.tap do |options|
|
56
|
-
if CHARSETS_OF_4BYTES_MAXLEN.include?(charset)
|
86
|
+
if !row_format_dynamic_by_default? && CHARSETS_OF_4BYTES_MAXLEN.include?(charset)
|
57
87
|
options[:collation] = collation.sub(/\A[^_]+/, "utf8")
|
58
88
|
end
|
59
89
|
end
|
@@ -70,6 +100,28 @@ module ActiveRecord
|
|
70
100
|
private
|
71
101
|
CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"]
|
72
102
|
|
103
|
+
def row_format_dynamic_by_default?
|
104
|
+
if mariadb?
|
105
|
+
version >= "10.2.2"
|
106
|
+
else
|
107
|
+
version >= "5.7.9"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def default_row_format
|
112
|
+
return if row_format_dynamic_by_default?
|
113
|
+
|
114
|
+
unless defined?(@default_row_format)
|
115
|
+
if query_value("SELECT @@innodb_file_per_table = 1 AND @@innodb_file_format = 'Barracuda'") == 1
|
116
|
+
@default_row_format = "ROW_FORMAT=DYNAMIC"
|
117
|
+
else
|
118
|
+
@default_row_format = nil
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
@default_row_format
|
123
|
+
end
|
124
|
+
|
73
125
|
def schema_creation
|
74
126
|
MySQL::SchemaCreation.new(self)
|
75
127
|
end
|
@@ -80,10 +132,13 @@ module ActiveRecord
|
|
80
132
|
|
81
133
|
def new_column_from_field(table_name, field)
|
82
134
|
type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
default, default_function =
|
135
|
+
default, default_function = field[:Default], nil
|
136
|
+
|
137
|
+
if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(default)
|
138
|
+
default, default_function = nil, default
|
139
|
+
elsif type_metadata.extra == "DEFAULT_GENERATED"
|
140
|
+
default = +"(#{default})" unless default.start_with?("(")
|
141
|
+
default, default_function = nil, default
|
87
142
|
end
|
88
143
|
|
89
144
|
MySQL::Column.new(
|
@@ -121,7 +176,7 @@ module ActiveRecord
|
|
121
176
|
def data_source_sql(name = nil, type: nil)
|
122
177
|
scope = quoted_scope(name, type: type)
|
123
178
|
|
124
|
-
sql = "SELECT table_name FROM information_schema.tables"
|
179
|
+
sql = +"SELECT table_name FROM information_schema.tables"
|
125
180
|
sql << " WHERE table_schema = #{scope[:schema]}"
|
126
181
|
sql << " AND table_name = #{scope[:name]}" if scope[:name]
|
127
182
|
sql << " AND table_type = #{scope[:type]}" if scope[:type]
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require "active_record/connection_adapters/abstract_mysql_adapter"
|
4
4
|
require "active_record/connection_adapters/mysql/database_statements"
|
5
5
|
|
6
|
-
gem "mysql2", ">= 0.4.4"
|
6
|
+
gem "mysql2", ">= 0.4.4"
|
7
7
|
require "mysql2"
|
8
8
|
|
9
9
|
module ActiveRecord
|
@@ -14,7 +14,7 @@ module ActiveRecord
|
|
14
14
|
config[:flags] ||= 0
|
15
15
|
|
16
16
|
if config[:flags].kind_of? Array
|
17
|
-
config[:flags].push "FOUND_ROWS"
|
17
|
+
config[:flags].push "FOUND_ROWS"
|
18
18
|
else
|
19
19
|
config[:flags] |= Mysql2::Client::FOUND_ROWS
|
20
20
|
end
|
@@ -32,7 +32,7 @@ module ActiveRecord
|
|
32
32
|
|
33
33
|
module ConnectionAdapters
|
34
34
|
class Mysql2Adapter < AbstractMysqlAdapter
|
35
|
-
ADAPTER_NAME = "Mysql2"
|
35
|
+
ADAPTER_NAME = "Mysql2"
|
36
36
|
|
37
37
|
include MySQL::DatabaseStatements
|
38
38
|
|
@@ -58,6 +58,10 @@ module ActiveRecord
|
|
58
58
|
true
|
59
59
|
end
|
60
60
|
|
61
|
+
def supports_lazy_transactions?
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
61
65
|
# HELPER METHODS ===========================================
|
62
66
|
|
63
67
|
def each_hash(result) # :nodoc:
|
@@ -117,7 +121,7 @@ module ActiveRecord
|
|
117
121
|
end
|
118
122
|
|
119
123
|
def configure_connection
|
120
|
-
@connection.query_options
|
124
|
+
@connection.query_options[:as] = :array
|
121
125
|
super
|
122
126
|
end
|
123
127
|
|
@@ -20,10 +20,9 @@ module ActiveRecord
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
|
23
|
+
private
|
24
24
|
attr_reader :max_identifier_length
|
25
25
|
|
26
|
-
private
|
27
26
|
def sequence_name_from_parts(table_name, column_name, suffix)
|
28
27
|
over_length = [table_name, column_name, suffix].map(&:length).sum + 2 - max_identifier_length
|
29
28
|
|
@@ -58,6 +58,8 @@ module ActiveRecord
|
|
58
58
|
|
59
59
|
# Queries the database and returns the results in an Array-like object
|
60
60
|
def query(sql, name = nil) #:nodoc:
|
61
|
+
materialize_transactions
|
62
|
+
|
61
63
|
log(sql, name) do
|
62
64
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
63
65
|
result_as_array @connection.async_exec(sql)
|
@@ -65,11 +67,24 @@ module ActiveRecord
|
|
65
67
|
end
|
66
68
|
end
|
67
69
|
|
70
|
+
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :explain, :select, :set, :show, :release, :savepoint, :rollback) # :nodoc:
|
71
|
+
private_constant :READ_QUERY
|
72
|
+
|
73
|
+
def write_query?(sql) # :nodoc:
|
74
|
+
!READ_QUERY.match?(sql)
|
75
|
+
end
|
76
|
+
|
68
77
|
# Executes an SQL statement, returning a PG::Result object on success
|
69
78
|
# or raising a PG::Error exception otherwise.
|
70
79
|
# Note: the PG::Result object is manually memory managed; if you don't
|
71
80
|
# need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
|
72
81
|
def execute(sql, name = nil)
|
82
|
+
if preventing_writes? && write_query?(sql)
|
83
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
84
|
+
end
|
85
|
+
|
86
|
+
materialize_transactions
|
87
|
+
|
73
88
|
log(sql, name) do
|
74
89
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
75
90
|
@connection.async_exec(sql)
|
@@ -95,7 +110,7 @@ module ActiveRecord
|
|
95
110
|
end
|
96
111
|
alias :exec_update :exec_delete
|
97
112
|
|
98
|
-
def sql_for_insert(sql, pk,
|
113
|
+
def sql_for_insert(sql, pk, sequence_name, binds) # :nodoc:
|
99
114
|
if pk.nil?
|
100
115
|
# Extract the table from the insert sql. Yuck.
|
101
116
|
table_ref = extract_table_ref_from_insert_sql(sql)
|
@@ -43,10 +43,7 @@ module ActiveRecord
|
|
43
43
|
/\A[0-9A-F]*\Z/i.match?(value)
|
44
44
|
end
|
45
45
|
|
46
|
-
|
47
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
48
|
-
protected
|
49
|
-
|
46
|
+
private
|
50
47
|
attr_reader :value
|
51
48
|
end
|
52
49
|
end
|
@@ -26,9 +26,9 @@ module ActiveRecord
|
|
26
26
|
|
27
27
|
value = value.sub(/^\((.+)\)$/, '-\1') # (4)
|
28
28
|
case value
|
29
|
-
when /^-?\D
|
29
|
+
when /^-?\D+[\d,]+\.\d{2}$/ # (1)
|
30
30
|
value.gsub!(/[^-\d.]/, "")
|
31
|
-
when /^-?\D
|
31
|
+
when /^-?\D+[\d.]+,\d{2}$/ # (2)
|
32
32
|
value.gsub!(/[^-\d,]/, "").sub!(/,/, ".")
|
33
33
|
end
|
34
34
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/array/extract"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module ConnectionAdapters
|
5
7
|
module PostgreSQL
|
@@ -16,12 +18,12 @@ module ActiveRecord
|
|
16
18
|
|
17
19
|
def run(records)
|
18
20
|
nodes = records.reject { |row| @store.key? row["oid"].to_i }
|
19
|
-
mapped
|
20
|
-
ranges
|
21
|
-
enums
|
22
|
-
domains
|
23
|
-
arrays
|
24
|
-
composites
|
21
|
+
mapped = nodes.extract! { |row| @store.key? row["typname"] }
|
22
|
+
ranges = nodes.extract! { |row| row["typtype"] == "r" }
|
23
|
+
enums = nodes.extract! { |row| row["typtype"] == "e" }
|
24
|
+
domains = nodes.extract! { |row| row["typtype"] == "d" }
|
25
|
+
arrays = nodes.extract! { |row| row["typinput"] == "array_in" }
|
26
|
+
composites = nodes.extract! { |row| row["typelem"].to_i != 0 }
|
25
27
|
|
26
28
|
mapped.each { |row| register_mapped_type(row) }
|
27
29
|
enums.each { |row| register_enum_type(row) }
|
@@ -34,7 +36,7 @@ module ActiveRecord
|
|
34
36
|
def query_conditions_for_initial_load
|
35
37
|
known_type_names = @store.keys.map { |n| "'#{n}'" }
|
36
38
|
known_type_types = %w('r' 'e' 'd')
|
37
|
-
|
39
|
+
<<~SQL % [known_type_names.join(", "), known_type_types.join(", ")]
|
38
40
|
WHERE
|
39
41
|
t.typname IN (%s)
|
40
42
|
OR t.typtype IN (%s)
|
@@ -93,11 +93,11 @@ module ActiveRecord
|
|
93
93
|
elsif value.hex?
|
94
94
|
"X'#{value}'"
|
95
95
|
end
|
96
|
-
when
|
97
|
-
if value.
|
98
|
-
"'#{value}'"
|
99
|
-
else
|
96
|
+
when Numeric
|
97
|
+
if value.finite?
|
100
98
|
super
|
99
|
+
else
|
100
|
+
"'#{value}'"
|
101
101
|
end
|
102
102
|
when OID::Array::Data
|
103
103
|
_quote(encode_array(value))
|