activerecord 5.0.7 → 5.1.7

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 (219) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +657 -2080
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +28 -28
  6. data/examples/simple.rb +3 -3
  7. data/lib/active_record/aggregations.rb +244 -244
  8. data/lib/active_record/association_relation.rb +5 -5
  9. data/lib/active_record/associations/alias_tracker.rb +10 -11
  10. data/lib/active_record/associations/association.rb +23 -5
  11. data/lib/active_record/associations/association_scope.rb +95 -81
  12. data/lib/active_record/associations/belongs_to_association.rb +7 -4
  13. data/lib/active_record/associations/builder/belongs_to.rb +30 -16
  14. data/lib/active_record/associations/builder/collection_association.rb +1 -2
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
  16. data/lib/active_record/associations/collection_association.rb +36 -205
  17. data/lib/active_record/associations/collection_proxy.rb +132 -63
  18. data/lib/active_record/associations/has_many_association.rb +10 -19
  19. data/lib/active_record/associations/has_many_through_association.rb +12 -4
  20. data/lib/active_record/associations/has_one_association.rb +24 -28
  21. data/lib/active_record/associations/has_one_through_association.rb +5 -1
  22. data/lib/active_record/associations/join_dependency/join_association.rb +4 -28
  23. data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
  24. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  25. data/lib/active_record/associations/join_dependency.rb +121 -118
  26. data/lib/active_record/associations/preloader/association.rb +64 -64
  27. data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
  28. data/lib/active_record/associations/preloader/collection_association.rb +6 -6
  29. data/lib/active_record/associations/preloader/has_many.rb +0 -2
  30. data/lib/active_record/associations/preloader/singular_association.rb +6 -8
  31. data/lib/active_record/associations/preloader/through_association.rb +41 -41
  32. data/lib/active_record/associations/preloader.rb +94 -94
  33. data/lib/active_record/associations/singular_association.rb +8 -25
  34. data/lib/active_record/associations/through_association.rb +2 -5
  35. data/lib/active_record/associations.rb +1591 -1562
  36. data/lib/active_record/attribute/user_provided_default.rb +4 -2
  37. data/lib/active_record/attribute.rb +98 -71
  38. data/lib/active_record/attribute_assignment.rb +61 -61
  39. data/lib/active_record/attribute_decorators.rb +35 -13
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
  41. data/lib/active_record/attribute_methods/dirty.rb +229 -46
  42. data/lib/active_record/attribute_methods/primary_key.rb +74 -73
  43. data/lib/active_record/attribute_methods/read.rb +39 -35
  44. data/lib/active_record/attribute_methods/serialization.rb +7 -7
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
  46. data/lib/active_record/attribute_methods/write.rb +30 -33
  47. data/lib/active_record/attribute_methods.rb +56 -65
  48. data/lib/active_record/attribute_mutation_tracker.rb +63 -11
  49. data/lib/active_record/attribute_set/builder.rb +27 -33
  50. data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
  51. data/lib/active_record/attribute_set.rb +9 -6
  52. data/lib/active_record/attributes.rb +22 -22
  53. data/lib/active_record/autosave_association.rb +18 -13
  54. data/lib/active_record/base.rb +24 -22
  55. data/lib/active_record/callbacks.rb +56 -14
  56. data/lib/active_record/coders/yaml_column.rb +9 -11
  57. data/lib/active_record/collection_cache_key.rb +3 -4
  58. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +330 -284
  59. data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
  60. data/lib/active_record/connection_adapters/abstract/database_statements.rb +39 -37
  61. data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -27
  62. data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -51
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +10 -20
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +74 -79
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +120 -100
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +49 -43
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +165 -135
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +404 -424
  70. data/lib/active_record/connection_adapters/column.rb +26 -4
  71. data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
  72. data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -49
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -28
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +7 -6
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +23 -27
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +32 -53
  83. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +19 -9
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
  91. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +0 -10
  92. data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
  93. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +32 -30
  97. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
  99. data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
  100. data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -35
  101. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +15 -0
  102. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +37 -24
  103. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +19 -23
  104. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +182 -222
  105. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +6 -4
  106. data/lib/active_record/connection_adapters/postgresql/utils.rb +7 -5
  107. data/lib/active_record/connection_adapters/postgresql_adapter.rb +198 -167
  108. data/lib/active_record/connection_adapters/schema_cache.rb +16 -7
  109. data/lib/active_record/connection_adapters/sql_type_metadata.rb +3 -3
  110. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +1 -1
  111. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +16 -19
  112. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +1 -8
  113. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +28 -0
  114. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +17 -0
  115. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +32 -0
  116. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +184 -167
  117. data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
  118. data/lib/active_record/connection_handling.rb +14 -26
  119. data/lib/active_record/core.rb +109 -93
  120. data/lib/active_record/counter_cache.rb +60 -13
  121. data/lib/active_record/define_callbacks.rb +20 -0
  122. data/lib/active_record/dynamic_matchers.rb +80 -79
  123. data/lib/active_record/enum.rb +8 -6
  124. data/lib/active_record/errors.rb +64 -15
  125. data/lib/active_record/explain.rb +1 -2
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +7 -4
  128. data/lib/active_record/fixture_set/file.rb +11 -8
  129. data/lib/active_record/fixtures.rb +66 -53
  130. data/lib/active_record/gem_version.rb +1 -1
  131. data/lib/active_record/inheritance.rb +93 -79
  132. data/lib/active_record/integration.rb +7 -7
  133. data/lib/active_record/internal_metadata.rb +3 -16
  134. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  135. data/lib/active_record/locking/optimistic.rb +69 -74
  136. data/lib/active_record/locking/pessimistic.rb +10 -1
  137. data/lib/active_record/log_subscriber.rb +23 -28
  138. data/lib/active_record/migration/command_recorder.rb +94 -94
  139. data/lib/active_record/migration/compatibility.rb +100 -47
  140. data/lib/active_record/migration/join_table.rb +6 -6
  141. data/lib/active_record/migration.rb +153 -155
  142. data/lib/active_record/model_schema.rb +94 -107
  143. data/lib/active_record/nested_attributes.rb +200 -199
  144. data/lib/active_record/null_relation.rb +11 -34
  145. data/lib/active_record/persistence.rb +65 -50
  146. data/lib/active_record/query_cache.rb +2 -6
  147. data/lib/active_record/querying.rb +3 -4
  148. data/lib/active_record/railtie.rb +16 -17
  149. data/lib/active_record/railties/controller_runtime.rb +6 -2
  150. data/lib/active_record/railties/databases.rake +105 -133
  151. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  152. data/lib/active_record/readonly_attributes.rb +2 -2
  153. data/lib/active_record/reflection.rb +154 -108
  154. data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
  155. data/lib/active_record/relation/batches.rb +80 -51
  156. data/lib/active_record/relation/calculations.rb +169 -162
  157. data/lib/active_record/relation/delegation.rb +32 -31
  158. data/lib/active_record/relation/finder_methods.rb +197 -231
  159. data/lib/active_record/relation/merger.rb +58 -62
  160. data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
  161. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
  162. data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
  163. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
  165. data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
  166. data/lib/active_record/relation/predicate_builder.rb +92 -89
  167. data/lib/active_record/relation/query_attribute.rb +1 -1
  168. data/lib/active_record/relation/query_methods.rb +255 -293
  169. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  170. data/lib/active_record/relation/spawn_methods.rb +4 -5
  171. data/lib/active_record/relation/where_clause.rb +80 -65
  172. data/lib/active_record/relation/where_clause_factory.rb +47 -8
  173. data/lib/active_record/relation.rb +93 -119
  174. data/lib/active_record/result.rb +41 -32
  175. data/lib/active_record/runtime_registry.rb +3 -3
  176. data/lib/active_record/sanitization.rb +176 -192
  177. data/lib/active_record/schema.rb +3 -3
  178. data/lib/active_record/schema_dumper.rb +15 -38
  179. data/lib/active_record/schema_migration.rb +8 -4
  180. data/lib/active_record/scoping/default.rb +90 -90
  181. data/lib/active_record/scoping/named.rb +11 -11
  182. data/lib/active_record/scoping.rb +6 -6
  183. data/lib/active_record/secure_token.rb +2 -2
  184. data/lib/active_record/statement_cache.rb +13 -15
  185. data/lib/active_record/store.rb +31 -32
  186. data/lib/active_record/suppressor.rb +2 -1
  187. data/lib/active_record/table_metadata.rb +9 -5
  188. data/lib/active_record/tasks/database_tasks.rb +65 -55
  189. data/lib/active_record/tasks/mysql_database_tasks.rb +76 -73
  190. data/lib/active_record/tasks/postgresql_database_tasks.rb +72 -47
  191. data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
  192. data/lib/active_record/timestamp.rb +46 -25
  193. data/lib/active_record/touch_later.rb +1 -2
  194. data/lib/active_record/transactions.rb +97 -109
  195. data/lib/active_record/type/adapter_specific_registry.rb +46 -42
  196. data/lib/active_record/type/decimal_without_scale.rb +13 -0
  197. data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
  198. data/lib/active_record/type/internal/abstract_json.rb +4 -0
  199. data/lib/active_record/type/serialized.rb +14 -8
  200. data/lib/active_record/type/text.rb +9 -0
  201. data/lib/active_record/type/time.rb +0 -1
  202. data/lib/active_record/type/type_map.rb +11 -15
  203. data/lib/active_record/type/unsigned_integer.rb +15 -0
  204. data/lib/active_record/type.rb +17 -13
  205. data/lib/active_record/type_caster/connection.rb +8 -6
  206. data/lib/active_record/type_caster/map.rb +3 -1
  207. data/lib/active_record/type_caster.rb +2 -2
  208. data/lib/active_record/validations/associated.rb +1 -1
  209. data/lib/active_record/validations/presence.rb +2 -2
  210. data/lib/active_record/validations/uniqueness.rb +8 -39
  211. data/lib/active_record/validations.rb +4 -4
  212. data/lib/active_record/version.rb +1 -1
  213. data/lib/active_record.rb +20 -20
  214. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
  215. data/lib/rails/generators/active_record/migration.rb +1 -1
  216. data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
  217. data/lib/rails/generators/active_record.rb +4 -4
  218. metadata +24 -13
  219. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -1,11 +1,14 @@
