activerecord 6.0.0.beta1 → 6.0.1.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.

Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +529 -10
  3. data/README.rdoc +3 -1
  4. data/lib/active_record.rb +7 -1
  5. data/lib/active_record/association_relation.rb +15 -6
  6. data/lib/active_record/associations.rb +4 -3
  7. data/lib/active_record/associations/association.rb +27 -2
  8. data/lib/active_record/associations/builder/association.rb +14 -18
  9. data/lib/active_record/associations/builder/belongs_to.rb +5 -2
  10. data/lib/active_record/associations/builder/collection_association.rb +5 -15
  11. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
  12. data/lib/active_record/associations/builder/has_many.rb +2 -0
  13. data/lib/active_record/associations/builder/has_one.rb +35 -1
  14. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  15. data/lib/active_record/associations/collection_association.rb +5 -6
  16. data/lib/active_record/associations/collection_proxy.rb +13 -42
  17. data/lib/active_record/associations/has_many_association.rb +1 -9
  18. data/lib/active_record/associations/has_many_through_association.rb +4 -11
  19. data/lib/active_record/associations/join_dependency.rb +14 -9
  20. data/lib/active_record/associations/join_dependency/join_association.rb +21 -7
  21. data/lib/active_record/associations/preloader.rb +12 -7
  22. data/lib/active_record/associations/preloader/association.rb +37 -34
  23. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  24. data/lib/active_record/attribute_methods.rb +3 -53
  25. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  26. data/lib/active_record/attribute_methods/dirty.rb +47 -14
  27. data/lib/active_record/attribute_methods/primary_key.rb +7 -15
  28. data/lib/active_record/attribute_methods/query.rb +2 -3
  29. data/lib/active_record/attribute_methods/read.rb +3 -9
  30. data/lib/active_record/attribute_methods/write.rb +6 -12
  31. data/lib/active_record/attributes.rb +13 -0
  32. data/lib/active_record/autosave_association.rb +21 -7
  33. data/lib/active_record/base.rb +0 -1
  34. data/lib/active_record/callbacks.rb +3 -3
  35. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +134 -23
  36. data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
  37. data/lib/active_record/connection_adapters/abstract/database_statements.rb +105 -70
  38. data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -5
  39. data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
  40. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -2
  41. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +51 -40
  42. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
  43. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +95 -30
  44. data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
  45. data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -35
  46. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +106 -138
  47. data/lib/active_record/connection_adapters/column.rb +17 -13
  48. data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
  49. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +3 -3
  50. data/lib/active_record/connection_adapters/mysql/database_statements.rb +48 -8
  51. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  52. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  53. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  54. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +66 -5
  55. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  56. data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
  57. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
  58. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -2
  59. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  60. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  61. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  62. data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -3
  63. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
  64. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +98 -89
  65. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +47 -63
  66. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
  67. data/lib/active_record/connection_adapters/postgresql_adapter.rb +95 -24
  68. data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
  69. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  70. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  71. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
  72. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -2
  73. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +73 -118
  74. data/lib/active_record/connection_handling.rb +40 -17
  75. data/lib/active_record/core.rb +35 -24
  76. data/lib/active_record/database_configurations.rb +99 -50
  77. data/lib/active_record/database_configurations/hash_config.rb +11 -11
  78. data/lib/active_record/database_configurations/url_config.rb +21 -16
  79. data/lib/active_record/dynamic_matchers.rb +1 -1
  80. data/lib/active_record/enum.rb +15 -0
  81. data/lib/active_record/errors.rb +18 -13
  82. data/lib/active_record/fixtures.rb +11 -6
  83. data/lib/active_record/gem_version.rb +2 -2
  84. data/lib/active_record/inheritance.rb +1 -1
  85. data/lib/active_record/insert_all.rb +179 -0
  86. data/lib/active_record/integration.rb +13 -1
  87. data/lib/active_record/internal_metadata.rb +5 -1
  88. data/lib/active_record/locking/optimistic.rb +3 -4
  89. data/lib/active_record/log_subscriber.rb +1 -1
  90. data/lib/active_record/middleware/database_selector.rb +75 -0
  91. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  92. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  93. data/lib/active_record/migration.rb +62 -44
  94. data/lib/active_record/migration/command_recorder.rb +28 -14
  95. data/lib/active_record/migration/compatibility.rb +72 -63
  96. data/lib/active_record/model_schema.rb +3 -0
  97. data/lib/active_record/persistence.rb +212 -19
  98. data/lib/active_record/querying.rb +18 -14
  99. data/lib/active_record/railtie.rb +9 -1
  100. data/lib/active_record/railties/collection_cache_association_loading.rb +3 -3
  101. data/lib/active_record/railties/databases.rake +124 -25
  102. data/lib/active_record/reflection.rb +18 -32
  103. data/lib/active_record/relation.rb +185 -35
  104. data/lib/active_record/relation/calculations.rb +40 -44
  105. data/lib/active_record/relation/delegation.rb +23 -31
  106. data/lib/active_record/relation/finder_methods.rb +23 -14
  107. data/lib/active_record/relation/merger.rb +11 -16
  108. data/lib/active_record/relation/query_attribute.rb +5 -3
  109. data/lib/active_record/relation/query_methods.rb +230 -69
  110. data/lib/active_record/relation/spawn_methods.rb +1 -1
  111. data/lib/active_record/relation/where_clause.rb +10 -10
  112. data/lib/active_record/sanitization.rb +33 -4
  113. data/lib/active_record/schema.rb +1 -1
  114. data/lib/active_record/schema_dumper.rb +10 -1
  115. data/lib/active_record/schema_migration.rb +1 -1
  116. data/lib/active_record/scoping.rb +6 -7
  117. data/lib/active_record/scoping/default.rb +7 -15
  118. data/lib/active_record/scoping/named.rb +10 -2
  119. data/lib/active_record/statement_cache.rb +2 -2
  120. data/lib/active_record/store.rb +48 -0
  121. data/lib/active_record/table_metadata.rb +9 -13
  122. data/lib/active_record/tasks/database_tasks.rb +109 -6
  123. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
  124. data/lib/active_record/test_databases.rb +1 -16
  125. data/lib/active_record/test_fixtures.rb +2 -2
  126. data/lib/active_record/timestamp.rb +35 -19
  127. data/lib/active_record/touch_later.rb +4 -2
  128. data/lib/active_record/transactions.rb +56 -46
  129. data/lib/active_record/type_caster/connection.rb +16 -10
  130. data/lib/active_record/validations.rb +1 -0
  131. data/lib/active_record/validations/uniqueness.rb +4 -4
  132. data/lib/arel.rb +18 -4
  133. data/lib/arel/insert_manager.rb +3 -3
  134. data/lib/arel/nodes.rb +2 -1
  135. data/lib/arel/nodes/and.rb +1 -1
  136. data/lib/arel/nodes/case.rb +1 -1
  137. data/lib/arel/nodes/comment.rb +29 -0
  138. data/lib/arel/nodes/select_core.rb +16 -12
  139. data/lib/arel/nodes/unary.rb +1 -0
  140. data/lib/arel/nodes/values_list.rb +2 -17
  141. data/lib/arel/select_manager.rb +10 -10
  142. data/lib/arel/visitors/depth_first.rb +7 -2
  143. data/lib/arel/visitors/dot.rb +7 -2
  144. data/lib/arel/visitors/ibm_db.rb +13 -0
  145. data/lib/arel/visitors/informix.rb +6 -0
  146. data/lib/arel/visitors/mssql.rb +15 -1
  147. data/lib/arel/visitors/oracle12.rb +4 -5
  148. data/lib/arel/visitors/postgresql.rb +4 -10
  149. data/lib/arel/visitors/to_sql.rb +107 -131
  150. data/lib/arel/visitors/visitor.rb +9 -5
  151. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  152. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  153. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  154. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  155. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  156. metadata +19 -12
  157. data/lib/active_record/collection_cache_key.rb +0 -53
  158. 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"] = @columns
