activerecord 5.2.6 → 6.0.0
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 +609 -622
- data/MIT-LICENSE +3 -1
- data/README.rdoc +4 -2
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/associations/association.rb +52 -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/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +19 -52
- data/lib/active_record/associations/builder/collection_association.rb +3 -13
- 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 +6 -21
- data/lib/active_record/associations/collection_proxy.rb +12 -15
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +2 -10
- data/lib/active_record/associations/has_many_through_association.rb +14 -14
- 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 +9 -10
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/join_dependency.rb +24 -28
- 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/preloader.rb +40 -32
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/associations.rb +19 -14
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +111 -40
- 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/attribute_methods.rb +28 -100
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +5 -9
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +5 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +94 -16
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +95 -123
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -8
- 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 +132 -53
- data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
- data/lib/active_record/connection_adapters/abstract_adapter.rb +180 -47
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +128 -194
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +73 -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 +20 -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/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 +12 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +160 -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 +118 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +125 -141
- data/lib/active_record/connection_handling.rb +149 -27
- data/lib/active_record/core.rb +100 -60
- 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 +79 -0
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +37 -7
- data/lib/active_record/errors.rb +15 -7
- 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/resolver/session.rb +45 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/migration/command_recorder.rb +50 -6
- data/lib/active_record/migration/compatibility.rb +76 -49
- data/lib/active_record/migration.rb +100 -81
- 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 +228 -24
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +32 -20
- data/lib/active_record/railtie.rb +80 -43
- 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 +32 -30
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +53 -47
- data/lib/active_record/relation/delegation.rb +26 -43
- data/lib/active_record/relation/finder_methods.rb +13 -26
- data/lib/active_record/relation/merger.rb +11 -20
- 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 +13 -8
- data/lib/active_record/relation/query_methods.rb +189 -63
- 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/relation.rb +310 -80
- 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/default.rb +4 -5
- data/lib/active_record/scoping/named.rb +19 -15
- data/lib/active_record/scoping.rb +8 -8
- data/lib/active_record/statement_cache.rb +30 -3
- 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 +194 -25
- 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/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type.rb +3 -4
- 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/uniqueness.rb +15 -27
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record.rb +9 -2
- 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/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/nodes.rb +68 -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 +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/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +51 -0
- 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/migration.rb +14 -1
- 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 +108 -26
- data/lib/active_record/collection_cache_key.rb +0 -53
@@ -4,17 +4,20 @@ require "active_record/connection_adapters/abstract_adapter"
|
|
4
4
|
require "active_record/connection_adapters/statement_pool"
|
5
5
|
require "active_record/connection_adapters/sqlite3/explain_pretty_printer"
|
6
6
|
require "active_record/connection_adapters/sqlite3/quoting"
|
7
|
+
require "active_record/connection_adapters/sqlite3/database_statements"
|
7
8
|
require "active_record/connection_adapters/sqlite3/schema_creation"
|
8
9
|
require "active_record/connection_adapters/sqlite3/schema_definitions"
|
9
10
|
require "active_record/connection_adapters/sqlite3/schema_dumper"
|
10
11
|
require "active_record/connection_adapters/sqlite3/schema_statements"
|
11
12
|
|
12
|
-
gem "sqlite3", "~> 1.
|
13
|
+
gem "sqlite3", "~> 1.4"
|
13
14
|
require "sqlite3"
|
14
15
|
|
15
16
|
module ActiveRecord
|
16
17
|
module ConnectionHandling # :nodoc:
|
17
18
|
def sqlite3_connection(config)
|
19
|
+
config = config.symbolize_keys
|
20
|
+
|
18
21
|
# Require database.
|
19
22
|
unless config[:database]
|
20
23
|
raise ArgumentError, "No database file specified. Missing argument: database"
|
@@ -31,11 +34,9 @@ module ActiveRecord
|
|
31
34
|
|
32
35
|
db = SQLite3::Database.new(
|
33
36
|
config[:database].to_s,
|
34
|
-
results_as_hash: true
|
37
|
+
config.merge(results_as_hash: true)
|
35
38
|
)
|
36
39
|
|
37
|
-
db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]
|
38
|
-
|
39
40
|
ConnectionAdapters::SQLite3Adapter.new(db, logger, nil, config)
|
40
41
|
rescue Errno::ENOENT => error
|
41
42
|
if error.message.include?("No such file or directory")
|
@@ -54,10 +55,11 @@ module ActiveRecord
|
|
54
55
|
#
|
55
56
|
# * <tt>:database</tt> - Path to the database file.
|
56
57
|
class SQLite3Adapter < AbstractAdapter
|
57
|
-
ADAPTER_NAME = "SQLite"
|
58
|
+
ADAPTER_NAME = "SQLite"
|
58
59
|
|
59
60
|
include SQLite3::Quoting
|
60
61
|
include SQLite3::SchemaStatements
|
62
|
+
include SQLite3::DatabaseStatements
|
61
63
|
|
62
64
|
NATIVE_DATABASE_TYPES = {
|
63
65
|
primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
|
@@ -74,39 +76,38 @@ module ActiveRecord
|
|
74
76
|
json: { name: "json" },
|
75
77
|
}
|
76
78
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
# ExampleModel.where("boolean_column = 't'").update_all(boolean_column: 1)
|
87
|
-
# ExampleModel.where("boolean_column = 'f'").update_all(boolean_column: 0)
|
88
|
-
# for all models and all boolean columns, after which the flag must be set
|
89
|
-
# to true by adding the following to your <tt>application.rb</tt> file:
|
90
|
-
#
|
91
|
-
# Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true
|
92
|
-
class_attribute :represent_boolean_as_integer, default: false
|
79
|
+
def self.represent_boolean_as_integer=(value) # :nodoc:
|
80
|
+
if value == false
|
81
|
+
raise "`.represent_boolean_as_integer=` is now always true, so make sure your application can work with it and remove this settings."
|
82
|
+
end
|
83
|
+
|
84
|
+
ActiveSupport::Deprecation.warn(
|
85
|
+
"`.represent_boolean_as_integer=` is now always true, so setting this is deprecated and will be removed in Rails 6.1."
|
86
|
+
)
|
87
|
+
end
|
93
88
|
|
94
89
|
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
95
90
|
private
|
96
91
|
def dealloc(stmt)
|
97
|
-
stmt
|
92
|
+
stmt.close unless stmt.closed?
|
98
93
|
end
|
99
94
|
end
|
100
95
|
|
101
96
|
def initialize(connection, logger, connection_options, config)
|
102
97
|
super(connection, logger, config)
|
103
|
-
|
104
|
-
@active = true
|
105
|
-
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
|
106
|
-
|
107
98
|
configure_connection
|
108
99
|
end
|
109
100
|
|
101
|
+
def self.database_exists?(config)
|
102
|
+
config = config.symbolize_keys
|
103
|
+
if config[:database] == ":memory:"
|
104
|
+
return true
|
105
|
+
else
|
106
|
+
database_file = defined?(Rails.root) ? File.expand_path(config[:database], Rails.root) : config[:database]
|
107
|
+
File.exist?(database_file)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
110
111
|
def supports_ddl_transactions?
|
111
112
|
true
|
112
113
|
end
|
@@ -116,15 +117,19 @@ module ActiveRecord
|
|
116
117
|
end
|
117
118
|
|
118
119
|
def supports_partial_index?
|
119
|
-
|
120
|
+
true
|
121
|
+
end
|
122
|
+
|
123
|
+
def supports_expression_index?
|
124
|
+
database_version >= "3.9.0"
|
120
125
|
end
|
121
126
|
|
122
127
|
def requires_reloading?
|
123
128
|
true
|
124
129
|
end
|
125
130
|
|
126
|
-
def
|
127
|
-
|
131
|
+
def supports_foreign_keys?
|
132
|
+
true
|
128
133
|
end
|
129
134
|
|
130
135
|
def supports_views?
|
@@ -139,27 +144,29 @@ module ActiveRecord
|
|
139
144
|
true
|
140
145
|
end
|
141
146
|
|
142
|
-
def
|
143
|
-
|
147
|
+
def supports_insert_on_conflict?
|
148
|
+
database_version >= "3.24.0"
|
144
149
|
end
|
150
|
+
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
151
|
+
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
152
|
+
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
145
153
|
|
146
154
|
def active?
|
147
|
-
|
155
|
+
!@connection.closed?
|
156
|
+
end
|
157
|
+
|
158
|
+
def reconnect!
|
159
|
+
super
|
160
|
+
connect if @connection.closed?
|
148
161
|
end
|
149
162
|
|
150
163
|
# Disconnects from the database if already connected. Otherwise, this
|
151
164
|
# method does nothing.
|
152
165
|
def disconnect!
|
153
166
|
super
|
154
|
-
@active = false
|
155
167
|
@connection.close rescue nil
|
156
168
|
end
|
157
169
|
|
158
|
-
# Clears the prepared statements cache.
|
159
|
-
def clear_cache!
|
160
|
-
@statements.clear
|
161
|
-
end
|
162
|
-
|
163
170
|
def supports_index_sort_order?
|
164
171
|
true
|
165
172
|
end
|
@@ -184,91 +191,34 @@ module ActiveRecord
|
|
184
191
|
true
|
185
192
|
end
|
186
193
|
|
194
|
+
def supports_lazy_transactions?
|
195
|
+
true
|
196
|
+
end
|
197
|
+
|
187
198
|
# REFERENTIAL INTEGRITY ====================================
|
188
199
|
|
189
200
|
def disable_referential_integrity # :nodoc:
|
190
|
-
|
201
|
+
old_foreign_keys = query_value("PRAGMA foreign_keys")
|
202
|
+
old_defer_foreign_keys = query_value("PRAGMA defer_foreign_keys")
|
191
203
|
|
192
204
|
begin
|
205
|
+
execute("PRAGMA defer_foreign_keys = ON")
|
193
206
|
execute("PRAGMA foreign_keys = OFF")
|
194
207
|
yield
|
195
208
|
ensure
|
196
|
-
execute("PRAGMA
|
209
|
+
execute("PRAGMA defer_foreign_keys = #{old_defer_foreign_keys}")
|
210
|
+
execute("PRAGMA foreign_keys = #{old_foreign_keys}")
|
197
211
|
end
|
198
212
|
end
|
199
213
|
|
200
214
|
#--
|
201
215
|
# DATABASE STATEMENTS ======================================
|
202
216
|
#++
|
203
|
-
|
204
217
|
def explain(arel, binds = [])
|
205
218
|
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
|
206
219
|
SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
|
207
220
|
end
|
208
221
|
|
209
|
-
def exec_query(sql, name = nil, binds = [], prepare: false)
|
210
|
-
type_casted_binds = type_casted_binds(binds)
|
211
|
-
|
212
|
-
log(sql, name, binds, type_casted_binds) do
|
213
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
214
|
-
# Don't cache statements if they are not prepared
|
215
|
-
unless prepare
|
216
|
-
stmt = @connection.prepare(sql)
|
217
|
-
begin
|
218
|
-
cols = stmt.columns
|
219
|
-
unless without_prepared_statement?(binds)
|
220
|
-
stmt.bind_params(type_casted_binds)
|
221
|
-
end
|
222
|
-
records = stmt.to_a
|
223
|
-
ensure
|
224
|
-
stmt.close
|
225
|
-
end
|
226
|
-
else
|
227
|
-
cache = @statements[sql] ||= {
|
228
|
-
stmt: @connection.prepare(sql)
|
229
|
-
}
|
230
|
-
stmt = cache[:stmt]
|
231
|
-
cols = cache[:cols] ||= stmt.columns
|
232
|
-
stmt.reset!
|
233
|
-
stmt.bind_params(type_casted_binds)
|
234
|
-
records = stmt.to_a
|
235
|
-
end
|
236
|
-
|
237
|
-
ActiveRecord::Result.new(cols, records)
|
238
|
-
end
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
def exec_delete(sql, name = "SQL", binds = [])
|
243
|
-
exec_query(sql, name, binds)
|
244
|
-
@connection.changes
|
245
|
-
end
|
246
|
-
alias :exec_update :exec_delete
|
247
|
-
|
248
|
-
def last_inserted_id(result)
|
249
|
-
@connection.last_insert_row_id
|
250
|
-
end
|
251
|
-
|
252
|
-
def execute(sql, name = nil) #:nodoc:
|
253
|
-
log(sql, name) do
|
254
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
255
|
-
@connection.execute(sql)
|
256
|
-
end
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
def begin_db_transaction #:nodoc:
|
261
|
-
log("begin transaction", nil) { @connection.transaction }
|
262
|
-
end
|
263
|
-
|
264
|
-
def commit_db_transaction #:nodoc:
|
265
|
-
log("commit transaction", nil) { @connection.commit }
|
266
|
-
end
|
267
|
-
|
268
|
-
def exec_rollback_db_transaction #:nodoc:
|
269
|
-
log("rollback transaction", nil) { @connection.rollback }
|
270
|
-
end
|
271
|
-
|
272
222
|
# SCHEMA STATEMENTS ========================================
|
273
223
|
|
274
224
|
def primary_keys(table_name) # :nodoc:
|
@@ -290,11 +240,6 @@ module ActiveRecord
|
|
290
240
|
rename_table_indexes(table_name, new_name)
|
291
241
|
end
|
292
242
|
|
293
|
-
def valid_alter_table_type?(type, options = {})
|
294
|
-
!invalid_alter_table_type?(type, options)
|
295
|
-
end
|
296
|
-
deprecate :valid_alter_table_type?
|
297
|
-
|
298
243
|
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
299
244
|
if invalid_alter_table_type?(type, options)
|
300
245
|
alter_table(table_name) do |definition|
|
@@ -308,6 +253,9 @@ module ActiveRecord
|
|
308
253
|
def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
|
309
254
|
alter_table(table_name) do |definition|
|
310
255
|
definition.remove_column column_name
|
256
|
+
definition.foreign_keys.delete_if do |_, fk_options|
|
257
|
+
fk_options[:column] == column_name.to_s
|
258
|
+
end
|
311
259
|
end
|
312
260
|
end
|
313
261
|
|
@@ -366,27 +314,36 @@ module ActiveRecord
|
|
366
314
|
end
|
367
315
|
end
|
368
316
|
|
369
|
-
def
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
317
|
+
def build_insert_sql(insert) # :nodoc:
|
318
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
319
|
+
|
320
|
+
if insert.skip_duplicates?
|
321
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
322
|
+
elsif insert.update_duplicates?
|
323
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
324
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
325
|
+
end
|
326
|
+
|
327
|
+
sql
|
375
328
|
end
|
376
329
|
|
377
|
-
def
|
378
|
-
|
379
|
-
|
380
|
-
tables_to_delete.each { |table| delete "DELETE FROM #{quote_table_name(table)}", "Fixture Delete" }
|
330
|
+
def get_database_version # :nodoc:
|
331
|
+
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
332
|
+
end
|
381
333
|
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
end
|
334
|
+
def check_version # :nodoc:
|
335
|
+
if database_version < "3.8.0"
|
336
|
+
raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.8."
|
386
337
|
end
|
387
338
|
end
|
388
339
|
|
389
340
|
private
|
341
|
+
# See https://www.sqlite.org/limits.html,
|
342
|
+
# the default value is 999 when not configured.
|
343
|
+
def bind_params_length
|
344
|
+
999
|
345
|
+
end
|
346
|
+
|
390
347
|
def initialize_type_map(m = type_map)
|
391
348
|
super
|
392
349
|
register_class_with_limit m, %r(int)i, SQLite3Integer
|
@@ -405,14 +362,27 @@ module ActiveRecord
|
|
405
362
|
type.to_sym == :primary_key || options[:primary_key]
|
406
363
|
end
|
407
364
|
|
408
|
-
def alter_table(table_name,
|
365
|
+
def alter_table(table_name, foreign_keys = foreign_keys(table_name), **options)
|
409
366
|
altered_table_name = "a#{table_name}"
|
410
|
-
|
367
|
+
|
368
|
+
caller = lambda do |definition|
|
369
|
+
rename = options[:rename] || {}
|
370
|
+
foreign_keys.each do |fk|
|
371
|
+
if column = rename[fk.options[:column]]
|
372
|
+
fk.options[:column] = column
|
373
|
+
end
|
374
|
+
to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
375
|
+
definition.foreign_key(to_table, fk.options)
|
376
|
+
end
|
377
|
+
|
378
|
+
yield definition if block_given?
|
379
|
+
end
|
411
380
|
|
412
381
|
transaction do
|
413
|
-
|
414
|
-
options.merge(temporary: true))
|
415
|
-
|
382
|
+
disable_referential_integrity do
|
383
|
+
move_table(table_name, altered_table_name, options.merge(temporary: true))
|
384
|
+
move_table(altered_table_name, table_name, &caller)
|
385
|
+
end
|
416
386
|
end
|
417
387
|
end
|
418
388
|
|
@@ -442,6 +412,7 @@ module ActiveRecord
|
|
442
412
|
primary_key: column_name == from_primary_key
|
443
413
|
)
|
444
414
|
end
|
415
|
+
|
445
416
|
yield @definition if block_given?
|
446
417
|
end
|
447
418
|
copy_table_indexes(from, to, options[:rename] || {})
|
@@ -459,9 +430,12 @@ module ActiveRecord
|
|
459
430
|
name = name[1..-1]
|
460
431
|
end
|
461
432
|
|
462
|
-
|
463
|
-
|
464
|
-
to_column_names.
|
433
|
+
columns = index.columns
|
434
|
+
if columns.is_a?(Array)
|
435
|
+
to_column_names = columns(to).map(&:name)
|
436
|
+
columns = columns.map { |c| rename[c] || c }.select do |column|
|
437
|
+
to_column_names.include?(column)
|
438
|
+
end
|
465
439
|
end
|
466
440
|
|
467
441
|
unless columns.empty?
|
@@ -487,22 +461,18 @@ module ActiveRecord
|
|
487
461
|
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
|
488
462
|
end
|
489
463
|
|
490
|
-
def
|
491
|
-
@sqlite_version ||= SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
492
|
-
end
|
493
|
-
|
494
|
-
def translate_exception(exception, message)
|
464
|
+
def translate_exception(exception, message:, sql:, binds:)
|
495
465
|
case exception.message
|
496
466
|
# SQLite 3.8.2 returns a newly formatted error message:
|
497
467
|
# UNIQUE constraint failed: *table_name*.*column_name*
|
498
468
|
# Older versions of SQLite return:
|
499
469
|
# column *column_name* is not unique
|
500
470
|
when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
|
501
|
-
RecordNotUnique.new(message)
|
471
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
502
472
|
when /.* may not be NULL/, /NOT NULL constraint failed: .*/
|
503
|
-
NotNullViolation.new(message)
|
473
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
504
474
|
when /FOREIGN KEY constraint failed/i
|
505
|
-
InvalidForeignKey.new(message)
|
475
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
506
476
|
else
|
507
477
|
super
|
508
478
|
end
|
@@ -512,7 +482,7 @@ module ActiveRecord
|
|
512
482
|
|
513
483
|
def table_structure_with_collation(table_name, basic_structure)
|
514
484
|
collation_hash = {}
|
515
|
-
sql =
|
485
|
+
sql = <<~SQL
|
516
486
|
SELECT sql FROM
|
517
487
|
(SELECT * FROM sqlite_master UNION ALL
|
518
488
|
SELECT * FROM sqlite_temp_master)
|
@@ -545,7 +515,7 @@ module ActiveRecord
|
|
545
515
|
column
|
546
516
|
end
|
547
517
|
else
|
548
|
-
basic_structure.
|
518
|
+
basic_structure.to_a
|
549
519
|
end
|
550
520
|
end
|
551
521
|
|
@@ -553,7 +523,21 @@ module ActiveRecord
|
|
553
523
|
Arel::Visitors::SQLite.new(self)
|
554
524
|
end
|
555
525
|
|
526
|
+
def build_statement_pool
|
527
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
528
|
+
end
|
529
|
+
|
530
|
+
def connect
|
531
|
+
@connection = ::SQLite3::Database.new(
|
532
|
+
@config[:database].to_s,
|
533
|
+
@config.merge(results_as_hash: true)
|
534
|
+
)
|
535
|
+
configure_connection
|
536
|
+
end
|
537
|
+
|
556
538
|
def configure_connection
|
539
|
+
@connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout])) if @config[:timeout]
|
540
|
+
|
557
541
|
execute("PRAGMA foreign_keys = ON", "SCHEMA")
|
558
542
|
end
|
559
543
|
|
@@ -46,41 +46,150 @@ module ActiveRecord
|
|
46
46
|
#
|
47
47
|
# The exceptions AdapterNotSpecified, AdapterNotFound and +ArgumentError+
|
48
48
|
# may be returned on an error.
|
49
|
-
def establish_connection(
|
50
|
-
|
49
|
+
def establish_connection(config_or_env = nil)
|
50
|
+
config_hash = resolve_config_for_connection(config_or_env)
|
51
|
+
connection_handler.establish_connection(config_hash)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Connects a model to the databases specified. The +database+ keyword
|
55
|
+
# takes a hash consisting of a +role+ and a +database_key+.
|
56
|
+
#
|
57
|
+
# This will create a connection handler for switching between connections,
|
58
|
+
# look up the config hash using the +database_key+ and finally
|
59
|
+
# establishes a connection to that config.
|
60
|
+
#
|
61
|
+
# class AnimalsModel < ApplicationRecord
|
62
|
+
# self.abstract_class = true
|
63
|
+
#
|
64
|
+
# connects_to database: { writing: :primary, reading: :primary_replica }
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# Returns an array of established connections.
|
68
|
+
def connects_to(database: {})
|
69
|
+
connections = []
|
51
70
|
|
52
|
-
|
53
|
-
|
54
|
-
|
71
|
+
database.each do |role, database_key|
|
72
|
+
config_hash = resolve_config_for_connection(database_key)
|
73
|
+
handler = lookup_connection_handler(role.to_sym)
|
55
74
|
|
56
|
-
|
57
|
-
|
58
|
-
spec[:name] = spec_name
|
75
|
+
connections << handler.establish_connection(config_hash)
|
76
|
+
end
|
59
77
|
|
60
|
-
|
78
|
+
connections
|
61
79
|
end
|
62
80
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
81
|
+
# Connects to a database or role (ex writing, reading, or another
|
82
|
+
# custom role) for the duration of the block.
|
83
|
+
#
|
84
|
+
# If a role is passed, Active Record will look up the connection
|
85
|
+
# based on the requested role:
|
86
|
+
#
|
87
|
+
# ActiveRecord::Base.connected_to(role: :writing) do
|
88
|
+
# Dog.create! # creates dog using dog writing connection
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# ActiveRecord::Base.connected_to(role: :reading) do
|
92
|
+
# Dog.create! # throws exception because we're on a replica
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# ActiveRecord::Base.connected_to(role: :unknown_role) do
|
96
|
+
# # raises exception due to non-existent role
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# For cases where you may want to connect to a database outside of the model,
|
100
|
+
# you can use +connected_to+ with a +database+ argument. The +database+ argument
|
101
|
+
# expects a symbol that corresponds to the database key in your config.
|
102
|
+
#
|
103
|
+
# ActiveRecord::Base.connected_to(database: :animals_slow_replica) do
|
104
|
+
# Dog.run_a_long_query # runs a long query while connected to the +animals_slow_replica+
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# This will connect to a new database for the queries inside the block. By
|
108
|
+
# default the `:writing` role will be used since all connections must be assigned
|
109
|
+
# a role. If you would like to use a different role you can pass a hash to database:
|
110
|
+
#
|
111
|
+
# ActiveRecord::Base.connected_to(database: { readonly_slow: :animals_slow_replica }) do
|
112
|
+
# # runs a long query while connected to the +animals_slow_replica+ using the readonly_slow role.
|
113
|
+
# Dog.run_a_long_query
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# When using the database key a new connection will be established every time.
|
117
|
+
def connected_to(database: nil, role: nil, &blk)
|
118
|
+
if database && role
|
119
|
+
raise ArgumentError, "connected_to can only accept a `database` or a `role` argument, but not both arguments."
|
120
|
+
elsif database
|
121
|
+
if database.is_a?(Hash)
|
122
|
+
role, database = database.first
|
123
|
+
role = role.to_sym
|
124
|
+
end
|
125
|
+
|
126
|
+
config_hash = resolve_config_for_connection(database)
|
127
|
+
handler = lookup_connection_handler(role)
|
68
128
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
129
|
+
handler.establish_connection(config_hash)
|
130
|
+
|
131
|
+
with_handler(role, &blk)
|
132
|
+
elsif role
|
133
|
+
with_handler(role.to_sym, &blk)
|
134
|
+
else
|
135
|
+
raise ArgumentError, "must provide a `database` or a `role`."
|
73
136
|
end
|
137
|
+
end
|
74
138
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
139
|
+
# Returns true if role is the current connected role.
|
140
|
+
#
|
141
|
+
# ActiveRecord::Base.connected_to(role: :writing) do
|
142
|
+
# ActiveRecord::Base.connected_to?(role: :writing) #=> true
|
143
|
+
# ActiveRecord::Base.connected_to?(role: :reading) #=> false
|
144
|
+
# end
|
145
|
+
def connected_to?(role:)
|
146
|
+
current_role == role.to_sym
|
147
|
+
end
|
148
|
+
|
149
|
+
# Returns the symbol representing the current connected role.
|
150
|
+
#
|
151
|
+
# ActiveRecord::Base.connected_to(role: :writing) do
|
152
|
+
# ActiveRecord::Base.current_role #=> :writing
|
153
|
+
# end
|
154
|
+
#
|
155
|
+
# ActiveRecord::Base.connected_to(role: :reading) do
|
156
|
+
# ActiveRecord::Base.current_role #=> :reading
|
157
|
+
# end
|
158
|
+
def current_role
|
159
|
+
connection_handlers.key(connection_handler)
|
160
|
+
end
|
161
|
+
|
162
|
+
def lookup_connection_handler(handler_key) # :nodoc:
|
163
|
+
handler_key ||= ActiveRecord::Base.writing_role
|
164
|
+
connection_handlers[handler_key] ||= ActiveRecord::ConnectionAdapters::ConnectionHandler.new
|
165
|
+
end
|
166
|
+
|
167
|
+
def with_handler(handler_key, &blk) # :nodoc:
|
168
|
+
handler = lookup_connection_handler(handler_key)
|
169
|
+
swap_connection_handler(handler, &blk)
|
170
|
+
end
|
171
|
+
|
172
|
+
def resolve_config_for_connection(config_or_env) # :nodoc:
|
173
|
+
raise "Anonymous class is not allowed." unless name
|
174
|
+
|
175
|
+
config_or_env ||= DEFAULT_ENV.call.to_sym
|
176
|
+
pool_name = primary_class? ? "primary" : name
|
177
|
+
self.connection_specification_name = pool_name
|
178
|
+
|
179
|
+
resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(Base.configurations)
|
180
|
+
config_hash = resolver.resolve(config_or_env, pool_name).symbolize_keys
|
181
|
+
config_hash[:name] = pool_name
|
182
|
+
|
183
|
+
config_hash
|
184
|
+
end
|
185
|
+
|
186
|
+
# Clears the query cache for all connections associated with the current thread.
|
187
|
+
def clear_query_caches_for_current_thread
|
188
|
+
ActiveRecord::Base.connection_handlers.each_value do |handler|
|
189
|
+
handler.connection_pool_list.each do |pool|
|
190
|
+
pool.connection.clear_query_cache if pool.active_connection?
|
83
191
|
end
|
192
|
+
end
|
84
193
|
end
|
85
194
|
|
86
195
|
# Returns the connection currently associated with the class. This can
|
@@ -95,11 +204,15 @@ module ActiveRecord
|
|
95
204
|
# Return the specification name from the current class or its parent.
|
96
205
|
def connection_specification_name
|
97
206
|
if !defined?(@connection_specification_name) || @connection_specification_name.nil?
|
98
|
-
return
|
207
|
+
return primary_class? ? "primary" : superclass.connection_specification_name
|
99
208
|
end
|
100
209
|
@connection_specification_name
|
101
210
|
end
|
102
211
|
|
212
|
+
def primary_class? # :nodoc:
|
213
|
+
self == Base || defined?(ApplicationRecord) && self == ApplicationRecord
|
214
|
+
end
|
215
|
+
|
103
216
|
# Returns the configuration of the associated connection as a hash:
|
104
217
|
#
|
105
218
|
# ActiveRecord::Base.connection_config
|
@@ -141,5 +254,14 @@ module ActiveRecord
|
|
141
254
|
|
142
255
|
delegate :clear_active_connections!, :clear_reloadable_connections!,
|
143
256
|
:clear_all_connections!, :flush_idle_connections!, to: :connection_handler
|
257
|
+
|
258
|
+
private
|
259
|
+
|
260
|
+
def swap_connection_handler(handler, &blk) # :nodoc:
|
261
|
+
old_handler, ActiveRecord::Base.connection_handler = ActiveRecord::Base.connection_handler, handler
|
262
|
+
yield
|
263
|
+
ensure
|
264
|
+
ActiveRecord::Base.connection_handler = old_handler
|
265
|
+
end
|
144
266
|
end
|
145
267
|
end
|