1
- require 'active_record/connection_adapters/abstract_adapter'
2
- require 'active_record/connection_adapters/statement_pool'
3
- require 'active_record/connection_adapters/sqlite3/explain_pretty_printer'
4
- require 'active_record/connection_adapters/sqlite3/quoting'
5
- require 'active_record/connection_adapters/sqlite3/schema_creation'
6
-
7
- gem 'sqlite3', '~> 1.3.6'
8
- require 'sqlite3'
1
+ require "active_record/connection_adapters/abstract_adapter"
2
+ require "active_record/connection_adapters/statement_pool"
3
+ require "active_record/connection_adapters/sqlite3/explain_pretty_printer"
4
+ require "active_record/connection_adapters/sqlite3/quoting"
5
+ require "active_record/connection_adapters/sqlite3/schema_creation"
6
+ require "active_record/connection_adapters/sqlite3/schema_definitions"
7
+ require "active_record/connection_adapters/sqlite3/schema_dumper"
8
+ require "active_record/connection_adapters/sqlite3/schema_statements"
9
+
10
+ gem "sqlite3", "~> 1.3", ">= 1.3.6"
11
+ require "sqlite3"
9
12
 
10
13
  module ActiveRecord
11
14
  module ConnectionHandling # :nodoc:
@@ -18,7 +21,7 @@ module ActiveRecord
18
21
  # Allow database path relative to Rails.root, but only if the database