28
- coder["columns_hash"] = @columns_hash
29
- coder["primary_keys"] = @primary_keys
30
- coder["data_sources"] = @data_sources
31
- coder["version"] = connection.migration_context.current_version
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 = coder["columns"]
36
- @columns_hash = coder["columns_hash"]
37
- @primary_keys = coder["primary_keys"]
38
- @data_sources = coder["data_sources"]
39
- @version = coder["version"]
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].map(&:size).inject :+
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
- attributes_for_hash == other.attributes_for_hash
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
- attributes_for_hash.hash
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
- @quoted_table_names[name] ||= super.gsub(".", "\".\"").freeze
16
+ self.class.quoted_table_names[name] ||= super.gsub(".", "\".\"").freeze
17
17
  end
18
18
 
19
19
  def quote_column_name(name)
20
- @quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}")
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)
@@ -52,6 +52,32 @@ module ActiveRecord
52
52
  end.compact
53
53
  end
54
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
+
55
81
  def create_schema_dumper(options)
56
82
  SQLite3::SchemaDumper.create(self, options)
57
83
  end
@@ -62,7 +88,7 @@ module ActiveRecord
62
88
  end
63
89
 
64
90
  def create_table_definition(*args)
65
- SQLite3::TableDefinition.new(*args)
91
+ SQLite3::TableDefinition.new(self, *args)
66
92
  end
