activerecord 5.2.8 → 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 -788
- 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
@@ -11,7 +11,7 @@ module ActiveRecord
|
|
11
11
|
# See https://www.sqlite.org/fileformat2.html#intschema
|
12
12
|
next if row["name"].starts_with?("sqlite_")
|
13
13
|
|
14
|
-
index_sql = query_value(
|
14
|
+
index_sql = query_value(<<~SQL, "SCHEMA")
|
15
15
|
SELECT sql
|
16
16
|
FROM sqlite_master
|
17
17
|
WHERE name = #{quote(row['name'])} AND type = 'index'
|
@@ -21,19 +21,24 @@ module ActiveRecord
|
|
21
21
|
WHERE name = #{quote(row['name'])} AND type = 'index'
|
22
22
|
SQL
|
23
23
|
|
24
|
-
/\
|
24
|
+
/\bON\b\s*"?(\w+?)"?\s*\((?<expressions>.+?)\)(?:\s*WHERE\b\s*(?<where>.+))?\z/i =~ index_sql
|
25
25
|
|
26
26
|
columns = exec_query("PRAGMA index_info(#{quote(row['name'])})", "SCHEMA").map do |col|
|
27
27
|
col["name"]
|
28
28
|
end
|
29
29
|
|
30
|
-
# Add info on sort order for columns (only desc order is explicitly specified, asc is
|
31
|
-
# the default)
|
32
30
|
orders = {}
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
31
|
+
|
32
|
+
if columns.any?(&:nil?) # index created with an expression
|
33
|
+
columns = expressions
|
34
|
+
else
|
35
|
+
# Add info on sort order for columns (only desc order is explicitly specified,
|
36
|
+
# asc is the default)
|
37
|
+
if index_sql # index_sql can be null in case of primary key indexes
|
38
|
+
index_sql.scan(/"(\w+)" DESC/).flatten.each { |order_column|
|
39
|
+
orders[order_column] = :desc
|
40
|
+
}
|
41
|
+
end
|
37
42
|
end
|
38
43
|
|
39
44
|
IndexDefinition.new(
|
@@ -81,7 +86,7 @@ module ActiveRecord
|
|
81
86
|
scope = quoted_scope(name, type: type)
|
82
87
|
scope[:type] ||= "'table','view'"
|
83
88
|
|
84
|
-
sql = "SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence'"
|
89
|
+
sql = +"SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence'"
|
85
90
|
sql << " AND name = #{scope[:name]}" if scope[:name]
|
86
91
|
sql << " AND type IN (#{scope[:type]})"
|
87
92
|
sql
|
@@ -9,12 +9,14 @@ require "active_record/connection_adapters/sqlite3/schema_definitions"
|
|
9
9
|
require "active_record/connection_adapters/sqlite3/schema_dumper"
|
10
10
|
require "active_record/connection_adapters/sqlite3/schema_statements"
|
11
11
|
|
12
|
-
gem "sqlite3", "~> 1.3
|
12
|
+
gem "sqlite3", "~> 1.3.6"
|
13
13
|
require "sqlite3"
|
14
14
|
|
15
15
|
module ActiveRecord
|
16
16
|
module ConnectionHandling # :nodoc:
|
17
17
|
def sqlite3_connection(config)
|
18
|
+
config = config.symbolize_keys
|
19
|
+
|
18
20
|
# Require database.
|
19
21
|
unless config[:database]
|
20
22
|
raise ArgumentError, "No database file specified. Missing argument: database"
|
@@ -31,7 +33,7 @@ module ActiveRecord
|
|
31
33
|
|
32
34
|
db = SQLite3::Database.new(
|
33
35
|
config[:database].to_s,
|
34
|
-
results_as_hash: true
|
36
|
+
config.merge(results_as_hash: true)
|
35
37
|
)
|
36
38
|
|
37
39
|
db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]
|
@@ -54,7 +56,7 @@ module ActiveRecord
|
|
54
56
|
#
|
55
57
|
# * <tt>:database</tt> - Path to the database file.
|
56
58
|
class SQLite3Adapter < AbstractAdapter
|
57
|
-
ADAPTER_NAME = "SQLite"
|
59
|
+
ADAPTER_NAME = "SQLite"
|
58
60
|
|
59
61
|
include SQLite3::Quoting
|
60
62
|
include SQLite3::SchemaStatements
|
@@ -74,27 +76,20 @@ 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
|
|
@@ -103,7 +98,6 @@ module ActiveRecord
|
|
103
98
|
|
104
99
|
@active = true
|
105
100
|
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
|
106
|
-
|
107
101
|
configure_connection
|
108
102
|
end
|
109
103
|
|
@@ -116,7 +110,11 @@ module ActiveRecord
|
|
116
110
|
end
|
117
111
|
|
118
112
|
def supports_partial_index?
|
119
|
-
|
113
|
+
true
|
114
|
+
end
|
115
|
+
|
116
|
+
def supports_expression_index?
|
117
|
+
sqlite_version >= "3.9.0"
|
120
118
|
end
|
121
119
|
|
122
120
|
def requires_reloading?
|
@@ -124,7 +122,7 @@ module ActiveRecord
|
|
124
122
|
end
|
125
123
|
|
126
124
|
def supports_foreign_keys_in_create?
|
127
|
-
|
125
|
+
true
|
128
126
|
end
|
129
127
|
|
130
128
|
def supports_views?
|
@@ -139,10 +137,6 @@ module ActiveRecord
|
|
139
137
|
true
|
140
138
|
end
|
141
139
|
|
142
|
-
def supports_multi_insert?
|
143
|
-
sqlite_version >= "3.7.11"
|
144
|
-
end
|
145
|
-
|
146
140
|
def active?
|
147
141
|
@active
|
148
142
|
end
|
@@ -184,16 +178,23 @@ module ActiveRecord
|
|
184
178
|
true
|
185
179
|
end
|
186
180
|
|
181
|
+
def supports_lazy_transactions?
|
182
|
+
true
|
183
|
+
end
|
184
|
+
|
187
185
|
# REFERENTIAL INTEGRITY ====================================
|
188
186
|
|
189
187
|
def disable_referential_integrity # :nodoc:
|
190
|
-
|
188
|
+
old_foreign_keys = query_value("PRAGMA foreign_keys")
|
189
|
+
old_defer_foreign_keys = query_value("PRAGMA defer_foreign_keys")
|
191
190
|
|
192
191
|
begin
|
192
|
+
execute("PRAGMA defer_foreign_keys = ON")
|
193
193
|
execute("PRAGMA foreign_keys = OFF")
|
194
194
|
yield
|
195
195
|
ensure
|
196
|
-
execute("PRAGMA
|
196
|
+
execute("PRAGMA defer_foreign_keys = #{old_defer_foreign_keys}")
|
197
|
+
execute("PRAGMA foreign_keys = #{old_foreign_keys}")
|
197
198
|
end
|
198
199
|
end
|
199
200
|
|
@@ -201,12 +202,25 @@ module ActiveRecord
|
|
201
202
|
# DATABASE STATEMENTS ======================================
|
202
203
|
#++
|
203
204
|
|
205
|
+
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :explain, :select, :pragma, :release, :savepoint, :rollback) # :nodoc:
|
206
|
+
private_constant :READ_QUERY
|
207
|
+
|
208
|
+
def write_query?(sql) # :nodoc:
|
209
|
+
!READ_QUERY.match?(sql)
|
210
|
+
end
|
211
|
+
|
204
212
|
def explain(arel, binds = [])
|
205
213
|
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
|
206
214
|
SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
|
207
215
|
end
|
208
216
|
|
209
217
|
def exec_query(sql, name = nil, binds = [], prepare: false)
|
218
|
+
if preventing_writes? && write_query?(sql)
|
219
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
220
|
+
end
|
221
|
+
|
222
|
+
materialize_transactions
|
223
|
+
|
210
224
|
type_casted_binds = type_casted_binds(binds)
|
211
225
|
|
212
226
|
log(sql, name, binds, type_casted_binds) do
|
@@ -224,11 +238,8 @@ module ActiveRecord
|
|
224
238
|
stmt.close
|
225
239
|
end
|
226
240
|
else
|
227
|
-
|
228
|
-
|
229
|
-
}
|
230
|
-
stmt = cache[:stmt]
|
231
|
-
cols = cache[:cols] ||= stmt.columns
|
241
|
+
stmt = @statements[sql] ||= @connection.prepare(sql)
|
242
|
+
cols = stmt.columns
|
232
243
|
stmt.reset!
|
233
244
|
stmt.bind_params(type_casted_binds)
|
234
245
|
records = stmt.to_a
|
@@ -250,6 +261,12 @@ module ActiveRecord
|
|
250
261
|
end
|
251
262
|
|
252
263
|
def execute(sql, name = nil) #:nodoc:
|
264
|
+
if preventing_writes? && write_query?(sql)
|
265
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
266
|
+
end
|
267
|
+
|
268
|
+
materialize_transactions
|
269
|
+
|
253
270
|
log(sql, name) do
|
254
271
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
255
272
|
@connection.execute(sql)
|
@@ -290,11 +307,6 @@ module ActiveRecord
|
|
290
307
|
rename_table_indexes(table_name, new_name)
|
291
308
|
end
|
292
309
|
|
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
310
|
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
299
311
|
if invalid_alter_table_type?(type, options)
|
300
312
|
alter_table(table_name) do |definition|
|
@@ -366,14 +378,6 @@ module ActiveRecord
|
|
366
378
|
end
|
367
379
|
end
|
368
380
|
|
369
|
-
def insert_fixtures(rows, table_name)
|
370
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
371
|
-
`insert_fixtures` is deprecated and will be removed in the next version of Rails.
|
372
|
-
Consider using `insert_fixtures_set` for performance improvement.
|
373
|
-
MSG
|
374
|
-
insert_fixtures_set(table_name => rows)
|
375
|
-
end
|
376
|
-
|
377
381
|
def insert_fixtures_set(fixture_set, tables_to_delete = [])
|
378
382
|
disable_referential_integrity do
|
379
383
|
transaction(requires_new: true) do
|
@@ -387,6 +391,18 @@ module ActiveRecord
|
|
387
391
|
end
|
388
392
|
|
389
393
|
private
|
394
|
+
# See https://www.sqlite.org/limits.html,
|
395
|
+
# the default value is 999 when not configured.
|
396
|
+
def bind_params_length
|
397
|
+
999
|
398
|
+
end
|
399
|
+
|
400
|
+
def check_version
|
401
|
+
if sqlite_version < "3.8.0"
|
402
|
+
raise "Your version of SQLite (#{sqlite_version}) is too old. Active Record supports SQLite >= 3.8."
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
390
406
|
def initialize_type_map(m = type_map)
|
391
407
|
super
|
392
408
|
register_class_with_limit m, %r(int)i, SQLite3Integer
|
@@ -407,12 +423,25 @@ module ActiveRecord
|
|
407
423
|
|
408
424
|
def alter_table(table_name, options = {})
|
409
425
|
altered_table_name = "a#{table_name}"
|
410
|
-
|
426
|
+
foreign_keys = foreign_keys(table_name)
|
427
|
+
|
428
|
+
caller = lambda do |definition|
|
429
|
+
rename = options[:rename] || {}
|
430
|
+
foreign_keys.each do |fk|
|
431
|
+
if column = rename[fk.options[:column]]
|
432
|
+
fk.options[:column] = column
|
433
|
+
end
|
434
|
+
definition.foreign_key(fk.to_table, fk.options)
|
435
|
+
end
|
436
|
+
|
437
|
+
yield definition if block_given?
|
438
|
+
end
|
411
439
|
|
412
440
|
transaction do
|
413
|
-
|
414
|
-
options.merge(temporary: true))
|
415
|
-
|
441
|
+
disable_referential_integrity do
|
442
|
+
move_table(table_name, altered_table_name, options.merge(temporary: true))
|
443
|
+
move_table(altered_table_name, table_name, &caller)
|
444
|
+
end
|
416
445
|
end
|
417
446
|
end
|
418
447
|
|
@@ -442,6 +471,7 @@ module ActiveRecord
|
|
442
471
|
primary_key: column_name == from_primary_key
|
443
472
|
)
|
444
473
|
end
|
474
|
+
|
445
475
|
yield @definition if block_given?
|
446
476
|
end
|
447
477
|
copy_table_indexes(from, to, options[:rename] || {})
|
@@ -459,9 +489,12 @@ module ActiveRecord
|
|
459
489
|
name = name[1..-1]
|
460
490
|
end
|
461
491
|
|
462
|
-
|
463
|
-
|
464
|
-
to_column_names.
|
492
|
+
columns = index.columns
|
493
|
+
if columns.is_a?(Array)
|
494
|
+
to_column_names = columns(to).map(&:name)
|
495
|
+
columns = columns.map { |c| rename[c] || c }.select do |column|
|
496
|
+
to_column_names.include?(column)
|
497
|
+
end
|
465
498
|
end
|
466
499
|
|
467
500
|
unless columns.empty?
|
@@ -491,18 +524,18 @@ module ActiveRecord
|
|
491
524
|
@sqlite_version ||= SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
492
525
|
end
|
493
526
|
|
494
|
-
def translate_exception(exception, message)
|
527
|
+
def translate_exception(exception, message:, sql:, binds:)
|
495
528
|
case exception.message
|
496
529
|
# SQLite 3.8.2 returns a newly formatted error message:
|
497
530
|
# UNIQUE constraint failed: *table_name*.*column_name*
|
498
531
|
# Older versions of SQLite return:
|
499
532
|
# column *column_name* is not unique
|
500
533
|
when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
|
501
|
-
RecordNotUnique.new(message)
|
534
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
502
535
|
when /.* may not be NULL/, /NOT NULL constraint failed: .*/
|
503
|
-
NotNullViolation.new(message)
|
536
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
504
537
|
when /FOREIGN KEY constraint failed/i
|
505
|
-
InvalidForeignKey.new(message)
|
538
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
506
539
|
else
|
507
540
|
super
|
508
541
|
end
|
@@ -512,7 +545,7 @@ module ActiveRecord
|
|
512
545
|
|
513
546
|
def table_structure_with_collation(table_name, basic_structure)
|
514
547
|
collation_hash = {}
|
515
|
-
sql =
|
548
|
+
sql = <<~SQL
|
516
549
|
SELECT sql FROM
|
517
550
|
(SELECT * FROM sqlite_master UNION ALL
|
518
551
|
SELECT * FROM sqlite_temp_master)
|
@@ -525,9 +558,9 @@ module ActiveRecord
|
|
525
558
|
result = exec_query(sql, "SCHEMA").first
|
526
559
|
|
527
560
|
if result
|
528
|
-
# Splitting with left parentheses and
|
561
|
+
# Splitting with left parentheses and picking up last will return all
|
529
562
|
# columns separated with comma(,).
|
530
|
-
columns_string = result["sql"].split("("
|
563
|
+
columns_string = result["sql"].split("(").last
|
531
564
|
|
532
565
|
columns_string.split(",").each do |column_string|
|
533
566
|
# This regex will match the column name and collation type and will save
|
@@ -545,7 +578,7 @@ module ActiveRecord
|
|
545
578
|
column
|
546
579
|
end
|
547
580
|
else
|
548
|
-
basic_structure.
|
581
|
+
basic_structure.to_a
|
549
582
|
end
|
550
583
|
end
|
551
584
|
|
@@ -46,41 +46,138 @@ 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
|
51
53
|
|
52
|
-
|
53
|
-
|
54
|
-
|
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 = []
|
55
70
|
|
56
|
-
|
57
|
-
|
58
|
-
|
71
|
+
database.each do |role, database_key|
|
72
|
+
config_hash = resolve_config_for_connection(database_key)
|
73
|
+
handler = lookup_connection_handler(role.to_sym)
|
74
|
+
|
75
|
+
connections << handler.establish_connection(config_hash)
|
76
|
+
end
|
59
77
|
|
60
|
-
|
78
|
+
connections
|
61
79
|
end
|
62
80
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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 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_ode) 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
|
+
# This will connect to a new database for the queries inside the block.
|
104
|
+
#
|
105
|
+
# ActiveRecord::Base.connected_to(database: :animals_slow_replica) do
|
106
|
+
# Dog.run_a_long_query # runs a long query while connected to the +animals_slow_replica+
|
107
|
+
# end
|
108
|
+
def connected_to(database: nil, role: nil, &blk)
|
109
|
+
if database && role
|
110
|
+
raise ArgumentError, "connected_to can only accept a `database` or a `role` argument, but not both arguments."
|
111
|
+
elsif database
|
112
|
+
if database.is_a?(Hash)
|
113
|
+
role, database = database.first
|
114
|
+
role = role.to_sym
|
115
|
+
else
|
116
|
+
role = database.to_sym
|
117
|
+
end
|
118
|
+
|
119
|
+
config_hash = resolve_config_for_connection(database)
|
120
|
+
handler = lookup_connection_handler(role)
|
121
|
+
|
122
|
+
with_handler(role) do
|
123
|
+
handler.establish_connection(config_hash)
|
124
|
+
yield
|
125
|
+
end
|
126
|
+
elsif role
|
127
|
+
with_handler(role.to_sym, &blk)
|
128
|
+
else
|
129
|
+
raise ArgumentError, "must provide a `database` or a `role`."
|
67
130
|
end
|
131
|
+
end
|
68
132
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
133
|
+
# Returns true if role is the current connected role.
|
134
|
+
#
|
135
|
+
# ActiveRecord::Base.connected_to(role: :writing) do
|
136
|
+
# ActiveRecord::Base.connected_to?(role: :writing) #=> true
|
137
|
+
# ActiveRecord::Base.connected_to?(role: :reading) #=> false
|
138
|
+
# end
|
139
|
+
def connected_to?(role:)
|
140
|
+
current_role == role.to_sym
|
141
|
+
end
|
142
|
+
|
143
|
+
# Returns the symbol representing the current connected role.
|
144
|
+
#
|
145
|
+
# ActiveRecord::Base.connected_to(role: :writing) do
|
146
|
+
# ActiveRecord::Base.current_role #=> :writing
|
147
|
+
# end
|
148
|
+
#
|
149
|
+
# ActiveRecord::Base.connected_to(role: :reading) do
|
150
|
+
# ActiveRecord::Base.current_role #=> :reading
|
151
|
+
# end
|
152
|
+
def current_role
|
153
|
+
connection_handlers.key(connection_handler)
|
154
|
+
end
|
155
|
+
|
156
|
+
def lookup_connection_handler(handler_key) # :nodoc:
|
157
|
+
connection_handlers[handler_key] ||= ActiveRecord::ConnectionAdapters::ConnectionHandler.new
|
158
|
+
end
|
159
|
+
|
160
|
+
def with_handler(handler_key, &blk) # :nodoc:
|
161
|
+
unless ActiveRecord::Base.connection_handlers.keys.include?(handler_key)
|
162
|
+
raise ArgumentError, "The #{handler_key} role does not exist. Add it by establishing a connection with `connects_to` or use an existing role (#{ActiveRecord::Base.connection_handlers.keys.join(", ")})."
|
73
163
|
end
|
74
164
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
165
|
+
handler = lookup_connection_handler(handler_key)
|
166
|
+
swap_connection_handler(handler, &blk)
|
167
|
+
end
|
168
|
+
|
169
|
+
def resolve_config_for_connection(config_or_env) # :nodoc:
|
170
|
+
raise "Anonymous class is not allowed." unless name
|
171
|
+
|
172
|
+
config_or_env ||= DEFAULT_ENV.call.to_sym
|
173
|
+
pool_name = self == Base ? "primary" : name
|
174
|
+
self.connection_specification_name = pool_name
|
175
|
+
|
176
|
+
resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(Base.configurations)
|
177
|
+
config_hash = resolver.resolve(config_or_env, pool_name).symbolize_keys
|
178
|
+
config_hash[:name] = pool_name
|
179
|
+
|
180
|
+
config_hash
|
84
181
|
end
|
85
182
|
|
86
183
|
# Returns the connection currently associated with the class. This can
|
@@ -141,5 +238,14 @@ module ActiveRecord
|
|
141
238
|
|
142
239
|
delegate :clear_active_connections!, :clear_reloadable_connections!,
|
143
240
|
:clear_all_connections!, :flush_idle_connections!, to: :connection_handler
|
241
|
+
|
242
|
+
private
|
243
|
+
|
244
|
+
def swap_connection_handler(handler, &blk) # :nodoc:
|
245
|
+
old_handler, ActiveRecord::Base.connection_handler = ActiveRecord::Base.connection_handler, handler
|
246
|
+
yield
|
247
|
+
ensure
|
248
|
+
ActiveRecord::Base.connection_handler = old_handler
|
249
|
+
end
|
144
250
|
end
|
145
251
|
end
|