19
22
  # path is not the special path that tells sqlite to build a database only
20
23
  # in memory.
21
- if ':memory:' != config[:database]
24
+ if ":memory:" != config[:database]
22
25
  config[:database] = File.expand_path(config[:database], Rails.root) if defined?(Rails.root)
23
26
  dirname = File.dirname(config[:database])
24
27
  Dir.mkdir(dirname) unless File.directory?(dirname)
@@ -26,7 +29,7 @@ module ActiveRecord
26
29
 
27
30
  db = SQLite3::Database.new(
28
31
  config[:database].to_s,
29
- :results_as_hash => true
32
+ results_as_hash: true
30
33
  )
31
34
 
32
35
  db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]
@@ -49,12 +52,14 @@ module ActiveRecord
49
52
  #
50
53
  # * <tt>:database</tt> - Path to the database file.
51
54
  class SQLite3Adapter < AbstractAdapter
52
- ADAPTER_NAME = 'SQLite'.freeze
55
+ ADAPTER_NAME = "SQLite".freeze
53
56
 
54
57
  include SQLite3::Quoting
58
+ include SQLite3::ColumnDumper
59
+ include SQLite3::SchemaStatements
55
60
 
56
61
  NATIVE_DATABASE_TYPES = {
57
- primary_key: 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL',
62
+ primary_key: "INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL",
58
63
  string: { name: "varchar" },
59
64
  text: { name: "text" },
60
65
  integer: { name: "integer" },
@@ -70,9 +75,13 @@ module ActiveRecord
70
75
  class StatementPool < ConnectionAdapters::StatementPool
71
76
  private
72
77
 
73
- def dealloc(stmt)
74
- stmt[:stmt].close unless stmt[:stmt].closed?
75
- end
78
+ def dealloc(stmt)
79
+ stmt[:stmt].close unless stmt[:stmt].closed?
80
+ end
81
+ end
82
+
83
+ def update_table_definition(table_name, base) # :nodoc:
84
+ SQLite3::Table.new(table_name, base)
76
85
  end
77
86
 
78
87
  def schema_creation # :nodoc:
@@ -88,6 +97,8 @@ module ActiveRecord
88
97
 
89
98
  @active = nil
90
99
  @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
100
+
101
+ configure_connection
91
102
  end
92
103
 
93
104
  def supports_ddl_transactions?
@@ -99,7 +110,7 @@ module ActiveRecord
99
110
  end
100
111
 
101
112
  def supports_partial_index?
102
- sqlite_version >= '3.8.0'
113
+ sqlite_version >= "3.8.0"
103
114
  end
104
115
 
105
116
  # Returns true, since this connection adapter supports prepared statement
@@ -108,17 +119,12 @@ module ActiveRecord
108
119
  true
109
120
  end
110
121
 
111
- # Returns true, since this connection adapter supports migrations.
112
- def supports_migrations? #:nodoc:
113
- true
114
- end
115
-
116
- def supports_primary_key? #:nodoc:
122
+ def requires_reloading?
117
123
  true
118
124
  end
119
125
 
120
- def requires_reloading?
121
- true
126
+ def supports_foreign_keys_in_create?
127
+ sqlite_version >= "3.6.19"
122
128
  end
123
129
 
124
130
  def supports_views?
@@ -130,7 +136,7 @@ module ActiveRecord
130
136
  end
131
137
 
132
138
  def supports_multi_insert?
133
- sqlite_version >= '3.7.11'
139
+ sqlite_version >= "3.7.11"
134
140
  end
135
141
 
136
142
  def active?
@@ -154,12 +160,8 @@ module ActiveRecord
154
160
  true
155
161
  end
156
162
 
157
- def valid_type?(type)
158
- true
159
- end
160
-
161
163
  # Returns 62. SQLite supports index names up to 64
162
- # characters. The rest is used by rails internally to perform
164
+ # characters. The rest is used by Rails internally to perform
163
165
  # temporary rename operations
164
166
  def allowed_index_name_length
165
167
  index_name_length - 2
@@ -178,47 +180,62 @@ module ActiveRecord
178
180
  true
179
181
  end
180
182
 
183
+ # REFERENTIAL INTEGRITY ====================================
184
+
185
+ def disable_referential_integrity # :nodoc:
186
+ old = query_value("PRAGMA foreign_keys")
187
+
188
+ begin
189
+ execute("PRAGMA foreign_keys = OFF")
190
+ yield
191
+ ensure
192
+ execute("PRAGMA foreign_keys = #{old}")
193
+ end
194
+ end
195
+
181
196
  #--
182
197
  # DATABASE STATEMENTS ======================================
183
198
  #++
184
199
 
185
200
  def explain(arel, binds = [])
186
201
  sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
187
- SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
202
+ SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
188
203
  end
189
204
 
190
205
  def exec_query(sql, name = nil, binds = [], prepare: false)
191
206
  type_casted_binds = type_casted_binds(binds)
192
207
 
193
208
  log(sql, name, binds, type_casted_binds) do
194
- # Don't cache statements if they are not prepared
195
- unless prepare
196
- stmt = @connection.prepare(sql)
197
- begin
198
- cols = stmt.columns
199
- unless without_prepared_statement?(binds)
200
- stmt.bind_params(type_casted_binds)
209
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
210
+ # Don't cache statements if they are not prepared
211
+ unless prepare
212
+ stmt = @connection.prepare(sql)
213
+ begin
214
+ cols = stmt.columns
215
+ unless without_prepared_statement?(binds)
216
+ stmt.bind_params(type_casted_binds)
217
+ end
218
+ records = stmt.to_a
219
+ ensure
220
+ stmt.close
201
221
  end
222
+ else
223
+ cache = @statements[sql] ||= {
224
+ stmt: @connection.prepare(sql)
225
+ }
226
+ stmt = cache[:stmt]
227
+ cols = cache[:cols] ||= stmt.columns
228
+ stmt.reset!
229
+ stmt.bind_params(type_casted_binds)
202
230
  records = stmt.to_a
203
- ensure
204
- stmt.close
205
231
  end
206
- else
207
- cache = @statements[sql] ||= {
208
- :stmt => @connection.prepare(sql)
209
- }
210
- stmt = cache[:stmt]
211
- cols = cache[:cols] ||= stmt.columns
212
- stmt.reset!
213
- stmt.bind_params(type_casted_binds)
214
- records = stmt.to_a
215
- end
216
232
 
217
- ActiveRecord::Result.new(cols, records)
233
+ ActiveRecord::Result.new(cols, records)
234
+ end
218
235
  end
219
236
  end
220
237
 
221
- def exec_delete(sql, name = 'SQL', binds = [])
238
+ def exec_delete(sql, name = "SQL", binds = [])
222
239
  exec_query(sql, name, binds)
223
240
  @connection.changes
224
241
  end
@@ -229,98 +246,52 @@ module ActiveRecord
229
246
  end
230
247
 
231
248
  def execute(sql, name = nil) #:nodoc:
232
- log(sql, name) { @connection.execute(sql) }
249
+ log(sql, name) do
250
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
251
+ @connection.execute(sql)
252
+ end
253
+ end
233
254
  end
234
255
 
235
256
  def begin_db_transaction #:nodoc:
236
- log('begin transaction',nil) { @connection.transaction }
257
+ log("begin transaction", nil) { @connection.transaction }
237
258
  end
238
259
 
239
260
  def commit_db_transaction #:nodoc:
240
- log('commit transaction',nil) { @connection.commit }
261
+ log("commit transaction", nil) { @connection.commit }
241
262
  end
242
263
 
243
264
  def exec_rollback_db_transaction #:nodoc:
244
- log('rollback transaction',nil) { @connection.rollback }
265
+ log("rollback transaction", nil) { @connection.rollback }
245
266
  end
246
267
 
247
268
  # SCHEMA STATEMENTS ========================================
248
269
 
249
- def tables(name = nil) # :nodoc:
250
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
251
- #tables currently returns both tables and views.
252
- This behavior is deprecated and will be changed with Rails 5.1 to only return tables.
253
- Use #data_sources instead.
254
- MSG
255
-
256
- if name
257
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
258
- Passing arguments to #tables is deprecated without replacement.
259
- MSG
270
+ def new_column_from_field(table_name, field) # :nondoc:
271
+ case field["dflt_value"]
272
+ when /^null$/i
273
+ field["dflt_value"] = nil
274
+ when /^'(.*)'$/m
275
+ field["dflt_value"] = $1.gsub("''", "'")
276
+ when /^"(.*)"$/m
277
+ field["dflt_value"] = $1.gsub('""', '"')
260
278
  end
261
279
 
262
- data_sources
263
- end
264
-
265
- def data_sources
266
- select_values("SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'", 'SCHEMA')
267
- end
268
-
269
- def table_exists?(table_name)
270
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
271
- #table_exists? currently checks both tables and views.
272
- This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
273
- Use #data_source_exists? instead.
274
- MSG
275
-
276
- data_source_exists?(table_name)
277
- end
278
-
279
- def data_source_exists?(table_name)
280
- return false unless table_name.present?
281
-
282
- sql = "SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'"
283
- sql << " AND name = #{quote(table_name)}"
284
-
285
- select_values(sql, 'SCHEMA').any?
286
- end
287
-
288
- def views # :nodoc:
289
- select_values("SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'", 'SCHEMA')
290
- end
291
-
292
- def view_exists?(view_name) # :nodoc:
293
- return false unless view_name.present?
294
-
295
- sql = "SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'"
296
- sql << " AND name = #{quote(view_name)}"
297
-
298
- select_values(sql, 'SCHEMA').any?
299
- end
300
-
301
- # Returns an array of +Column+ objects for the table specified by +table_name+.
302
- def columns(table_name) # :nodoc:
303
- table_name = table_name.to_s
304
- table_structure(table_name).map do |field|
305
- case field["dflt_value"]
306
- when /^null$/i
307
- field["dflt_value"] = nil
308
- when /^'(.*)'$/m
309
- field["dflt_value"] = $1.gsub("''", "'")
310
- when /^"(.*)"$/m
311
- field["dflt_value"] = $1.gsub('""', '"')
312
- end
313
-
314
- collation = field['collation']
315
- sql_type = field['type']
316
- type_metadata = fetch_type_metadata(sql_type)
317
- new_column(field['name'], field['dflt_value'], type_metadata, field['notnull'].to_i == 0, table_name, nil, collation)
318
- end
280
+ collation = field["collation"]
281
+ sql_type = field["type"]
282
+ type_metadata = fetch_type_metadata(sql_type)
283
+ new_column(field["name"], field["dflt_value"], type_metadata, field["notnull"].to_i == 0, table_name, nil, collation)
319
284
  end
320
285
 
321
286
  # Returns an array of indexes for the given table.
322
287
  def indexes(table_name, name = nil) #:nodoc:
323
- exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').map do |row|
288
+ if name
289
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
290
+ Passing name to #indexes is deprecated without replacement.
291
+ MSG
292
+ end
293
+
294
+ exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").map do |row|
324
295
  sql = <<-SQL
325
296
  SELECT sql
326
297
  FROM sqlite_master
@@ -330,22 +301,22 @@ module ActiveRecord
330
301
  FROM sqlite_temp_master
331
302
  WHERE name=#{quote(row['name'])} AND type='index'
332
303
  SQL
333
- index_sql = exec_query(sql).first['sql']
304
+ index_sql = exec_query(sql).first["sql"]
334
305
  match = /\sWHERE\s+(.+)$/i.match(index_sql)
335
306
  where = match[1] if match
336
307
  IndexDefinition.new(
337
308
  table_name,
338
- row['name'],
339
- row['unique'] != 0,
309
+ row["name"],
310
+ row["unique"] != 0,
340
311
  exec_query("PRAGMA index_info('#{row['name']}')", "SCHEMA").map { |col|
341
- col['name']
312
+ col["name"]
342
313
  }, nil, nil, where)
343
314
  end
344
315
  end
345
316
 
346
317
  def primary_keys(table_name) # :nodoc:
347
- pks = table_structure(table_name).select { |f| f['pk'] > 0 }
348
- pks.sort_by { |f| f['pk'] }.map { |f| f['name'] }
318
+ pks = table_structure(table_name).select { |f| f["pk"] > 0 }
319
+ pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
349
320
  end
350
321
 
351
322
  def remove_index(table_name, options = {}) #:nodoc:
@@ -369,7 +340,7 @@ module ActiveRecord
369
340
  end
370
341
 
371
342
  def add_column(table_name, column_name, type, options = {}) #:nodoc:
372
- if valid_alter_table_type?(type)
343
+ if valid_alter_table_type?(type) && !options[:primary_key]
373
344
  super(table_name, column_name, type, options)
374
345
  else
375
346
  alter_table(table_name) do |definition|
@@ -403,14 +374,13 @@ module ActiveRecord
403
374
 
404
375
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
405
376
  alter_table(table_name) do |definition|
406
- include_default = options_include_default?(options)
407
377
  definition[column_name].instance_eval do
408
378
  self.type = type
409
379
  self.limit = options[:limit] if options.include?(:limit)
410
- self.default = options[:default] if include_default
380
+ self.default = options[:default] if options.include?(:default)
411
381
  self.null = options[:null] if options.include?(:null)
412
382
  self.precision = options[:precision] if options.include?(:precision)
413
- self.scale = options[:scale] if options.include?(:scale)
383
+ self.scale = options[:scale] if options.include?(:scale)
414
384
  self.collation = options[:collation] if options.include?(:collation)
415
385
  end
416
386
  end
@@ -418,51 +388,73 @@ module ActiveRecord
418
388
 
419
389
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
420
390
  column = column_for(table_name, column_name)
421
- alter_table(table_name, rename: {column.name => new_column_name.to_s})
391
+ alter_table(table_name, rename: { column.name => new_column_name.to_s })
422
392
  rename_column_indexes(table_name, column.name, new_column_name)
423
393
  end
424
394
 
425
- protected
395
+ def add_reference(table_name, ref_name, **options) # :nodoc:
396
+ super(table_name, ref_name, type: :integer, **options)
397
+ end
398
+ alias :add_belongs_to :add_reference
399
+
400
+ def foreign_keys(table_name)
401
+ fk_info = exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
402
+ fk_info.map do |row|
403
+ options = {
404
+ column: row["from"],
405
+ primary_key: row["to"],
406
+ on_delete: extract_foreign_key_action(row["on_delete"]),
407
+ on_update: extract_foreign_key_action(row["on_update"])
408
+ }
409
+ ForeignKeyDefinition.new(table_name, row["table"], options)
410
+ end
411
+ end
412
+
413
+ private
426
414
 
427
415
  def table_structure(table_name)
428
- structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA')
416
+ structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
429
417
  raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
430
418
  table_structure_with_collation(table_name, structure)
431
419
  end
420
+ alias column_definitions table_structure
432
421
 
433
- def alter_table(table_name, options = {}) #:nodoc:
422
+ def alter_table(table_name, options = {})
434
423
  altered_table_name = "a#{table_name}"
435
- caller = lambda {|definition| yield definition if block_given?}
424
+ caller = lambda { |definition| yield definition if block_given? }
436
425
 
437
426
  transaction do
438
427
  move_table(table_name, altered_table_name,
439
- options.merge(:temporary => true))
428
+ options.merge(temporary: true))
440
429
  move_table(altered_table_name, table_name, &caller)