67
93
 
68
94
  def new_column_from_field(table_name, field)
@@ -79,7 +105,7 @@ module ActiveRecord
79
105
  end
80
106
 
81
107
  type_metadata = fetch_type_metadata(field["type"])
82
- Column.new(field["name"], default, type_metadata, field["notnull"].to_i == 0, table_name, nil, field["collation"])
108
+ Column.new(field["name"], default, type_metadata, field["notnull"].to_i == 0, collation: field["collation"])
83
109
  end
84
110
 
85
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.3.6"
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,14 +121,14 @@ module ActiveRecord
114
121
  end
115
122
 
116
123
  def supports_expression_index?
117
- sqlite_version >= "3.9.0"
124
+ database_version >= "3.9.0"
118
125
  end
119
126
 
120
127
  def requires_reloading?
121
128
  true
122
129
  end
123
130
 
124
- def supports_foreign_keys_in_create?
131
+ def supports_foreign_keys?
125
132
  true
126
133
  end
127
134
 
@@ -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
- @active
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:
@@ -320,6 +257,9 @@ module ActiveRecord
320
257
  def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
321
258
  alter_table(table_name) do |definition|
322
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
323
263
  end
324
264
  end
325
265
 
@@ -378,15 +318,26 @@ module ActiveRecord
378
318
  end
379
319
  end
380
320
 
381
- def insert_fixtures_set(fixture_set, tables_to_delete = [])
382
- disable_referential_integrity do
383
- transaction(requires_new: true) do
384
- 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}"
385
323
 
386
- fixture_set.each do |table_name, rows|
387
- rows.each { |row| insert_fixture(row, table_name) }
388
- end
389
- end
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."
390
341
  end
391
342
  end
392
343
 
@@ -397,12 +348,6 @@ module ActiveRecord
397
348
  999
398
349
  end
399
350
 
400
- def check_version
401
- if sqlite_version < "3.8.0"
402
- raise "Your version of SQLite (#{sqlite_version}) is too old. Active Record supports SQLite >= 3.8."
403
- end
404
- end
405
-
406
351
  def initialize_type_map(m = type_map)
407
352
  super
408
353
  register_class_with_limit m, %r(int)i, SQLite3Integer
@@ -421,9 +366,8 @@ module ActiveRecord
421
366
  type.to_sym == :primary_key || options[:primary_key]
422
367
  end
423
368
 
424
- def alter_table(table_name, options = {})
369
+ def alter_table(table_name, foreign_keys = foreign_keys(table_name), **options)
425
370
  altered_table_name = "a#{table_name}"
426
- foreign_keys = foreign_keys(table_name)
427
371
 
428
372
  caller = lambda do |definition|
429
373
  rename = options[:rename] || {}
@@ -431,7 +375,8 @@ module ActiveRecord
431
375
  if column = rename[fk.options[:column]]
432
376
  fk.options[:column] = column
433
377
  end
434
- definition.foreign_key(fk.to_table, fk.options)
378
+ to_table = strip_table_name_prefix_and_suffix(fk.to_table)
379
+ definition.foreign_key(to_table, fk.options)
435
380
  end
436
381
 
437
382
  yield definition if block_given?
@@ -520,10 +465,6 @@ module ActiveRecord
520
465
  SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
521
466
  end
522
467
 
523
- def sqlite_version
524
- @sqlite_version ||= SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
525
- end
526
-
527
468
  def translate_exception(exception, message:, sql:, binds:)
528
469
  case exception.message
529
470
  # SQLite 3.8.2 returns a newly formatted error message:
@@ -558,9 +499,9 @@ module ActiveRecord
558
499
  result = exec_query(sql, "SCHEMA").first
559
500
 
560
501
  if result
561
- # Splitting with left parentheses and picking up last will return all
502
+ # Splitting with left parentheses and discarding the first part will return all
562
503
  # columns separated with comma(,).
563
- columns_string = result["sql"].split("(").last
504
+ columns_string = result["sql"].split("(", 2).last
564
505
 
565
506
  columns_string.split(",").each do |column_string|
566
507
  # This regex will match the column name and collation type and will save
@@ -586,7 +527,21 @@ module ActiveRecord
586
527
  Arel::Visitors::SQLite.new(self)
587
528
  end
588
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
+
589
542
  def configure_connection
543
+ @connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout])) if @config[:timeout]
544
+
590
545
  execute("PRAGMA foreign_keys = ON", "SCHEMA")
591
546
  end
592
547