activerecord 5.2.4 → 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 +617 -581
- data/MIT-LICENSE +3 -1
- data/README.rdoc +4 -2
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +9 -2
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/associations.rb +19 -14
- 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.rb +24 -28
- 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/preloader.rb +40 -32
- data/lib/active_record/associations/preloader/association.rb +38 -36
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods.rb +28 -100
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +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/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.rb +233 -0
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +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.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +100 -81
- data/lib/active_record/migration/command_recorder.rb +50 -6
- data/lib/active_record/migration/compatibility.rb +76 -49
- 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.rb +310 -80
- 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.rb +4 -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/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/result.rb +30 -11
- data/lib/active_record/sanitization.rb +32 -40
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +22 -7
- data/lib/active_record/schema_migration.rb +5 -1
- data/lib/active_record/scoping.rb +8 -8
- data/lib/active_record/scoping/default.rb +4 -5
- data/lib/active_record/scoping/named.rb +19 -15
- 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.rb +3 -4
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type_caster/connection.rb +15 -14
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +15 -27
- data/lib/arel.rb +51 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +108 -26
- data/lib/active_record/collection_cache_key.rb +0 -53
@@ -13,6 +13,7 @@ module ActiveRecord
|
|
13
13
|
@columns_hash = {}
|
14
14
|
@primary_keys = {}
|
15
15
|
@data_sources = {}
|
16
|
+
@indexes = {}
|
16
17
|
end
|
17
18
|
|
18
19
|
def initialize_dup(other)
|
@@ -21,22 +22,27 @@ module ActiveRecord
|
|
21
22
|
@columns_hash = @columns_hash.dup
|
22
23
|
@primary_keys = @primary_keys.dup
|
23
24
|
@data_sources = @data_sources.dup
|
25
|
+
@indexes = @indexes.dup
|
24
26
|
end
|
25
27
|
|
26
28
|
def encode_with(coder)
|
27
|
-
coder["columns"]
|
28
|
-
coder["columns_hash"]
|
29
|
-
coder["primary_keys"]
|
30
|
-
coder["data_sources"]
|
31
|
-
coder["
|
29
|
+
coder["columns"] = @columns
|
30
|
+
coder["columns_hash"] = @columns_hash
|
31
|
+
coder["primary_keys"] = @primary_keys
|
32
|
+
coder["data_sources"] = @data_sources
|
33
|
+
coder["indexes"] = @indexes
|
34
|
+
coder["version"] = connection.migration_context.current_version
|
35
|
+
coder["database_version"] = database_version
|
32
36
|
end
|
33
37
|
|
34
38
|
def init_with(coder)
|
35
|
-
@columns
|
36
|
-
@columns_hash
|
37
|
-
@primary_keys
|
38
|
-
@data_sources
|
39
|
-
@
|
39
|
+
@columns = coder["columns"]
|
40
|
+
@columns_hash = coder["columns_hash"]
|
41
|
+
@primary_keys = coder["primary_keys"]
|
42
|
+
@data_sources = coder["data_sources"]
|
43
|
+
@indexes = coder["indexes"] || {}
|
44
|
+
@version = coder["version"]
|
45
|
+
@database_version = coder["database_version"]
|
40
46
|
end
|
41
47
|
|
42
48
|
def primary_keys(table_name)
|
@@ -57,6 +63,7 @@ module ActiveRecord
|
|
57
63
|
primary_keys(table_name)
|
58
64
|
columns(table_name)
|
59
65
|
columns_hash(table_name)
|
66
|
+
indexes(table_name)
|
60
67
|
end
|
61
68
|
end
|
62
69
|
|
@@ -77,17 +84,32 @@ module ActiveRecord
|
|
77
84
|
}]
|
78
85
|
end
|
79
86
|
|
87
|
+
# Checks whether the columns hash is already cached for a table.
|
88
|
+
def columns_hash?(table_name)
|
89
|
+
@columns_hash.key?(table_name)
|
90
|
+
end
|
91
|
+
|
92
|
+
def indexes(table_name)
|
93
|
+
@indexes[table_name] ||= connection.indexes(table_name)
|
94
|
+
end
|
95
|
+
|
96
|
+
def database_version # :nodoc:
|
97
|
+
@database_version ||= connection.get_database_version
|
98
|
+
end
|
99
|
+
|
80
100
|
# Clears out internal caches
|
81
101
|
def clear!
|
82
102
|
@columns.clear
|
83
103
|
@columns_hash.clear
|
84
104
|
@primary_keys.clear
|
85
105
|
@data_sources.clear
|
106
|
+
@indexes.clear
|
86
107
|
@version = nil
|
108
|
+
@database_version = nil
|
87
109
|
end
|
88
110
|
|
89
111
|
def size
|
90
|
-
[@columns, @columns_hash, @primary_keys, @data_sources].
|
112
|
+
[@columns, @columns_hash, @primary_keys, @data_sources].sum(&:size)
|
91
113
|
end
|
92
114
|
|
93
115
|
# Clear out internal caches for the data source +name+.
|
@@ -96,20 +118,21 @@ module ActiveRecord
|
|
96
118
|
@columns_hash.delete name
|
97
119
|
@primary_keys.delete name
|
98
120
|
@data_sources.delete name
|
121
|
+
@indexes.delete name
|
99
122
|
end
|
100
123
|
|
101
124
|
def marshal_dump
|
102
125
|
# if we get current version during initialization, it happens stack over flow.
|
103
126
|
@version = connection.migration_context.current_version
|
104
|
-
[@version, @columns, @columns_hash, @primary_keys, @data_sources]
|
127
|
+
[@version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, database_version]
|
105
128
|
end
|
106
129
|
|
107
130
|
def marshal_load(array)
|
108
|
-
@version, @columns, @columns_hash, @primary_keys, @data_sources = array
|
131
|
+
@version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
|
132
|
+
@indexes = @indexes || {}
|
109
133
|
end
|
110
134
|
|
111
135
|
private
|
112
|
-
|
113
136
|
def prepare_data_sources
|
114
137
|
connection.data_sources.each { |source| @data_sources[source] = true }
|
115
138
|
end
|
@@ -16,19 +16,22 @@ module ActiveRecord
|
|
16
16
|
|
17
17
|
def ==(other)
|
18
18
|
other.is_a?(SqlTypeMetadata) &&
|
19
|
-
|
19
|
+
sql_type == other.sql_type &&
|
20
|
+
type == other.type &&
|
21
|
+
limit == other.limit &&
|
22
|
+
precision == other.precision &&
|
23
|
+
scale == other.scale
|
20
24
|
end
|
21
25
|
alias eql? ==
|
22
26
|
|
23
27
|
def hash
|
24
|
-
|
28
|
+
SqlTypeMetadata.hash ^
|
29
|
+
sql_type.hash ^
|
30
|
+
type.hash ^
|
31
|
+
limit.hash ^
|
32
|
+
precision.hash >> 1 ^
|
33
|
+
scale.hash >> 2
|
25
34
|
end
|
26
|
-
|
27
|
-
protected
|
28
|
-
|
29
|
-
def attributes_for_hash
|
30
|
-
[self.class, sql_type, type, limit, precision, scale]
|
31
|
-
end
|
32
35
|
end
|
33
36
|
end
|
34
37
|
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module SQLite3
|
6
|
+
module DatabaseStatements
|
7
|
+
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :explain, :select, :pragma, :release, :savepoint, :rollback) # :nodoc:
|
8
|
+
private_constant :READ_QUERY
|
9
|
+
|
10
|
+
def write_query?(sql) # :nodoc:
|
11
|
+
!READ_QUERY.match?(sql)
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute(sql, name = nil) #:nodoc:
|
15
|
+
if preventing_writes? && write_query?(sql)
|
16
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
17
|
+
end
|
18
|
+
|
19
|
+
materialize_transactions
|
20
|
+
|
21
|
+
log(sql, name) do
|
22
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
23
|
+
@connection.execute(sql)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def exec_query(sql, name = nil, binds = [], prepare: false)
|
29
|
+
if preventing_writes? && write_query?(sql)
|
30
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
31
|
+
end
|
32
|
+
|
33
|
+
materialize_transactions
|
34
|
+
|
35
|
+
type_casted_binds = type_casted_binds(binds)
|
36
|
+
|
37
|
+
log(sql, name, binds, type_casted_binds) do
|
38
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
39
|
+
# Don't cache statements if they are not prepared
|
40
|
+
unless prepare
|
41
|
+
stmt = @connection.prepare(sql)
|
42
|
+
begin
|
43
|
+
cols = stmt.columns
|
44
|
+
unless without_prepared_statement?(binds)
|
45
|
+
stmt.bind_params(type_casted_binds)
|
46
|
+
end
|
47
|
+
records = stmt.to_a
|
48
|
+
ensure
|
49
|
+
stmt.close
|
50
|
+
end
|
51
|
+
else
|
52
|
+
stmt = @statements[sql] ||= @connection.prepare(sql)
|
53
|
+
cols = stmt.columns
|
54
|
+
stmt.reset!
|
55
|
+
stmt.bind_params(type_casted_binds)
|
56
|
+
records = stmt.to_a
|
57
|
+
end
|
58
|
+
|
59
|
+
ActiveRecord::Result.new(cols, records)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def exec_delete(sql, name = "SQL", binds = [])
|
65
|
+
exec_query(sql, name, binds)
|
66
|
+
@connection.changes
|
67
|
+
end
|
68
|
+
alias :exec_update :exec_delete
|
69
|
+
|
70
|
+
def begin_db_transaction #:nodoc:
|
71
|
+
log("begin transaction", nil) { @connection.transaction }
|
72
|
+
end
|
73
|
+
|
74
|
+
def commit_db_transaction #:nodoc:
|
75
|
+
log("commit transaction", nil) { @connection.commit }
|
76
|
+
end
|
77
|
+
|
78
|
+
def exec_rollback_db_transaction #:nodoc:
|
79
|
+
log("rollback transaction", nil) { @connection.rollback }
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
private
|
84
|
+
def execute_batch(sql, name = nil)
|
85
|
+
if preventing_writes? && write_query?(sql)
|
86
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
87
|
+
end
|
88
|
+
|
89
|
+
materialize_transactions
|
90
|
+
|
91
|
+
log(sql, name) do
|
92
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
93
|
+
@connection.execute_batch2(sql)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def last_inserted_id(result)
|
99
|
+
@connection.last_insert_row_id
|
100
|
+
end
|
101
|
+
|
102
|
+
def build_fixture_statements(fixture_set)
|
103
|
+
fixture_set.flat_map do |table_name, fixtures|
|
104
|
+
next if fixtures.empty?
|
105
|
+
fixtures.map { |fixture| build_fixture_sql([fixture], table_name) }
|
106
|
+
end.compact
|
107
|
+
end
|
108
|
+
|
109
|
+
def build_truncate_statements(*table_names)
|
110
|
+
truncate_tables = table_names.map do |table_name|
|
111
|
+
"DELETE FROM #{quote_table_name(table_name)}"
|
112
|
+
end
|
113
|
+
combine_multi_statements(truncate_tables)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -13,11 +13,11 @@ module ActiveRecord
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def quote_table_name(name)
|
16
|
-
|
16
|
+
self.class.quoted_table_names[name] ||= super.gsub(".", "\".\"").freeze
|
17
17
|
end
|
18
18
|
|
19
19
|
def quote_column_name(name)
|
20
|
-
|
20
|
+
self.class.quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}")
|
21
21
|
end
|
22
22
|
|
23
23
|
def quoted_time(value)
|
@@ -30,21 +30,57 @@ module ActiveRecord
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def quoted_true
|
33
|
-
|
33
|
+
"1"
|
34
34
|
end
|
35
35
|
|
36
36
|
def unquoted_true
|
37
|
-
|
37
|
+
1
|
38
38
|
end
|
39
39
|
|
40
40
|
def quoted_false
|
41
|
-
|
41
|
+
"0"
|
42
42
|
end
|
43
43
|
|
44
44
|
def unquoted_false
|
45
|
-
|
45
|
+
0
|
46
46
|
end
|
47
47
|
|
48
|
+
def column_name_matcher
|
49
|
+
COLUMN_NAME
|
50
|
+
end
|
51
|
+
|
52
|
+
def column_name_with_order_matcher
|
53
|
+
COLUMN_NAME_WITH_ORDER
|
54
|
+
end
|
55
|
+
|
56
|
+
COLUMN_NAME = /
|
57
|
+
\A
|
58
|
+
(
|
59
|
+
(?:
|
60
|
+
# "table_name"."column_name" | function(one or no argument)
|
61
|
+
((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
|
62
|
+
)
|
63
|
+
(?:\s+AS\s+(?:\w+|"\w+"))?
|
64
|
+
)
|
65
|
+
(?:\s*,\s*\g<1>)*
|
66
|
+
\z
|
67
|
+
/ix
|
68
|
+
|
69
|
+
COLUMN_NAME_WITH_ORDER = /
|
70
|
+
\A
|
71
|
+
(
|
72
|
+
(?:
|
73
|
+
# "table_name"."column_name" | function(one or no argument)
|
74
|
+
((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
|
75
|
+
)
|
76
|
+
(?:\s+ASC|\s+DESC)?
|
77
|
+
)
|
78
|
+
(?:\s*,\s*\g<1>)*
|
79
|
+
\z
|
80
|
+
/ix
|
81
|
+
|
82
|
+
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
83
|
+
|
48
84
|
private
|
49
85
|
|
50
86
|
def _type_cast(value)
|
@@ -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(
|
@@ -47,6 +52,32 @@ module ActiveRecord
|
|
47
52
|
end.compact
|
48
53
|
end
|
49
54
|
|
55
|
+
def add_foreign_key(from_table, to_table, **options)
|
56
|
+
alter_table(from_table) do |definition|
|
57
|
+
to_table = strip_table_name_prefix_and_suffix(to_table)
|
58
|
+
definition.foreign_key(to_table, options)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def remove_foreign_key(from_table, to_table = nil, **options)
|
63
|
+
to_table ||= options[:to_table]
|
64
|
+
options = options.except(:name, :to_table)
|
65
|
+
foreign_keys = foreign_keys(from_table)
|
66
|
+
|
67
|
+
fkey = foreign_keys.detect do |fk|
|
68
|
+
table = to_table || begin
|
69
|
+
table = options[:column].to_s.delete_suffix("_id")
|
70
|
+
Base.pluralize_table_names ? table.pluralize : table
|
71
|
+
end
|
72
|
+
table = strip_table_name_prefix_and_suffix(table)
|
73
|
+
fk_to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
74
|
+
fk_to_table == table && options.all? { |k, v| fk.options[k].to_s == v.to_s }
|
75
|
+
end || raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
|
76
|
+
|
77
|
+
foreign_keys.delete(fkey)
|
78
|
+
alter_table(from_table, foreign_keys)
|
79
|
+
end
|
80
|
+
|
50
81
|
def create_schema_dumper(options)
|
51
82
|
SQLite3::SchemaDumper.create(self, options)
|
52
83
|
end
|
@@ -57,7 +88,7 @@ module ActiveRecord
|
|
57
88
|
end
|
58
89
|
|
59
90
|
def create_table_definition(*args)
|
60
|
-
SQLite3::TableDefinition.new(*args)
|
91
|
+
SQLite3::TableDefinition.new(self, *args)
|
61
92
|
end
|
62
93
|
|
63
94
|
def new_column_from_field(table_name, field)
|
@@ -74,14 +105,14 @@ module ActiveRecord
|
|
74
105
|
end
|
75
106
|
|
76
107
|
type_metadata = fetch_type_metadata(field["type"])
|
77
|
-
Column.new(field["name"], default, type_metadata, field["notnull"].to_i == 0,
|
108
|
+
Column.new(field["name"], default, type_metadata, field["notnull"].to_i == 0, collation: field["collation"])
|
78
109
|
end
|
79
110
|
|
80
111
|
def data_source_sql(name = nil, type: nil)
|
81
112
|
scope = quoted_scope(name, type: type)
|
82
113
|
scope[:type] ||= "'table','view'"
|
83
114
|
|
84
|
-
sql = "SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence'"
|
115
|
+
sql = +"SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence'"
|
85
116
|
sql << " AND name = #{scope[:name]}" if scope[:name]
|
86
117
|
sql << " AND type IN (#{scope[:type]})"
|
87
118
|
sql
|
@@ -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
|
|