441
430
  end
442
431
  end
443
432
 
444
- def move_table(from, to, options = {}, &block) #:nodoc:
433
+ def move_table(from, to, options = {}, &block)
445
434
  copy_table(from, to, options, &block)
446
435
  drop_table(from)
447
436
  end
448
437
 
449
- def copy_table(from, to, options = {}) #:nodoc:
438
+ def copy_table(from, to, options = {})
450
439
  from_primary_key = primary_key(from)
451
440
  options[:id] = false
452
441
  create_table(to, options) do |definition|
453
442
  @definition = definition
454
- @definition.primary_key(from_primary_key) if from_primary_key.present?
443
+ if from_primary_key.is_a?(Array)
444
+ @definition.primary_keys from_primary_key
445
+ end
455
446
  columns(from).each do |column|
456
447
  column_name = options[:rename] ?
457
448
  (options[:rename][column.name] ||
458
449
  options[:rename][column.name.to_sym] ||
459
450
  column.name) : column.name
460
- next if column_name == from_primary_key
461
451
 
462
452
  @definition.column(column_name, column.type,
463
- :limit => column.limit, :default => column.default,
464
- :precision => column.precision, :scale => column.scale,
465
- :null => column.null, collation: column.collation)
453
+ limit: column.limit, default: column.default,
454
+ precision: column.precision, scale: column.scale,
455
+ null: column.null, collation: column.collation,
456
+ primary_key: column_name == from_primary_key
457
+ )
466
458
  end
