activerecord 5.2.4.4 → 6.0.3.4
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 +777 -552
- data/MIT-LICENSE +3 -1
- data/README.rdoc +5 -3
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +10 -2
- data/lib/active_record/advisory_lock_base.rb +18 -0
- data/lib/active_record/aggregations.rb +4 -3
- data/lib/active_record/association_relation.rb +10 -8
- data/lib/active_record/associations.rb +21 -16
- data/lib/active_record/associations/alias_tracker.rb +0 -1
- data/lib/active_record/associations/association.rb +56 -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 -40
- 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 +12 -23
- data/lib/active_record/associations/collection_proxy.rb +13 -17
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +2 -11
- 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 +37 -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 +39 -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 -11
- data/lib/active_record/attribute_decorators.rb +0 -2
- data/lib/active_record/attribute_methods.rb +28 -100
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -2
- data/lib/active_record/attribute_methods/dirty.rb +111 -40
- data/lib/active_record/attribute_methods/primary_key.rb +15 -24
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +15 -54
- data/lib/active_record/attribute_methods/serialization.rb +1 -2
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -3
- data/lib/active_record/attribute_methods/write.rb +17 -25
- data/lib/active_record/attributes.rb +13 -1
- data/lib/active_record/autosave_association.rb +3 -5
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +6 -21
- data/lib/active_record/coders/yaml_column.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +103 -18
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +102 -124
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -9
- data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +20 -14
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +100 -72
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +175 -79
- data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -57
- data/lib/active_record/connection_adapters/abstract_adapter.rb +191 -43
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +142 -215
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +54 -45
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +70 -14
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +0 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +4 -6
- 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 +132 -16
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -10
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +26 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.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 +5 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -3
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +63 -75
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +168 -75
- 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 +119 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -12
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +135 -146
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_handling.rb +139 -26
- data/lib/active_record/core.rb +103 -61
- data/lib/active_record/counter_cache.rb +8 -30
- 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 +78 -0
- data/lib/active_record/dynamic_matchers.rb +3 -4
- data/lib/active_record/enum.rb +37 -7
- data/lib/active_record/errors.rb +15 -7
- data/lib/active_record/explain.rb +1 -2
- 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 +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +144 -474
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +13 -6
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +68 -16
- data/lib/active_record/internal_metadata.rb +11 -3
- data/lib/active_record/locking/optimistic.rb +5 -7
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +8 -27
- data/lib/active_record/middleware/database_selector.rb +74 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +104 -85
- data/lib/active_record/migration/command_recorder.rb +54 -22
- data/lib/active_record/migration/compatibility.rb +79 -52
- data/lib/active_record/migration/join_table.rb +0 -1
- data/lib/active_record/model_schema.rb +33 -11
- data/lib/active_record/nested_attributes.rb +2 -4
- data/lib/active_record/no_touching.rb +9 -2
- data/lib/active_record/null_relation.rb +0 -1
- data/lib/active_record/persistence.rb +232 -29
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +33 -21
- 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 +199 -46
- data/lib/active_record/reflection.rb +40 -38
- data/lib/active_record/relation.rb +322 -80
- data/lib/active_record/relation/batches.rb +13 -11
- data/lib/active_record/relation/calculations.rb +54 -48
- data/lib/active_record/relation/delegation.rb +33 -49
- data/lib/active_record/relation/finder_methods.rb +23 -28
- data/lib/active_record/relation/from_clause.rb +4 -0
- data/lib/active_record/relation/merger.rb +11 -21
- data/lib/active_record/relation/predicate_builder.rb +5 -11
- 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 +221 -70
- data/lib/active_record/relation/spawn_methods.rb +1 -2
- data/lib/active_record/relation/where_clause.rb +14 -11
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/result.rb +30 -12
- 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 +6 -2
- data/lib/active_record/scoping.rb +8 -9
- data/lib/active_record/scoping/default.rb +4 -6
- data/lib/active_record/scoping/named.rb +21 -17
- data/lib/active_record/statement_cache.rb +30 -3
- data/lib/active_record/store.rb +87 -8
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +23 -15
- data/lib/active_record/tasks/database_tasks.rb +194 -25
- data/lib/active_record/tasks/mysql_database_tasks.rb +5 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -8
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -9
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +225 -0
- data/lib/active_record/timestamp.rb +39 -26
- data/lib/active_record/touch_later.rb +5 -4
- data/lib/active_record/transactions.rb +64 -73
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +3 -13
- data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
- data/lib/active_record/type/serialized.rb +0 -1
- data/lib/active_record/type/type_map.rb +0 -1
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- 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 +3 -3
- data/lib/active_record/validations/associated.rb +1 -2
- data/lib/active_record/validations/uniqueness.rb +15 -27
- data/lib/arel.rb +62 -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 +256 -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 +203 -0
- data/lib/arel/visitors/dot.rb +296 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +156 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +158 -0
- data/lib/arel/visitors/oracle12.rb +65 -0
- data/lib/arel/visitors/postgresql.rb +109 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +888 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors/where_sql.rb +22 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/migration.rb +14 -2
- 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 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +115 -29
- 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,119 @@
|
|
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(
|
8
|
+
:begin, :commit, :explain, :select, :pragma, :release, :savepoint, :rollback, :with
|
9
|
+
) # :nodoc:
|
10
|
+
private_constant :READ_QUERY
|
11
|
+
|
12
|
+
def write_query?(sql) # :nodoc:
|
13
|
+
!READ_QUERY.match?(sql)
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute(sql, name = nil) #:nodoc:
|
17
|
+
if preventing_writes? && write_query?(sql)
|
18
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
19
|
+
end
|
20
|
+
|
21
|
+
materialize_transactions
|
22
|
+
|
23
|
+
log(sql, name) do
|
24
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
25
|
+
@connection.execute(sql)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def exec_query(sql, name = nil, binds = [], prepare: false)
|
31
|
+
if preventing_writes? && write_query?(sql)
|
32
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
33
|
+
end
|
34
|
+
|
35
|
+
materialize_transactions
|
36
|
+
|
37
|
+
type_casted_binds = type_casted_binds(binds)
|
38
|
+
|
39
|
+
log(sql, name, binds, type_casted_binds) do
|
40
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
41
|
+
# Don't cache statements if they are not prepared
|
42
|
+
unless prepare
|
43
|
+
stmt = @connection.prepare(sql)
|
44
|
+
begin
|
45
|
+
cols = stmt.columns
|
46
|
+
unless without_prepared_statement?(binds)
|
47
|
+
stmt.bind_params(type_casted_binds)
|
48
|
+
end
|
49
|
+
records = stmt.to_a
|
50
|
+
ensure
|
51
|
+
stmt.close
|
52
|
+
end
|
53
|
+
else
|
54
|
+
stmt = @statements[sql] ||= @connection.prepare(sql)
|
55
|
+
cols = stmt.columns
|
56
|
+
stmt.reset!
|
57
|
+
stmt.bind_params(type_casted_binds)
|
58
|
+
records = stmt.to_a
|
59
|
+
end
|
60
|
+
|
61
|
+
ActiveRecord::Result.new(cols, records)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def exec_delete(sql, name = "SQL", binds = [])
|
67
|
+
exec_query(sql, name, binds)
|
68
|
+
@connection.changes
|
69
|
+
end
|
70
|
+
alias :exec_update :exec_delete
|
71
|
+
|
72
|
+
def begin_db_transaction #:nodoc:
|
73
|
+
log("begin transaction", nil) { @connection.transaction }
|
74
|
+
end
|
75
|
+
|
76
|
+
def commit_db_transaction #:nodoc:
|
77
|
+
log("commit transaction", nil) { @connection.commit }
|
78
|
+
end
|
79
|
+
|
80
|
+
def exec_rollback_db_transaction #:nodoc:
|
81
|
+
log("rollback transaction", nil) { @connection.rollback }
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
private
|
86
|
+
def execute_batch(statements, name = nil)
|
87
|
+
sql = combine_multi_statements(statements)
|
88
|
+
|
89
|
+
if preventing_writes? && write_query?(sql)
|
90
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
91
|
+
end
|
92
|
+
|
93
|
+
materialize_transactions
|
94
|
+
|
95
|
+
log(sql, name) do
|
96
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
97
|
+
@connection.execute_batch2(sql)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def last_inserted_id(result)
|
103
|
+
@connection.last_insert_row_id
|
104
|
+
end
|
105
|
+
|
106
|
+
def build_fixture_statements(fixture_set)
|
107
|
+
fixture_set.flat_map do |table_name, fixtures|
|
108
|
+
next if fixtures.empty?
|
109
|
+
fixtures.map { |fixture| build_fixture_sql([fixture], table_name) }
|
110
|
+
end.compact
|
111
|
+
end
|
112
|
+
|
113
|
+
def build_truncate_statement(table_name)
|
114
|
+
"DELETE FROM #{quote_table_name(table_name)}"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
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,23 +30,58 @@ 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
|
-
|
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
|
49
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
|
+
|
84
|
+
private
|
50
85
|
def _type_cast(value)
|
51
86
|
case value
|
52
87
|
when BigDecimal
|
@@ -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
|
@@ -56,8 +87,8 @@ module ActiveRecord
|
|
56
87
|
SQLite3::SchemaCreation.new(self)
|
57
88
|
end
|
58
89
|
|
59
|
-
def create_table_definition(*args)
|
60
|
-
SQLite3::TableDefinition.new(*args)
|
90
|
+
def create_table_definition(*args, **options)
|
91
|
+
SQLite3::TableDefinition.new(self, *args, **options)
|
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
|
+
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,33 @@ module ActiveRecord
|
|
139
144
|
true
|
140
145
|
end
|
141
146
|
|
142
|
-
def
|
143
|
-
|
147
|
+
def supports_common_table_expressions?
|
148
|
+
database_version >= "3.8.3"
|
149
|
+
end
|
150
|
+
|
151
|
+
def supports_insert_on_conflict?
|
152
|
+
database_version >= "3.24.0"
|
144
153
|
end
|
154
|
+
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
155
|
+
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
156
|
+
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
145
157
|
|
146
158
|
def active?
|
147
|
-
|
159
|
+
!@connection.closed?
|
160
|
+
end
|
161
|
+
|
162
|
+
def reconnect!
|
163
|
+
super
|
164
|
+
connect if @connection.closed?
|
148
165
|
end
|
149
166
|
|
150
167
|
# Disconnects from the database if already connected. Otherwise, this
|
151
168
|
# method does nothing.
|
152
169
|
def disconnect!
|
153
170
|
super
|
154
|
-
@active = false
|
155
171
|
@connection.close rescue nil
|
156
172
|
end
|
157
173
|
|
158
|
-
# Clears the prepared statements cache.
|
159
|
-
def clear_cache!
|
160
|
-
@statements.clear
|
161
|
-
end
|
162
|
-
|
163
174
|
def supports_index_sort_order?
|
164
175
|
true
|
165
176
|
end
|
@@ -184,91 +195,34 @@ module ActiveRecord
|
|
184
195
|
true
|
185
196
|
end
|
186
197
|
|
198
|
+
def supports_lazy_transactions?
|
199
|
+
true
|
200
|
+
end
|
201
|
+
|
187
202
|
# REFERENTIAL INTEGRITY ====================================
|
188
203
|
|
189
204
|
def disable_referential_integrity # :nodoc:
|
190
|
-
|
205
|
+
old_foreign_keys = query_value("PRAGMA foreign_keys")
|
206
|
+
old_defer_foreign_keys = query_value("PRAGMA defer_foreign_keys")
|
191
207
|
|
192
208
|
begin
|
209
|
+
execute("PRAGMA defer_foreign_keys = ON")
|
193
210
|
execute("PRAGMA foreign_keys = OFF")
|
194
211
|
yield
|
195
212
|
ensure
|
196
|
-
execute("PRAGMA
|
213
|
+
execute("PRAGMA defer_foreign_keys = #{old_defer_foreign_keys}")
|
214
|
+
execute("PRAGMA foreign_keys = #{old_foreign_keys}")
|
197
215
|
end
|
198
216
|
end
|
199
217
|
|
200
218
|
#--
|
201
219
|
# DATABASE STATEMENTS ======================================
|
202
220
|
#++
|
203
|
-
|
204
221
|
def explain(arel, binds = [])
|
205
222
|
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
|
206
223
|
SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
|
207
224
|
end
|
208
225
|
|
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
226
|
# SCHEMA STATEMENTS ========================================
|
273
227
|
|
274
228
|
def primary_keys(table_name) # :nodoc:
|
@@ -290,24 +244,22 @@ module ActiveRecord
|
|
290
244
|
rename_table_indexes(table_name, new_name)
|
291
245
|
end
|
292
246
|
|
293
|
-
def
|
294
|
-
!invalid_alter_table_type?(type, options)
|
295
|
-
end
|
296
|
-
deprecate :valid_alter_table_type?
|
297
|
-
|
298
|
-
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
247
|
+
def add_column(table_name, column_name, type, **options) #:nodoc:
|
299
248
|
if invalid_alter_table_type?(type, options)
|
300
249
|
alter_table(table_name) do |definition|
|
301
|
-
definition.column(column_name, type, options)
|
250
|
+
definition.column(column_name, type, **options)
|
302
251
|
end
|
303
252
|
else
|
304
253
|
super
|
305
254
|
end
|
306
255
|
end
|
307
256
|
|
308
|
-
def remove_column(table_name, column_name, type = nil, options
|
257
|
+
def remove_column(table_name, column_name, type = nil, **options) #:nodoc:
|
309
258
|
alter_table(table_name) do |definition|
|
310
259
|
definition.remove_column column_name
|
260
|
+
definition.foreign_keys.delete_if do |_, fk_options|
|
261
|
+
fk_options[:column] == column_name.to_s
|
262
|
+
end
|
311
263
|
end
|
312
264
|
end
|
313
265
|
|
@@ -366,27 +318,36 @@ module ActiveRecord
|
|
366
318
|
end
|
367
319
|
end
|
368
320
|
|
369
|
-
def
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
321
|
+
def build_insert_sql(insert) # :nodoc:
|
322
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
323
|
+
|
324
|
+
if insert.skip_duplicates?
|
325
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
326
|
+
elsif insert.update_duplicates?
|
327
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
328
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
329
|
+
end
|
330
|
+
|
331
|
+
sql
|
375
332
|
end
|
376
333
|
|
377
|
-
def
|
378
|
-
|
379
|
-
|
380
|
-
tables_to_delete.each { |table| delete "DELETE FROM #{quote_table_name(table)}", "Fixture Delete" }
|
334
|
+
def get_database_version # :nodoc:
|
335
|
+
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
336
|
+
end
|
381
337
|
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
end
|
338
|
+
def check_version # :nodoc:
|
339
|
+
if database_version < "3.8.0"
|
340
|
+
raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.8."
|
386
341
|
end
|
387
342
|
end
|
388
343
|
|
389
344
|
private
|
345
|
+
# See https://www.sqlite.org/limits.html,
|
346
|
+
# the default value is 999 when not configured.
|
347
|
+
def bind_params_length
|
348
|
+
999
|
349
|
+
end
|
350
|
+
|
390
351
|
def initialize_type_map(m = type_map)
|
391
352
|
super
|
392
353
|
register_class_with_limit m, %r(int)i, SQLite3Integer
|
@@ -402,17 +363,31 @@ module ActiveRecord
|
|
402
363
|
# See: https://www.sqlite.org/lang_altertable.html
|
403
364
|
# SQLite has an additional restriction on the ALTER TABLE statement
|
404
365
|
def invalid_alter_table_type?(type, options)
|
405
|
-
type.to_sym == :primary_key || options[:primary_key]
|
366
|
+
type.to_sym == :primary_key || options[:primary_key] ||
|
367
|
+
options[:null] == false && options[:default].nil?
|
406
368
|
end
|
407
369
|
|
408
|
-
def alter_table(table_name,
|
370
|
+
def alter_table(table_name, foreign_keys = foreign_keys(table_name), **options)
|
409
371
|
altered_table_name = "a#{table_name}"
|
410
|
-
|
372
|
+
|
373
|
+
caller = lambda do |definition|
|
374
|
+
rename = options[:rename] || {}
|
375
|
+
foreign_keys.each do |fk|
|
376
|
+
if column = rename[fk.options[:column]]
|
377
|
+
fk.options[:column] = column
|
378
|
+
end
|
379
|
+
to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
380
|
+
definition.foreign_key(to_table, **fk.options)
|
381
|
+
end
|
382
|
+
|
383
|
+
yield definition if block_given?
|
384
|
+
end
|
411
385
|
|
412
386
|
transaction do
|
413
|
-
|
414
|
-
options.merge(temporary: true))
|
415
|
-
|
387
|
+
disable_referential_integrity do
|
388
|
+
move_table(table_name, altered_table_name, options.merge(temporary: true))
|
389
|
+
move_table(altered_table_name, table_name, &caller)
|
390
|
+
end
|
416
391
|
end
|
417
392
|
end
|
418
393
|
|
@@ -424,7 +399,7 @@ module ActiveRecord
|
|
424
399
|
def copy_table(from, to, options = {})
|
425
400
|
from_primary_key = primary_key(from)
|
426
401
|
options[:id] = false
|
427
|
-
create_table(to, options) do |definition|
|
402
|
+
create_table(to, **options) do |definition|
|
428
403
|
@definition = definition
|
429
404
|
if from_primary_key.is_a?(Array)
|
430
405
|
@definition.primary_keys from_primary_key
|
@@ -442,6 +417,7 @@ module ActiveRecord
|
|
442
417
|
primary_key: column_name == from_primary_key
|
443
418
|
)
|
444
419
|
end
|
420
|
+
|
445
421
|
yield @definition if block_given?
|
446
422
|
end
|
447
423
|
copy_table_indexes(from, to, options[:rename] || {})
|
@@ -459,9 +435,12 @@ module ActiveRecord
|
|
459
435
|
name = name[1..-1]
|
460
436
|
end
|
461
437
|
|
462
|
-
|
463
|
-
|
464
|
-
to_column_names.
|
438
|
+
columns = index.columns
|
439
|
+
if columns.is_a?(Array)
|
440
|
+
to_column_names = columns(to).map(&:name)
|
441
|
+
columns = columns.map { |c| rename[c] || c }.select do |column|
|
442
|
+
to_column_names.include?(column)
|
443
|
+
end
|
465
444
|
end
|
466
445
|
|
467
446
|
unless columns.empty?
|
@@ -487,22 +466,18 @@ module ActiveRecord
|
|
487
466
|
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
|
488
467
|
end
|
489
468
|
|
490
|
-
def
|
491
|
-
@sqlite_version ||= SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
492
|
-
end
|
493
|
-
|
494
|
-
def translate_exception(exception, message)
|
469
|
+
def translate_exception(exception, message:, sql:, binds:)
|
495
470
|
case exception.message
|
496
471
|
# SQLite 3.8.2 returns a newly formatted error message:
|
497
472
|
# UNIQUE constraint failed: *table_name*.*column_name*
|
498
473
|
# Older versions of SQLite return:
|
499
474
|
# column *column_name* is not unique
|
500
475
|
when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
|
501
|
-
RecordNotUnique.new(message)
|
476
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
502
477
|
when /.* may not be NULL/, /NOT NULL constraint failed: .*/
|
503
|
-
NotNullViolation.new(message)
|
478
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
504
479
|
when /FOREIGN KEY constraint failed/i
|
505
|
-
InvalidForeignKey.new(message)
|
480
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
506
481
|
else
|
507
482
|
super
|
508
483
|
end
|
@@ -512,7 +487,7 @@ module ActiveRecord
|
|
512
487
|
|
513
488
|
def table_structure_with_collation(table_name, basic_structure)
|
514
489
|
collation_hash = {}
|
515
|
-
sql =
|
490
|
+
sql = <<~SQL
|
516
491
|
SELECT sql FROM
|
517
492
|
(SELECT * FROM sqlite_master UNION ALL
|
518
493
|
SELECT * FROM sqlite_temp_master)
|
@@ -545,7 +520,7 @@ module ActiveRecord
|
|
545
520
|
column
|
546
521
|
end
|
547
522
|
else
|
548
|
-
basic_structure.
|
523
|
+
basic_structure.to_a
|
549
524
|
end
|
550
525
|
end
|
551
526
|
|
@@ -553,7 +528,21 @@ module ActiveRecord
|
|
553
528
|
Arel::Visitors::SQLite.new(self)
|
554
529
|
end
|
555
530
|
|
531
|
+
def build_statement_pool
|
532
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
533
|
+
end
|
534
|
+
|
535
|
+
def connect
|
536
|
+
@connection = ::SQLite3::Database.new(
|
537
|
+
@config[:database].to_s,
|
538
|
+
@config.merge(results_as_hash: true)
|
539
|
+
)
|
540
|
+
configure_connection
|
541
|
+
end
|
542
|
+
|
556
543
|
def configure_connection
|
544
|
+
@connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout])) if @config[:timeout]
|
545
|
+
|
557
546
|
execute("PRAGMA foreign_keys = ON", "SCHEMA")
|
558
547
|
end
|
559
548
|
|