activerecord 6.0.0.beta2 → 6.0.2.rc1
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 +471 -9
- data/README.rdoc +3 -1
- data/lib/active_record.rb +0 -1
- data/lib/active_record/association_relation.rb +15 -6
- data/lib/active_record/associations.rb +4 -3
- data/lib/active_record/associations/association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +5 -2
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
- 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 -2
- data/lib/active_record/associations/collection_proxy.rb +2 -2
- data/lib/active_record/associations/has_many_through_association.rb +4 -11
- data/lib/active_record/associations/join_dependency.rb +14 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +12 -3
- data/lib/active_record/associations/preloader.rb +13 -8
- data/lib/active_record/associations/preloader/association.rb +34 -30
- data/lib/active_record/associations/preloader/through_association.rb +48 -28
- data/lib/active_record/attribute_methods.rb +3 -53
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +47 -14
- data/lib/active_record/attribute_methods/primary_key.rb +7 -15
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +3 -9
- data/lib/active_record/attribute_methods/write.rb +6 -12
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +21 -7
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +109 -11
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +88 -61
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -7
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +79 -22
- data/lib/active_record/connection_adapters/abstract/transaction.rb +12 -4
- data/lib/active_record/connection_adapters/abstract_adapter.rb +114 -34
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +78 -73
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -2
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +48 -8
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +10 -7
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +39 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +34 -38
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +68 -27
- data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -114
- data/lib/active_record/connection_handling.rb +31 -13
- data/lib/active_record/core.rb +23 -24
- data/lib/active_record/database_configurations.rb +73 -44
- data/lib/active_record/database_configurations/hash_config.rb +11 -11
- data/lib/active_record/database_configurations/url_config.rb +12 -12
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +15 -0
- data/lib/active_record/errors.rb +1 -1
- data/lib/active_record/fixtures.rb +11 -6
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +13 -1
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +3 -4
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/middleware/database_selector.rb +3 -3
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -6
- data/lib/active_record/migration.rb +62 -44
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +10 -0
- data/lib/active_record/model_schema.rb +3 -0
- data/lib/active_record/persistence.rb +206 -13
- data/lib/active_record/querying.rb +17 -12
- data/lib/active_record/railtie.rb +0 -1
- data/lib/active_record/railties/databases.rake +127 -25
- data/lib/active_record/reflection.rb +3 -3
- data/lib/active_record/relation.rb +99 -20
- data/lib/active_record/relation/calculations.rb +38 -40
- data/lib/active_record/relation/delegation.rb +22 -30
- data/lib/active_record/relation/finder_methods.rb +17 -12
- data/lib/active_record/relation/merger.rb +11 -16
- data/lib/active_record/relation/query_methods.rb +228 -76
- data/lib/active_record/relation/where_clause.rb +9 -5
- data/lib/active_record/sanitization.rb +33 -4
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +10 -1
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/store.rb +48 -0
- data/lib/active_record/table_metadata.rb +9 -13
- data/lib/active_record/tasks/database_tasks.rb +109 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
- data/lib/active_record/test_databases.rb +1 -16
- data/lib/active_record/test_fixtures.rb +1 -0
- data/lib/active_record/timestamp.rb +26 -16
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +56 -46
- data/lib/active_record/type_caster/connection.rb +16 -10
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +3 -5
- data/lib/arel.rb +12 -5
- data/lib/arel/insert_manager.rb +3 -3
- data/lib/arel/nodes.rb +2 -1
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/select_core.rb +16 -12
- data/lib/arel/nodes/unary.rb +1 -0
- data/lib/arel/nodes/values_list.rb +2 -17
- data/lib/arel/select_manager.rb +10 -10
- data/lib/arel/visitors/depth_first.rb +7 -2
- data/lib/arel/visitors/dot.rb +7 -2
- data/lib/arel/visitors/ibm_db.rb +13 -0
- data/lib/arel/visitors/informix.rb +6 -0
- data/lib/arel/visitors/mssql.rb +15 -1
- data/lib/arel/visitors/oracle12.rb +4 -5
- data/lib/arel/visitors/postgresql.rb +4 -10
- data/lib/arel/visitors/to_sql.rb +107 -131
- data/lib/arel/visitors/visitor.rb +9 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- 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 +16 -12
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/arel/nodes/values.rb +0 -16
@@ -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
|
|
@@ -82,17 +89,27 @@ module ActiveRecord
|
|
82
89
|
@columns_hash.key?(table_name)
|
83
90
|
end
|
84
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
|
+
|
85
100
|
# Clears out internal caches
|
86
101
|
def clear!
|
87
102
|
@columns.clear
|
88
103
|
@columns_hash.clear
|
89
104
|
@primary_keys.clear
|
90
105
|
@data_sources.clear
|
106
|
+
@indexes.clear
|
91
107
|
@version = nil
|
108
|
+
@database_version = nil
|
92
109
|
end
|
93
110
|
|
94
111
|
def size
|
95
|
-
[@columns, @columns_hash, @primary_keys, @data_sources].
|
112
|
+
[@columns, @columns_hash, @primary_keys, @data_sources].sum(&:size)
|
96
113
|
end
|
97
114
|
|
98
115
|
# Clear out internal caches for the data source +name+.
|
@@ -101,20 +118,21 @@ module ActiveRecord
|
|
101
118
|
@columns_hash.delete name
|
102
119
|
@primary_keys.delete name
|
103
120
|
@data_sources.delete name
|
121
|
+
@indexes.delete name
|
104
122
|
end
|
105
123
|
|
106
124
|
def marshal_dump
|
107
125
|
# if we get current version during initialization, it happens stack over flow.
|
108
126
|
@version = connection.migration_context.current_version
|
109
|
-
[@version, @columns, @columns_hash, @primary_keys, @data_sources]
|
127
|
+
[@version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, database_version]
|
110
128
|
end
|
111
129
|
|
112
130
|
def marshal_load(array)
|
113
|
-
@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 || {}
|
114
133
|
end
|
115
134
|
|
116
135
|
private
|
117
|
-
|
118
136
|
def prepare_data_sources
|
119
137
|
connection.data_sources.each { |source| @data_sources[source] = true }
|
120
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,120 @@
|
|
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(sql, name = nil)
|
87
|
+
if preventing_writes? && write_query?(sql)
|
88
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
89
|
+
end
|
90
|
+
|
91
|
+
materialize_transactions
|
92
|
+
|
93
|
+
log(sql, name) do
|
94
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
95
|
+
@connection.execute_batch2(sql)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def last_inserted_id(result)
|
101
|
+
@connection.last_insert_row_id
|
102
|
+
end
|
103
|
+
|
104
|
+
def build_fixture_statements(fixture_set)
|
105
|
+
fixture_set.flat_map do |table_name, fixtures|
|
106
|
+
next if fixtures.empty?
|
107
|
+
fixtures.map { |fixture| build_fixture_sql([fixture], table_name) }
|
108
|
+
end.compact
|
109
|
+
end
|
110
|
+
|
111
|
+
def build_truncate_statements(*table_names)
|
112
|
+
truncate_tables = table_names.map do |table_name|
|
113
|
+
"DELETE FROM #{quote_table_name(table_name)}"
|
114
|
+
end
|
115
|
+
combine_multi_statements(truncate_tables)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
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)
|
@@ -45,6 +45,42 @@ module ActiveRecord
|
|
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)
|
@@ -72,7 +72,7 @@ module ActiveRecord
|
|
72
72
|
table = strip_table_name_prefix_and_suffix(table)
|
73
73
|
fk_to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
74
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}")
|
75
|
+
end || raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
|
76
76
|
|
77
77
|
foreign_keys.delete(fkey)
|
78
78
|
alter_table(from_table, foreign_keys)
|
@@ -105,7 +105,7 @@ module ActiveRecord
|
|
105
105
|
end
|
106
106
|
|
107
107
|
type_metadata = fetch_type_metadata(field["type"])
|
108
|
-
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"])
|
109
109
|
end
|
110
110
|
|
111
111
|
def data_source_sql(name = nil, type: nil)
|
@@ -4,12 +4,13 @@ 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
|
@@ -36,8 +37,6 @@ module ActiveRecord
|
|
36
37
|
config.merge(results_as_hash: true)
|
37
38
|
)
|
38
39
|
|
39
|
-
db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]
|
40
|
-
|
41
40
|
ConnectionAdapters::SQLite3Adapter.new(db, logger, nil, config)
|
42
41
|
rescue Errno::ENOENT => error
|
43
42
|
if error.message.include?("No such file or directory")
|
@@ -60,6 +59,7 @@ module ActiveRecord
|
|
60
59
|
|
61
60
|
include SQLite3::Quoting
|
62
61
|
include SQLite3::SchemaStatements
|
62
|
+
include SQLite3::DatabaseStatements
|
63
63
|
|
64
64
|
NATIVE_DATABASE_TYPES = {
|
65
65
|
primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
|
@@ -95,12 +95,19 @@ module ActiveRecord
|
|
95
95
|
|
96
96
|
def initialize(connection, logger, connection_options, config)
|
97
97
|
super(connection, logger, config)
|
98
|
-
|
99
|
-
@active = true
|
100
|
-
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
|
101
98
|
configure_connection
|
102
99
|
end
|
103
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
|
+
|
104
111
|
def supports_ddl_transactions?
|
105
112
|
true
|
106
113
|
end
|
@@ -114,7 +121,7 @@ module ActiveRecord
|
|
114
121
|
end
|
115
122
|
|
116
123
|
def supports_expression_index?
|
117
|
-
|
124
|
+
database_version >= "3.9.0"
|
118
125
|
end
|
119
126
|
|
120
127
|
def requires_reloading?
|
@@ -137,23 +144,33 @@ module ActiveRecord
|
|
137
144
|
true
|
138
145
|
end
|
139
146
|
|
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"
|
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?
|
157
|
+
|
140
158
|
def active?
|
141
|
-
|
159
|
+
!@connection.closed?
|
160
|
+
end
|
161
|
+
|
162
|
+
def reconnect!
|
163
|
+
super
|
164
|
+
connect if @connection.closed?
|
142
165
|
end
|
143
166
|
|
144
167
|
# Disconnects from the database if already connected. Otherwise, this
|
145
168
|
# method does nothing.
|
146
169
|
def disconnect!
|
147
170
|
super
|
148
|
-
@active = false
|
149
171
|
@connection.close rescue nil
|
150
172
|
end
|
151
173
|
|
152
|
-
# Clears the prepared statements cache.
|
153
|
-
def clear_cache!
|
154
|
-
@statements.clear
|
155
|
-
end
|
156
|
-
|
157
174
|
def supports_index_sort_order?
|
158
175
|
true
|
159
176
|
end
|
@@ -201,91 +218,11 @@ module ActiveRecord
|
|
201
218
|
#--
|
202
219
|
# DATABASE STATEMENTS ======================================
|
203
220
|
#++
|
204
|
-
|
205
|
-
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :explain, :select, :pragma, :release, :savepoint, :rollback) # :nodoc:
|
206
|
-
private_constant :READ_QUERY
|
207
|
-
|
208
|
-
def write_query?(sql) # :nodoc:
|
209
|
-
!READ_QUERY.match?(sql)
|
210
|
-
end
|
211
|
-
|
212
221
|
def explain(arel, binds = [])
|
213
222
|
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
|
214
223
|
SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
|
215
224
|
end
|
216
225
|
|
217
|
-
def exec_query(sql, name = nil, binds = [], prepare: false)
|
218
|
-
if preventing_writes? && write_query?(sql)
|
219
|
-
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
220
|
-
end
|
221
|
-
|
222
|
-
materialize_transactions
|
223
|
-
|
224
|
-
type_casted_binds = type_casted_binds(binds)
|
225
|
-
|
226
|
-
log(sql, name, binds, type_casted_binds) do
|
227
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
228
|
-
# Don't cache statements if they are not prepared
|
229
|
-
unless prepare
|
230
|
-
stmt = @connection.prepare(sql)
|
231
|
-
begin
|
232
|
-
cols = stmt.columns
|
233
|
-
unless without_prepared_statement?(binds)
|
234
|
-
stmt.bind_params(type_casted_binds)
|
235
|
-
end
|
236
|
-
records = stmt.to_a
|
237
|
-
ensure
|
238
|
-
stmt.close
|
239
|
-
end
|
240
|
-
else
|
241
|
-
stmt = @statements[sql] ||= @connection.prepare(sql)
|
242
|
-
cols = stmt.columns
|
243
|
-
stmt.reset!
|
244
|
-
stmt.bind_params(type_casted_binds)
|
245
|
-
records = stmt.to_a
|
246
|
-
end
|
247
|
-
|
248
|
-
ActiveRecord::Result.new(cols, records)
|
249
|
-
end
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
def exec_delete(sql, name = "SQL", binds = [])
|
254
|
-
exec_query(sql, name, binds)
|
255
|
-
@connection.changes
|
256
|
-
end
|
257
|
-
alias :exec_update :exec_delete
|
258
|
-
|
259
|
-
def last_inserted_id(result)
|
260
|
-
@connection.last_insert_row_id
|
261
|
-
end
|
262
|
-
|
263
|
-
def execute(sql, name = nil) #:nodoc:
|
264
|
-
if preventing_writes? && write_query?(sql)
|
265
|
-
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
266
|
-
end
|
267
|
-
|
268
|
-
materialize_transactions
|
269
|
-
|
270
|
-
log(sql, name) do
|
271
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
272
|
-
@connection.execute(sql)
|
273
|
-
end
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
def begin_db_transaction #:nodoc:
|
278
|
-
log("begin transaction", nil) { @connection.transaction }
|
279
|
-
end
|
280
|
-
|
281
|
-
def commit_db_transaction #:nodoc:
|
282
|
-
log("commit transaction", nil) { @connection.commit }
|
283
|
-
end
|
284
|
-
|
285
|
-
def exec_rollback_db_transaction #:nodoc:
|
286
|
-
log("rollback transaction", nil) { @connection.rollback }
|
287
|
-
end
|
288
|
-
|
289
226
|
# SCHEMA STATEMENTS ========================================
|
290
227
|
|
291
228
|
def primary_keys(table_name) # :nodoc:
|
@@ -381,15 +318,26 @@ module ActiveRecord
|
|
381
318
|
end
|
382
319
|
end
|
383
320
|
|
384
|
-
def
|
385
|
-
|
386
|
-
transaction(requires_new: true) do
|
387
|
-
tables_to_delete.each { |table| delete "DELETE FROM #{quote_table_name(table)}", "Fixture Delete" }
|
321
|
+
def build_insert_sql(insert) # :nodoc:
|
322
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
388
323
|
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
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
|
332
|
+
end
|
333
|
+
|
334
|
+
def get_database_version # :nodoc:
|
335
|
+
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
336
|
+
end
|
337
|
+
|
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."
|
393
341
|
end
|
394
342
|
end
|
395
343
|
|
@@ -400,12 +348,6 @@ module ActiveRecord
|
|
400
348
|
999
|
401
349
|
end
|
402
350
|
|
403
|
-
def check_version
|
404
|
-
if sqlite_version < "3.8.0"
|
405
|
-
raise "Your version of SQLite (#{sqlite_version}) is too old. Active Record supports SQLite >= 3.8."
|
406
|
-
end
|
407
|
-
end
|
408
|
-
|
409
351
|
def initialize_type_map(m = type_map)
|
410
352
|
super
|
411
353
|
register_class_with_limit m, %r(int)i, SQLite3Integer
|
@@ -523,10 +465,6 @@ module ActiveRecord
|
|
523
465
|
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
|
524
466
|
end
|
525
467
|
|
526
|
-
def sqlite_version
|
527
|
-
@sqlite_version ||= SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
528
|
-
end
|
529
|
-
|
530
468
|
def translate_exception(exception, message:, sql:, binds:)
|
531
469
|
case exception.message
|
532
470
|
# SQLite 3.8.2 returns a newly formatted error message:
|
@@ -561,9 +499,9 @@ module ActiveRecord
|
|
561
499
|
result = exec_query(sql, "SCHEMA").first
|
562
500
|
|
563
501
|
if result
|
564
|
-
# Splitting with left parentheses and
|
502
|
+
# Splitting with left parentheses and discarding the first part will return all
|
565
503
|
# columns separated with comma(,).
|
566
|
-
columns_string = result["sql"].split("(").last
|
504
|
+
columns_string = result["sql"].split("(", 2).last
|
567
505
|
|
568
506
|
columns_string.split(",").each do |column_string|
|
569
507
|
# This regex will match the column name and collation type and will save
|
@@ -589,7 +527,21 @@ module ActiveRecord
|
|
589
527
|
Arel::Visitors::SQLite.new(self)
|
590
528
|
end
|
591
529
|
|
530
|
+
def build_statement_pool
|
531
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
532
|
+
end
|
533
|
+
|
534
|
+
def connect
|
535
|
+
@connection = ::SQLite3::Database.new(
|
536
|
+
@config[:database].to_s,
|
537
|
+
@config.merge(results_as_hash: true)
|
538
|
+
)
|
539
|
+
configure_connection
|
540
|
+
end
|
541
|
+
|
592
542
|
def configure_connection
|
543
|
+
@connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout])) if @config[:timeout]
|
544
|
+
|
593
545
|
execute("PRAGMA foreign_keys = ON", "SCHEMA")
|
594
546
|
end
|
595
547
|
|