467
459
  yield @definition if block_given?
468
460
  end
@@ -472,9 +464,12 @@ module ActiveRecord
472
464
  options[:rename] || {})
473
465
  end
474
466
 
475
- def copy_table_indexes(from, to, rename = {}) #:nodoc:
467
+ def copy_table_indexes(from, to, rename = {})
476
468
  indexes(from).each do |index|
477
469
  name = index.name
470
+ # indexes sqlite creates for internal use start with `sqlite_` and
471
+ # don't need to be copied
472
+ next if name.starts_with?("sqlite_")
478
473
  if to == "a#{from}"
479
474
  name = "t#{name}"
480
475
  elsif from == "a#{to}"
@@ -482,7 +477,7 @@ module ActiveRecord
482
477
  end
483
478
 
484
479
  to_column_names = columns(to).map(&:name)
485
- columns = index.columns.map {|c| rename[c] || c }.select do |column|
480
+ columns = index.columns.map { |c| rename[c] || c }.select do |column|
486
481
  to_column_names.include?(column)
487
482
  end
488
483
 
@@ -490,26 +485,27 @@ module ActiveRecord
490
485
  # index name can't be the same
491
486
  opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
492
487
  opts[:unique] = true if index.unique
488
+ opts[:where] = index.where if index.where
493
489
  add_index(to, columns, opts)
