activerecord 6.0.6 → 6.1.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 +783 -910
- data/MIT-LICENSE +1 -1
- data/README.rdoc +3 -3
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +22 -14
- data/lib/active_record/associations/alias_tracker.rb +19 -15
- data/lib/active_record/associations/association.rb +43 -26
- data/lib/active_record/associations/association_scope.rb +11 -15
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +9 -3
- data/lib/active_record/associations/builder/belongs_to.rb +10 -7
- data/lib/active_record/associations/builder/collection_association.rb +5 -4
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
- data/lib/active_record/associations/builder/has_many.rb +6 -2
- data/lib/active_record/associations/builder/has_one.rb +11 -14
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +19 -13
- data/lib/active_record/associations/collection_proxy.rb +12 -5
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -2
- data/lib/active_record/associations/has_many_through_association.rb +10 -4
- data/lib/active_record/associations/has_one_association.rb +15 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +29 -14
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +63 -49
- data/lib/active_record/associations/preloader/association.rb +13 -5
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +5 -3
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations.rb +114 -11
- data/lib/active_record/attribute_assignment.rb +10 -8
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
- data/lib/active_record/attribute_methods/dirty.rb +1 -11
- data/lib/active_record/attribute_methods/primary_key.rb +6 -2
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -11
- data/lib/active_record/attribute_methods/serialization.rb +11 -5
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
- data/lib/active_record/attribute_methods/write.rb +12 -20
- data/lib/active_record/attribute_methods.rb +64 -54
- data/lib/active_record/attributes.rb +32 -7
- data/lib/active_record/autosave_association.rb +47 -30
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +152 -22
- data/lib/active_record/coders/yaml_column.rb +2 -24
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +185 -134
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
- data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +110 -30
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
- data/lib/active_record/connection_adapters/abstract/transaction.rb +80 -32
- data/lib/active_record/connection_adapters/abstract_adapter.rb +49 -72
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
- data/lib/active_record/connection_adapters/column.rb +15 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -24
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -3
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
- data/lib/active_record/connection_adapters/pool_config.rb +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +12 -53
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
- data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +30 -5
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_handling.rb +210 -71
- data/lib/active_record/core.rb +223 -66
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +52 -9
- data/lib/active_record/database_configurations/hash_config.rb +54 -8
- data/lib/active_record/database_configurations/url_config.rb +15 -40
- data/lib/active_record/database_configurations.rb +124 -85
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/enum.rb +27 -10
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -4
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +1 -2
- data/lib/active_record/fixture_set/render_context.rb +1 -1
- data/lib/active_record/fixture_set/table_row.rb +2 -2
- data/lib/active_record/fixtures.rb +54 -8
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +40 -18
- data/lib/active_record/insert_all.rb +34 -5
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +16 -7
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +13 -16
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +26 -8
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
- data/lib/active_record/middleware/database_selector.rb +4 -1
- data/lib/active_record/migration/command_recorder.rb +47 -27
- data/lib/active_record/migration/compatibility.rb +67 -17
- data/lib/active_record/migration.rb +113 -83
- data/lib/active_record/model_schema.rb +88 -13
- data/lib/active_record/nested_attributes.rb +2 -3
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +50 -45
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +11 -6
- data/lib/active_record/railtie.rb +64 -44
- data/lib/active_record/railties/databases.rake +266 -95
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +60 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +38 -31
- data/lib/active_record/relation/calculations.rb +100 -43
- data/lib/active_record/relation/finder_methods.rb +44 -14
- data/lib/active_record/relation/from_clause.rb +1 -1
- data/lib/active_record/relation/merger.rb +20 -23
- data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +57 -33
- data/lib/active_record/relation/query_methods.rb +318 -195
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -7
- data/lib/active_record/relation/where_clause.rb +104 -57
- data/lib/active_record/relation.rb +90 -64
- data/lib/active_record/result.rb +41 -33
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +6 -17
- data/lib/active_record/schema_dumper.rb +34 -4
- data/lib/active_record/schema_migration.rb +2 -8
- data/lib/active_record/scoping/named.rb +1 -17
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +20 -4
- data/lib/active_record/store.rb +2 -2
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +39 -51
- data/lib/active_record/tasks/database_tasks.rb +139 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +36 -33
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/touch_later.rb +21 -21
- data/lib/active_record/transactions.rb +15 -64
- data/lib/active_record/type/serialized.rb +6 -2
- data/lib/active_record/type.rb +8 -1
- data/lib/active_record/type_caster/connection.rb +0 -1
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record.rb +7 -14
- data/lib/arel/attributes/attribute.rb +4 -0
- data/lib/arel/collectors/bind.rb +5 -0
- data/lib/arel/collectors/composite.rb +8 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/nodes/binary.rb +82 -8
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/casted.rb +21 -9
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/node.rb +7 -6
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/sql_literal.rb +3 -0
- data/lib/arel/nodes/table_alias.rb +7 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes.rb +3 -1
- data/lib/arel/predications.rb +12 -18
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors/dot.rb +14 -2
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -4
- data/lib/arel/visitors/to_sql.rb +89 -78
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +5 -13
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
- data/lib/rails/generators/active_record/migration.rb +6 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +28 -30
- data/lib/active_record/advisory_lock_base.rb +0 -18
- data/lib/active_record/attribute_decorators.rb +0 -88
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -203
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -156
- data/lib/arel/visitors/oracle.rb +0 -158
- data/lib/arel/visitors/oracle12.rb +0 -65
- data/lib/arel/visitors/where_sql.rb +0 -22
@@ -1,8 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/file/atomic"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module ConnectionAdapters
|
5
7
|
class SchemaCache
|
8
|
+
def self.load_from(filename)
|
9
|
+
return unless File.file?(filename)
|
10
|
+
|
11
|
+
read(filename) do |file|
|
12
|
+
filename.include?(".dump") ? Marshal.load(file) : YAML.load(file)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.read(filename, &block)
|
17
|
+
if File.extname(filename) == ".gz"
|
18
|
+
Zlib::GzipReader.open(filename) { |gz|
|
19
|
+
yield gz.read
|
20
|
+
}
|
21
|
+
else
|
22
|
+
yield File.read(filename)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
private_class_method :read
|
26
|
+
|
6
27
|
attr_reader :version
|
7
28
|
attr_accessor :connection
|
8
29
|
|
@@ -26,27 +47,33 @@ module ActiveRecord
|
|
26
47
|
end
|
27
48
|
|
28
49
|
def encode_with(coder)
|
50
|
+
reset_version!
|
51
|
+
|
29
52
|
coder["columns"] = @columns
|
30
|
-
coder["columns_hash"] = @columns_hash
|
31
53
|
coder["primary_keys"] = @primary_keys
|
32
54
|
coder["data_sources"] = @data_sources
|
33
55
|
coder["indexes"] = @indexes
|
34
|
-
coder["version"] =
|
56
|
+
coder["version"] = @version
|
35
57
|
coder["database_version"] = database_version
|
36
58
|
end
|
37
59
|
|
38
60
|
def init_with(coder)
|
39
61
|
@columns = coder["columns"]
|
40
|
-
@columns_hash = coder["columns_hash"]
|
41
62
|
@primary_keys = coder["primary_keys"]
|
42
63
|
@data_sources = coder["data_sources"]
|
43
64
|
@indexes = coder["indexes"] || {}
|
44
65
|
@version = coder["version"]
|
45
66
|
@database_version = coder["database_version"]
|
67
|
+
|
68
|
+
derive_columns_hash_and_deduplicate_values
|
46
69
|
end
|
47
70
|
|
48
71
|
def primary_keys(table_name)
|
49
|
-
@primary_keys
|
72
|
+
@primary_keys.fetch(table_name) do
|
73
|
+
if data_source_exists?(table_name)
|
74
|
+
@primary_keys[deep_deduplicate(table_name)] = deep_deduplicate(connection.primary_key(table_name))
|
75
|
+
end
|
76
|
+
end
|
50
77
|
end
|
51
78
|
|
52
79
|
# A cached lookup for table existence.
|
@@ -54,7 +81,7 @@ module ActiveRecord
|
|
54
81
|
prepare_data_sources if @data_sources.empty?
|
55
82
|
return @data_sources[name] if @data_sources.key? name
|
56
83
|
|
57
|
-
@data_sources[name] = connection.data_source_exists?(name)
|
84
|
+
@data_sources[deep_deduplicate(name)] = connection.data_source_exists?(name)
|
58
85
|
end
|
59
86
|
|
60
87
|
# Add internal cache for table with +table_name+.
|
@@ -73,15 +100,17 @@ module ActiveRecord
|
|
73
100
|
|
74
101
|
# Get the columns for a table
|
75
102
|
def columns(table_name)
|
76
|
-
@columns
|
103
|
+
@columns.fetch(table_name) do
|
104
|
+
@columns[deep_deduplicate(table_name)] = deep_deduplicate(connection.columns(table_name))
|
105
|
+
end
|
77
106
|
end
|
78
107
|
|
79
108
|
# Get the columns for a table as a hash, key is the column name
|
80
109
|
# value is the column object.
|
81
110
|
def columns_hash(table_name)
|
82
|
-
@columns_hash
|
83
|
-
[
|
84
|
-
|
111
|
+
@columns_hash.fetch(table_name) do
|
112
|
+
@columns_hash[deep_deduplicate(table_name)] = columns(table_name).index_by(&:name).freeze
|
113
|
+
end
|
85
114
|
end
|
86
115
|
|
87
116
|
# Checks whether the columns hash is already cached for a table.
|
@@ -90,7 +119,9 @@ module ActiveRecord
|
|
90
119
|
end
|
91
120
|
|
92
121
|
def indexes(table_name)
|
93
|
-
@indexes
|
122
|
+
@indexes.fetch(table_name) do
|
123
|
+
@indexes[deep_deduplicate(table_name)] = deep_deduplicate(connection.indexes(table_name))
|
124
|
+
end
|
94
125
|
end
|
95
126
|
|
96
127
|
def database_version # :nodoc:
|
@@ -121,21 +152,73 @@ module ActiveRecord
|
|
121
152
|
@indexes.delete name
|
122
153
|
end
|
123
154
|
|
155
|
+
def dump_to(filename)
|
156
|
+
clear!
|
157
|
+
connection.data_sources.each { |table| add(table) }
|
158
|
+
open(filename) { |f|
|
159
|
+
if filename.include?(".dump")
|
160
|
+
f.write(Marshal.dump(self))
|
161
|
+
else
|
162
|
+
f.write(YAML.dump(self))
|
163
|
+
end
|
164
|
+
}
|
165
|
+
end
|
166
|
+
|
124
167
|
def marshal_dump
|
125
|
-
|
126
|
-
|
127
|
-
[@version, @columns,
|
168
|
+
reset_version!
|
169
|
+
|
170
|
+
[@version, @columns, {}, @primary_keys, @data_sources, @indexes, database_version]
|
128
171
|
end
|
129
172
|
|
130
173
|
def marshal_load(array)
|
131
|
-
@version, @columns,
|
132
|
-
@indexes
|
174
|
+
@version, @columns, _columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
|
175
|
+
@indexes ||= {}
|
176
|
+
|
177
|
+
derive_columns_hash_and_deduplicate_values
|
133
178
|
end
|
134
179
|
|
135
180
|
private
|
181
|
+
def reset_version!
|
182
|
+
@version = connection.migration_context.current_version
|
183
|
+
end
|
184
|
+
|
185
|
+
def derive_columns_hash_and_deduplicate_values
|
186
|
+
@columns = deep_deduplicate(@columns)
|
187
|
+
@columns_hash = @columns.transform_values { |columns| columns.index_by(&:name) }
|
188
|
+
@primary_keys = deep_deduplicate(@primary_keys)
|
189
|
+
@data_sources = deep_deduplicate(@data_sources)
|
190
|
+
@indexes = deep_deduplicate(@indexes)
|
191
|
+
end
|
192
|
+
|
193
|
+
def deep_deduplicate(value)
|
194
|
+
case value
|
195
|
+
when Hash
|
196
|
+
value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
|
197
|
+
when Array
|
198
|
+
value.map { |i| deep_deduplicate(i) }
|
199
|
+
when String, Deduplicable
|
200
|
+
-value
|
201
|
+
else
|
202
|
+
value
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
136
206
|
def prepare_data_sources
|
137
207
|
connection.data_sources.each { |source| @data_sources[source] = true }
|
138
208
|
end
|
209
|
+
|
210
|
+
def open(filename)
|
211
|
+
File.atomic_write(filename) do |file|
|
212
|
+
if File.extname(filename) == ".gz"
|
213
|
+
zipper = Zlib::GzipWriter.new file
|
214
|
+
yield zipper
|
215
|
+
zipper.flush
|
216
|
+
zipper.close
|
217
|
+
else
|
218
|
+
yield file
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
139
222
|
end
|
140
223
|
end
|
141
224
|
end
|
@@ -1,9 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_record/connection_adapters/deduplicable"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
# :stopdoc:
|
5
7
|
module ConnectionAdapters
|
6
8
|
class SqlTypeMetadata
|
9
|
+
include Deduplicable
|
10
|
+
|
7
11
|
attr_reader :sql_type, :type, :limit, :precision, :scale
|
8
12
|
|
9
13
|
def initialize(sql_type: nil, type: nil, limit: nil, precision: nil, scale: nil)
|
@@ -32,6 +36,12 @@ module ActiveRecord
|
|
32
36
|
precision.hash >> 1 ^
|
33
37
|
scale.hash >> 2
|
34
38
|
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def deduplicated
|
42
|
+
@sql_type = -sql_type
|
43
|
+
super
|
44
|
+
end
|
35
45
|
end
|
36
46
|
end
|
37
47
|
end
|
@@ -13,12 +13,18 @@ module ActiveRecord
|
|
13
13
|
!READ_QUERY.match?(sql)
|
14
14
|
end
|
15
15
|
|
16
|
+
def explain(arel, binds = [])
|
17
|
+
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
|
18
|
+
SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
|
19
|
+
end
|
20
|
+
|
16
21
|
def execute(sql, name = nil) #:nodoc:
|
17
22
|
if preventing_writes? && write_query?(sql)
|
18
23
|
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
19
24
|
end
|
20
25
|
|
21
26
|
materialize_transactions
|
27
|
+
mark_transaction_written_if_write(sql)
|
22
28
|
|
23
29
|
log(sql, name) do
|
24
30
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
@@ -33,6 +39,7 @@ module ActiveRecord
|
|
33
39
|
end
|
34
40
|
|
35
41
|
materialize_transactions
|
42
|
+
mark_transaction_written_if_write(sql)
|
36
43
|
|
37
44
|
type_casted_binds = type_casted_binds(binds)
|
38
45
|
|
@@ -58,7 +65,7 @@ module ActiveRecord
|
|
58
65
|
records = stmt.to_a
|
59
66
|
end
|
60
67
|
|
61
|
-
|
68
|
+
build_result(columns: cols, rows: records)
|
62
69
|
end
|
63
70
|
end
|
64
71
|
end
|
@@ -69,20 +76,37 @@ module ActiveRecord
|
|
69
76
|
end
|
70
77
|
alias :exec_update :exec_delete
|
71
78
|
|
79
|
+
def begin_isolated_db_transaction(isolation) #:nodoc
|
80
|
+
raise TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
|
81
|
+
raise StandardError, "You need to enable the shared-cache mode in SQLite mode before attempting to change the transaction isolation level" unless shared_cache?
|
82
|
+
|
83
|
+
Thread.current.thread_variable_set("read_uncommitted", @connection.get_first_value("PRAGMA read_uncommitted"))
|
84
|
+
@connection.read_uncommitted = true
|
85
|
+
begin_db_transaction
|
86
|
+
end
|
87
|
+
|
72
88
|
def begin_db_transaction #:nodoc:
|
73
|
-
log("begin transaction",
|
89
|
+
log("begin transaction", "TRANSACTION") { @connection.transaction }
|
74
90
|
end
|
75
91
|
|
76
92
|
def commit_db_transaction #:nodoc:
|
77
|
-
log("commit transaction",
|
93
|
+
log("commit transaction", "TRANSACTION") { @connection.commit }
|
94
|
+
reset_read_uncommitted
|
78
95
|
end
|
79
96
|
|
80
97
|
def exec_rollback_db_transaction #:nodoc:
|
81
|
-
log("rollback transaction",
|
98
|
+
log("rollback transaction", "TRANSACTION") { @connection.rollback }
|
99
|
+
reset_read_uncommitted
|
82
100
|
end
|
83
101
|
|
84
|
-
|
85
102
|
private
|
103
|
+
def reset_read_uncommitted
|
104
|
+
read_uncommitted = Thread.current.thread_variable_get("read_uncommitted")
|
105
|
+
return unless read_uncommitted
|
106
|
+
|
107
|
+
@connection.read_uncommitted = read_uncommitted
|
108
|
+
end
|
109
|
+
|
86
110
|
def execute_batch(statements, name = nil)
|
87
111
|
sql = combine_multi_statements(statements)
|
88
112
|
|
@@ -91,6 +115,7 @@ module ActiveRecord
|
|
91
115
|
end
|
92
116
|
|
93
117
|
materialize_transactions
|
118
|
+
mark_transaction_written_if_write(sql)
|
94
119
|
|
95
120
|
log(sql, name) do
|
96
121
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
@@ -3,8 +3,12 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module ConnectionAdapters
|
5
5
|
module SQLite3
|
6
|
-
class SchemaCreation <
|
6
|
+
class SchemaCreation < SchemaCreation # :nodoc:
|
7
7
|
private
|
8
|
+
def supports_index_using?
|
9
|
+
false
|
10
|
+
end
|
11
|
+
|
8
12
|
def add_column_options!(sql, options)
|
9
13
|
if options[:collation]
|
10
14
|
sql << " COLLATE \"#{options[:collation]}\""
|
@@ -9,7 +9,7 @@ module ActiveRecord
|
|
9
9
|
exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").map do |row|
|
10
10
|
# Indexes SQLite creates implicitly for internal use start with "sqlite_".
|
11
11
|
# See https://www.sqlite.org/fileformat2.html#intschema
|
12
|
-
next if row["name"].
|
12
|
+
next if row["name"].start_with?("sqlite_")
|
13
13
|
|
14
14
|
index_sql = query_value(<<~SQL, "SCHEMA")
|
15
15
|
SELECT sql
|
@@ -78,6 +78,35 @@ module ActiveRecord
|
|
78
78
|
alter_table(from_table, foreign_keys)
|
79
79
|
end
|
80
80
|
|
81
|
+
def check_constraints(table_name)
|
82
|
+
table_sql = query_value(<<-SQL, "SCHEMA")
|
83
|
+
SELECT sql
|
84
|
+
FROM sqlite_master
|
85
|
+
WHERE name = #{quote_table_name(table_name)} AND type = 'table'
|
86
|
+
UNION ALL
|
87
|
+
SELECT sql
|
88
|
+
FROM sqlite_temp_master
|
89
|
+
WHERE name = #{quote_table_name(table_name)} AND type = 'table'
|
90
|
+
SQL
|
91
|
+
|
92
|
+
table_sql.to_s.scan(/CONSTRAINT\s+(?<name>\w+)\s+CHECK\s+\((?<expression>(:?[^()]|\(\g<expression>\))+)\)/i).map do |name, expression|
|
93
|
+
CheckConstraintDefinition.new(table_name, expression, name: name)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def add_check_constraint(table_name, expression, **options)
|
98
|
+
alter_table(table_name) do |definition|
|
99
|
+
definition.check_constraint(expression, **options)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def remove_check_constraint(table_name, expression = nil, **options)
|
104
|
+
check_constraints = check_constraints(table_name)
|
105
|
+
chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
|
106
|
+
check_constraints.delete_if { |chk| chk.name == chk_name_to_delete }
|
107
|
+
alter_table(table_name, foreign_keys(table_name), check_constraints)
|
108
|
+
end
|
109
|
+
|
81
110
|
def create_schema_dumper(options)
|
82
111
|
SQLite3::SchemaDumper.create(self, options)
|
83
112
|
end
|
@@ -87,8 +116,12 @@ module ActiveRecord
|
|
87
116
|
SQLite3::SchemaCreation.new(self)
|
88
117
|
end
|
89
118
|
|
90
|
-
def create_table_definition(
|
91
|
-
SQLite3::TableDefinition.new(self,
|
119
|
+
def create_table_definition(name, **options)
|
120
|
+
SQLite3::TableDefinition.new(self, name, **options)
|
121
|
+
end
|
122
|
+
|
123
|
+
def validate_index_length!(table_name, new_name, internal = false)
|
124
|
+
super unless internal
|
92
125
|
end
|
93
126
|
|
94
127
|
def new_column_from_field(table_name, field)
|
@@ -26,7 +26,7 @@ module ActiveRecord
|
|
26
26
|
# Allow database path relative to Rails.root, but only if the database
|
27
27
|
# path is not the special path that tells sqlite to build a database only
|
28
28
|
# in memory.
|
29
|
-
if ":memory:" != config[:database]
|
29
|
+
if ":memory:" != config[:database] && !config[:database].to_s.start_with?("file:")
|
30
30
|
config[:database] = File.expand_path(config[:database], Rails.root) if defined?(Rails.root)
|
31
31
|
dirname = File.dirname(config[:database])
|
32
32
|
Dir.mkdir(dirname) unless File.directory?(dirname)
|
@@ -48,8 +48,8 @@ module ActiveRecord
|
|
48
48
|
end
|
49
49
|
|
50
50
|
module ConnectionAdapters #:nodoc:
|
51
|
-
# The SQLite3 adapter works
|
52
|
-
#
|
51
|
+
# The SQLite3 adapter works with the sqlite3-ruby drivers
|
52
|
+
# (available as gem from https://rubygems.org/gems/sqlite3).
|
53
53
|
#
|
54
54
|
# Options:
|
55
55
|
#
|
@@ -76,16 +76,6 @@ module ActiveRecord
|
|
76
76
|
json: { name: "json" },
|
77
77
|
}
|
78
78
|
|
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
|
88
|
-
|
89
79
|
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
90
80
|
private
|
91
81
|
def dealloc(stmt)
|
@@ -116,6 +106,10 @@ module ActiveRecord
|
|
116
106
|
true
|
117
107
|
end
|
118
108
|
|
109
|
+
def supports_transaction_isolation?
|
110
|
+
true
|
111
|
+
end
|
112
|
+
|
119
113
|
def supports_partial_index?
|
120
114
|
true
|
121
115
|
end
|
@@ -132,6 +126,10 @@ module ActiveRecord
|
|
132
126
|
true
|
133
127
|
end
|
134
128
|
|
129
|
+
def supports_check_constraints?
|
130
|
+
true
|
131
|
+
end
|
132
|
+
|
135
133
|
def supports_views?
|
136
134
|
true
|
137
135
|
end
|
@@ -175,13 +173,6 @@ module ActiveRecord
|
|
175
173
|
true
|
176
174
|
end
|
177
175
|
|
178
|
-
# Returns 62. SQLite supports index names up to 64
|
179
|
-
# characters. The rest is used by Rails internally to perform
|
180
|
-
# temporary rename operations
|
181
|
-
def allowed_index_name_length
|
182
|
-
index_name_length - 2
|
183
|
-
end
|
184
|
-
|
185
176
|
def native_database_types #:nodoc:
|
186
177
|
NATIVE_DATABASE_TYPES
|
187
178
|
end
|
@@ -215,14 +206,6 @@ module ActiveRecord
|
|
215
206
|
end
|
216
207
|
end
|
217
208
|
|
218
|
-
#--
|
219
|
-
# DATABASE STATEMENTS ======================================
|
220
|
-
#++
|
221
|
-
def explain(arel, binds = [])
|
222
|
-
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
|
223
|
-
SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
|
224
|
-
end
|
225
|
-
|
226
209
|
# SCHEMA STATEMENTS ========================================
|
227
210
|
|
228
211
|
def primary_keys(table_name) # :nodoc:
|
@@ -230,8 +213,11 @@ module ActiveRecord
|
|
230
213
|
pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
|
231
214
|
end
|
232
215
|
|
233
|
-
def remove_index(table_name,
|
234
|
-
|
216
|
+
def remove_index(table_name, column_name = nil, **options) # :nodoc:
|
217
|
+
return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
|
218
|
+
|
219
|
+
index_name = index_name_for_remove(table_name, column_name, options)
|
220
|
+
|
235
221
|
exec_query "DROP INDEX #{quote_column_name(index_name)}"
|
236
222
|
end
|
237
223
|
|
@@ -240,6 +226,8 @@ module ActiveRecord
|
|
240
226
|
# Example:
|
241
227
|
# rename_table('octopuses', 'octopi')
|
242
228
|
def rename_table(table_name, new_name)
|
229
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
230
|
+
schema_cache.clear_data_source_cache!(new_name.to_s)
|
243
231
|
exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
244
232
|
rename_table_indexes(table_name, new_name)
|
245
233
|
end
|
@@ -280,16 +268,10 @@ module ActiveRecord
|
|
280
268
|
end
|
281
269
|
end
|
282
270
|
|
283
|
-
def change_column(table_name, column_name, type, options
|
271
|
+
def change_column(table_name, column_name, type, **options) #:nodoc:
|
284
272
|
alter_table(table_name) do |definition|
|
285
273
|
definition[column_name].instance_eval do
|
286
|
-
self.type
|
287
|
-
self.limit = options[:limit] if options.include?(:limit)
|
288
|
-
self.default = options[:default] if options.include?(:default)
|
289
|
-
self.null = options[:null] if options.include?(:null)
|
290
|
-
self.precision = options[:precision] if options.include?(:precision)
|
291
|
-
self.scale = options[:scale] if options.include?(:scale)
|
292
|
-
self.collation = options[:collation] if options.include?(:collation)
|
274
|
+
self.type = type
|
293
275
|
self.options.merge!(options)
|
294
276
|
end
|
295
277
|
end
|
@@ -326,12 +308,17 @@ module ActiveRecord
|
|
326
308
|
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
327
309
|
elsif insert.update_duplicates?
|
328
310
|
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
311
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{column} IS excluded.#{column}" }
|
329
312
|
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
330
313
|
end
|
331
314
|
|
332
315
|
sql
|
333
316
|
end
|
334
317
|
|
318
|
+
def shared_cache? # :nodoc:
|
319
|
+
@config.fetch(:flags, 0).anybits?(::SQLite3::Constants::Open::SHAREDCACHE)
|
320
|
+
end
|
321
|
+
|
335
322
|
def get_database_version # :nodoc:
|
336
323
|
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
337
324
|
end
|
@@ -368,7 +355,12 @@ module ActiveRecord
|
|
368
355
|
options[:null] == false && options[:default].nil?
|
369
356
|
end
|
370
357
|
|
371
|
-
def alter_table(
|
358
|
+
def alter_table(
|
359
|
+
table_name,
|
360
|
+
foreign_keys = foreign_keys(table_name),
|
361
|
+
check_constraints = check_constraints(table_name),
|
362
|
+
**options
|
363
|
+
)
|
372
364
|
altered_table_name = "a#{table_name}"
|
373
365
|
|
374
366
|
caller = lambda do |definition|
|
@@ -381,6 +373,10 @@ module ActiveRecord
|
|
381
373
|
definition.foreign_key(to_table, **fk.options)
|
382
374
|
end
|
383
375
|
|
376
|
+
check_constraints.each do |chk|
|
377
|
+
definition.check_constraint(chk.expression, **chk.options)
|
378
|
+
end
|
379
|
+
|
384
380
|
yield definition if block_given?
|
385
381
|
end
|
386
382
|
|
@@ -405,6 +401,7 @@ module ActiveRecord
|
|
405
401
|
if from_primary_key.is_a?(Array)
|
406
402
|
@definition.primary_keys from_primary_key
|
407
403
|
end
|
404
|
+
|
408
405
|
columns(from).each do |column|
|
409
406
|
column_name = options[:rename] ?
|
410
407
|
(options[:rename][column.name] ||
|
@@ -446,10 +443,10 @@ module ActiveRecord
|
|
446
443
|
|
447
444
|
unless columns.empty?
|
448
445
|
# index name can't be the same
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
add_index(to, columns,
|
446
|
+
options = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
|
447
|
+
options[:unique] = true if index.unique
|
448
|
+
options[:where] = index.where if index.where
|
449
|
+
add_index(to, columns, **options)
|
453
450
|
end
|
454
451
|
end
|
455
452
|
end
|
@@ -468,17 +465,18 @@ module ActiveRecord
|
|
468
465
|
end
|
469
466
|
|
470
467
|
def translate_exception(exception, message:, sql:, binds:)
|
471
|
-
case exception.message
|
472
468
|
# SQLite 3.8.2 returns a newly formatted error message:
|
473
469
|
# UNIQUE constraint failed: *table_name*.*column_name*
|
474
470
|
# Older versions of SQLite return:
|
475
471
|
# column *column_name* is not unique
|
476
|
-
|
472
|
+
if exception.message.match?(/(column(s)? .* (is|are) not unique|UNIQUE constraint failed: .*)/i)
|
477
473
|
RecordNotUnique.new(message, sql: sql, binds: binds)
|
478
|
-
|
474
|
+
elsif exception.message.match?(/(.* may not be NULL|NOT NULL constraint failed: .*)/i)
|
479
475
|
NotNullViolation.new(message, sql: sql, binds: binds)
|
480
|
-
|
476
|
+
elsif exception.message.match?(/FOREIGN KEY constraint failed/i)
|
481
477
|
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
478
|
+
elsif exception.message.match?(/called on a closed database/i)
|
479
|
+
ConnectionNotEstablished.new(exception)
|
482
480
|
else
|
483
481
|
super
|
484
482
|
end
|
@@ -498,12 +496,12 @@ module ActiveRecord
|
|
498
496
|
# Result will have following sample string
|
499
497
|
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
500
498
|
# "password_digest" varchar COLLATE "NOCASE");
|
501
|
-
result =
|
499
|
+
result = query_value(sql, "SCHEMA")
|
502
500
|
|
503
501
|
if result
|
504
502
|
# Splitting with left parentheses and discarding the first part will return all
|
505
503
|
# columns separated with comma(,).
|
506
|
-
columns_string = result
|
504
|
+
columns_string = result.split("(", 2).last
|
507
505
|
|
508
506
|
columns_string.split(",").each do |column_string|
|
509
507
|
# This regex will match the column name and collation type and will save
|
@@ -511,7 +509,7 @@ module ActiveRecord
|
|
511
509
|
collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
|
512
510
|
end
|
513
511
|
|
514
|
-
basic_structure.map
|
512
|
+
basic_structure.map do |column|
|
515
513
|
column_name = column["name"]
|
516
514
|
|
517
515
|
if collation_hash.has_key? column_name
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
extend ActiveSupport::Autoload
|
6
|
+
|
7
|
+
eager_autoload do
|
8
|
+
autoload :AbstractAdapter
|
9
|
+
end
|
10
|
+
|
11
|
+
autoload :Column
|
12
|
+
autoload :PoolConfig
|
13
|
+
autoload :PoolManager
|
14
|
+
autoload :LegacyPoolManager
|
15
|
+
|
16
|
+
autoload_at "active_record/connection_adapters/abstract/schema_definitions" do
|
17
|
+
autoload :IndexDefinition
|
18
|
+
autoload :ColumnDefinition
|
19
|
+
autoload :ChangeColumnDefinition
|
20
|
+
autoload :ForeignKeyDefinition
|
21
|
+
autoload :CheckConstraintDefinition
|
22
|
+
autoload :TableDefinition
|
23
|
+
autoload :Table
|
24
|
+
autoload :AlterTable
|
25
|
+
autoload :ReferenceDefinition
|
26
|
+
end
|
27
|
+
|
28
|
+
autoload_at "active_record/connection_adapters/abstract/connection_pool" do
|
29
|
+
autoload :ConnectionHandler
|
30
|
+
end
|
31
|
+
|
32
|
+
autoload_under "abstract" do
|
33
|
+
autoload :SchemaStatements
|
34
|
+
autoload :DatabaseStatements
|
35
|
+
autoload :DatabaseLimits
|
36
|
+
autoload :Quoting
|
37
|
+
autoload :ConnectionPool
|
38
|
+
autoload :QueryCache
|
39
|
+
autoload :Savepoints
|
40
|
+
end
|
41
|
+
|
42
|
+
autoload_at "active_record/connection_adapters/abstract/transaction" do
|
43
|
+
autoload :TransactionManager
|
44
|
+
autoload :NullTransaction
|
45
|
+
autoload :RealTransaction
|
46
|
+
autoload :SavepointTransaction
|
47
|
+
autoload :TransactionState
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|