494
490
  end
495
491
  end
496
492
  end
497
493
 
498
- def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
499
- column_mappings = Hash[columns.map {|name| [name, name]}]
494
+ def copy_table_contents(from, to, columns, rename = {})
495
+ column_mappings = Hash[columns.map { |name| [name, name] }]
500
496
  rename.each { |a| column_mappings[a.last] = a.first }
501
497
  from_columns = columns(from).collect(&:name)
502
- columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
498
+ columns = columns.find_all { |col| from_columns.include?(column_mappings[col]) }
503
499
  from_columns_to_copy = columns.map { |col| column_mappings[col] }
504
- quoted_columns = columns.map { |col| quote_column_name(col) } * ','
505
- quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ','
500
+ quoted_columns = columns.map { |col| quote_column_name(col) } * ","
501
+ quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
506
502
 
507
503
  exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
508
504
  SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
509
505
  end
510
506
 
511
507
  def sqlite_version
512
- @sqlite_version ||= SQLite3Adapter::Version.new(select_value('select sqlite_version(*)'))
508
+ @sqlite_version ||= SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
513
509
  end
514
510
 
515
511
  def translate_exception(exception, message)
@@ -520,42 +516,47 @@ module ActiveRecord
520
516
  # column *column_name* is not unique
521
517
  when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
522
518
  RecordNotUnique.new(message)
519
+ when /.* may not be NULL/, /NOT NULL constraint failed: .*/
520
+ NotNullViolation.new(message)
521
+ when /FOREIGN KEY constraint failed/i
522
+ InvalidForeignKey.new(message)
523
523
  else
524
524
  super
525
525
  end
526
526
  end
527
527
 
528
- private
529
528
  COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
530
529
 
531
530
  def table_structure_with_collation(table_name, basic_structure)
532
531
  collation_hash = {}
533
- sql = "SELECT sql FROM
534
- (SELECT * FROM sqlite_master UNION ALL
535
- SELECT * FROM sqlite_temp_master)
536
- WHERE type='table' and name='#{ table_name }' \;"
532
+ sql = <<-SQL
533
+ SELECT sql FROM
534
+ (SELECT * FROM sqlite_master UNION ALL
535
+ SELECT * FROM sqlite_temp_master)
536
+ WHERE type = 'table' AND name = #{quote(table_name)}
537
+ SQL
537
538
 
538
539
  # Result will have following sample string
539
540
  # CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
540
541
  # "password_digest" varchar COLLATE "NOCASE");
541
- result = exec_query(sql, 'SCHEMA').first
542
+ result = exec_query(sql, "SCHEMA").first
542
543
 
543
544
  if result
544
545
  # Splitting with left parentheses and picking up last will return all
545
546
  # columns separated with comma(,).
546
- columns_string = result["sql"].split('(').last
547
+ columns_string = result["sql"].split("(").last
547
548
 
548
- columns_string.split(',').each do |column_string|
549
+ columns_string.split(",").each do |column_string|
549
550
  # This regex will match the column name and collation type and will save
550
551
  # the value in $1 and $2 respectively.
551
- collation_hash[$1] = $2 if (COLLATE_REGEX =~ column_string)
552
+ collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
552
553
  end
553
554
 
554
555
  basic_structure.map! do |column|
555
- column_name = column['name']
556
+ column_name = column["name"]
556
557
 
557
558
  if collation_hash.has_key? column_name
558
- column['collation'] = collation_hash[column_name]
559
+ column["collation"] = collation_hash[column_name]
559
560
  end
560
561
 
561
562
  column
@@ -564,6 +565,22 @@ module ActiveRecord
564
565
  basic_structure.to_hash
565
566
  end
566
567
  end
568
+
569
+ def create_table_definition(*args)
570
+ SQLite3::TableDefinition.new(*args)
571
+ end
572
+
573
+ def extract_foreign_key_action(specifier)
574
+ case specifier
575
+ when "CASCADE"; :cascade
576
+ when "SET NULL"; :nullify
577
+ when "RESTRICT"; :restrict
578
+ end
579
+ end
580
+
581
+ def configure_connection
582
+ execute("PRAGMA foreign_keys = ON", "SCHEMA")
583
+ end
567
584
  end
568
585
  end
569
586
  end
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  DEFAULT_STATEMENT_LIMIT = 1000
7
7
 
8
8
  def initialize(statement_limit = nil)
9
- @cache = Hash.new { |h,pid| h[pid] = {} }
9
+ @cache = Hash.new { |h, pid| h[pid] = {} }
10
10
  @statement_limit = statement_limit || DEFAULT_STATEMENT_LIMIT
11
11
  end
12
12
 
@@ -47,13 +47,13 @@ module ActiveRecord
47
47
 
48
48
  private
49
49
 
50
- def cache
51
- @cache[Process.pid]
52
- end
50
+ def cache
51
+ @cache[Process.pid]
52
+ end
53
53
 
54
- def dealloc(stmt)
55
- raise NotImplementedError
56
- end
54
+ def dealloc(stmt)
55
+ raise NotImplementedError
56
+ end
57
57
  end
58
58
  end
59
59
  end