activerecord 4.2.11 → 5.2.4.1

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 (274) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +580 -1626
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -11
  5. data/examples/performance.rb +32 -31
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +263 -249
  8. data/lib/active_record/association_relation.rb +11 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +77 -43
  11. data/lib/active_record/associations/association_scope.rb +106 -133
  12. data/lib/active_record/associations/belongs_to_association.rb +52 -41
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +29 -38
  15. data/lib/active_record/associations/builder/belongs_to.rb +77 -30
  16. data/lib/active_record/associations/builder/collection_association.rb +9 -22
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -35
  18. data/lib/active_record/associations/builder/has_many.rb +6 -4
  19. data/lib/active_record/associations/builder/has_one.rb +13 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +15 -11
  21. data/lib/active_record/associations/collection_association.rb +139 -280
  22. data/lib/active_record/associations/collection_proxy.rb +231 -133
  23. data/lib/active_record/associations/foreign_association.rb +3 -1
  24. data/lib/active_record/associations/has_many_association.rb +34 -89
  25. data/lib/active_record/associations/has_many_through_association.rb +49 -76
  26. data/lib/active_record/associations/has_one_association.rb +38 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +40 -87
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +133 -159
  32. data/lib/active_record/associations/preloader/association.rb +85 -120
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +81 -91
  35. data/lib/active_record/associations/singular_association.rb +27 -34
  36. data/lib/active_record/associations/through_association.rb +38 -18
  37. data/lib/active_record/associations.rb +1732 -1597
  38. data/lib/active_record/attribute_assignment.rb +58 -182
  39. data/lib/active_record/attribute_decorators.rb +39 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +10 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -135
  42. data/lib/active_record/attribute_methods/primary_key.rb +86 -71
  43. data/lib/active_record/attribute_methods/query.rb +4 -2
  44. data/lib/active_record/attribute_methods/read.rb +45 -63
  45. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
  47. data/lib/active_record/attribute_methods/write.rb +30 -45
  48. data/lib/active_record/attribute_methods.rb +166 -109
  49. data/lib/active_record/attributes.rb +201 -82
  50. data/lib/active_record/autosave_association.rb +94 -36
  51. data/lib/active_record/base.rb +57 -44
  52. data/lib/active_record/callbacks.rb +97 -57
  53. data/lib/active_record/coders/json.rb +3 -1
  54. data/lib/active_record/coders/yaml_column.rb +24 -12
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -290
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +237 -90
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +71 -21
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +118 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +318 -217
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +570 -228
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +138 -70
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +325 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +542 -601
  69. data/lib/active_record/connection_adapters/column.rb +50 -41
  70. data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +41 -180
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +45 -114
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -7
  99. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +55 -53
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -284
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +432 -323
  117. data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -308
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +40 -27
  128. data/lib/active_record/core.rb +178 -198
  129. data/lib/active_record/counter_cache.rb +79 -36
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +87 -105
  132. data/lib/active_record/enum.rb +135 -88
  133. data/lib/active_record/errors.rb +179 -52
  134. data/lib/active_record/explain.rb +23 -11
  135. data/lib/active_record/explain_registry.rb +4 -2
  136. data/lib/active_record/explain_subscriber.rb +10 -5
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +188 -132
  139. data/lib/active_record/gem_version.rb +5 -3
  140. data/lib/active_record/inheritance.rb +148 -112
  141. data/lib/active_record/integration.rb +70 -28
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +88 -96
  146. data/lib/active_record/locking/pessimistic.rb +15 -3
  147. data/lib/active_record/log_subscriber.rb +95 -33
  148. data/lib/active_record/migration/command_recorder.rb +133 -90
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +8 -6
  151. data/lib/active_record/migration.rb +581 -282
  152. data/lib/active_record/model_schema.rb +290 -111
  153. data/lib/active_record/nested_attributes.rb +264 -222
  154. data/lib/active_record/no_touching.rb +7 -1
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +347 -119
  157. data/lib/active_record/query_cache.rb +13 -24
  158. data/lib/active_record/querying.rb +19 -17
  159. data/lib/active_record/railtie.rb +94 -32
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +9 -3
  162. data/lib/active_record/railties/databases.rake +149 -156
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +414 -267
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +204 -55
  167. data/lib/active_record/relation/calculations.rb +256 -248
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +288 -239
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +86 -86
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -24
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  179. data/lib/active_record/relation/predicate_builder.rb +116 -119
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +448 -393
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +11 -13
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +287 -340
  187. data/lib/active_record/result.rb +54 -36
  188. data/lib/active_record/runtime_registry.rb +6 -4
  189. data/lib/active_record/sanitization.rb +155 -124
  190. data/lib/active_record/schema.rb +30 -24
  191. data/lib/active_record/schema_dumper.rb +91 -87
  192. data/lib/active_record/schema_migration.rb +19 -16
  193. data/lib/active_record/scoping/default.rb +102 -85
  194. data/lib/active_record/scoping/named.rb +81 -32
  195. data/lib/active_record/scoping.rb +45 -26
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +5 -5
  198. data/lib/active_record/statement_cache.rb +45 -35
  199. data/lib/active_record/store.rb +42 -36
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +134 -96
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +56 -100
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +83 -41
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
  206. data/lib/active_record/timestamp.rb +70 -38
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +199 -124
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +4 -45
  212. data/lib/active_record/type/date_time.rb +4 -49
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +24 -15
  218. data/lib/active_record/type/text.rb +2 -2
  219. data/lib/active_record/type/time.rb +11 -16
  220. data/lib/active_record/type/type_map.rb +15 -17
  221. data/lib/active_record/type/unsigned_integer.rb +9 -7
  222. data/lib/active_record/type.rb +79 -23
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +13 -4
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +14 -13
  230. data/lib/active_record/validations/uniqueness.rb +40 -41
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +34 -22
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
  237. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -3
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -1
  239. data/lib/rails/generators/active_record/migration.rb +18 -1
  240. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  243. data/lib/rails/generators/active_record.rb +7 -5
  244. metadata +72 -50
  245. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  246. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  247. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  248. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  249. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  250. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  251. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  252. data/lib/active_record/attribute.rb +0 -163
  253. data/lib/active_record/attribute_set/builder.rb +0 -106
  254. data/lib/active_record/attribute_set.rb +0 -81
  255. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  256. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  257. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  258. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  259. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  260. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  261. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  262. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  263. data/lib/active_record/type/big_integer.rb +0 -13
  264. data/lib/active_record/type/binary.rb +0 -50
  265. data/lib/active_record/type/boolean.rb +0 -31
  266. data/lib/active_record/type/decimal.rb +0 -64
  267. data/lib/active_record/type/decorator.rb +0 -14
  268. data/lib/active_record/type/float.rb +0 -19
  269. data/lib/active_record/type/integer.rb +0 -59
  270. data/lib/active_record/type/mutable.rb +0 -16
  271. data/lib/active_record/type/numeric.rb +0 -36
  272. data/lib/active_record/type/string.rb +0 -40
  273. data/lib/active_record/type/time_value.rb +0 -38
  274. data/lib/active_record/type/value.rb +0 -110
@@ -1,13 +1,19 @@
1
- require 'active_record/connection_adapters/abstract_adapter'
2
- require 'active_record/connection_adapters/statement_pool'
3
- require 'arel/visitors/bind_visitor'
1
+ # frozen_string_literal: true
4
2
 
5
- gem 'sqlite3', '~> 1.3.6'
6
- require 'sqlite3'
3
+ require "active_record/connection_adapters/abstract_adapter"
4
+ require "active_record/connection_adapters/statement_pool"
5
+ require "active_record/connection_adapters/sqlite3/explain_pretty_printer"
6
+ require "active_record/connection_adapters/sqlite3/quoting"
7
+ require "active_record/connection_adapters/sqlite3/schema_creation"
8
+ require "active_record/connection_adapters/sqlite3/schema_definitions"
9
+ require "active_record/connection_adapters/sqlite3/schema_dumper"
10
+ require "active_record/connection_adapters/sqlite3/schema_statements"
11
+
12
+ gem "sqlite3", "~> 1.3", ">= 1.3.6"
13
+ require "sqlite3"
7
14
 
8
15
  module ActiveRecord
9
16
  module ConnectionHandling # :nodoc:
10
- # sqlite3 adapter reuses sqlite_connection.
11
17
  def sqlite3_connection(config)
12
18
  # Require database.
13
19
  unless config[:database]
@@ -17,7 +23,7 @@ module ActiveRecord
17
23
  # Allow database path relative to Rails.root, but only if the database
18
24
  # path is not the special path that tells sqlite to build a database only
19
25
  # in memory.
20
- if ':memory:' != config[:database]
26
+ if ":memory:" != config[:database]
21
27
  config[:database] = File.expand_path(config[:database], Rails.root) if defined?(Rails.root)
22
28
  dirname = File.dirname(config[:database])
23
29
  Dir.mkdir(dirname) unless File.directory?(dirname)
@@ -25,7 +31,7 @@ module ActiveRecord
25
31
 
26
32
  db = SQLite3::Database.new(
27
33
  config[:database].to_s,
28
- :results_as_hash => true
34
+ results_as_hash: true
29
35
  )
30
36
 
31
37
  db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]
@@ -33,7 +39,7 @@ module ActiveRecord
33
39
  ConnectionAdapters::SQLite3Adapter.new(db, logger, nil, config)
34
40
  rescue Errno::ENOENT => error
35
41
  if error.message.include?("No such file or directory")
36
- raise ActiveRecord::NoDatabaseError.new(error.message, error)
42
+ raise ActiveRecord::NoDatabaseError
37
43
  else
38
44
  raise
39
45
  end
@@ -41,15 +47,6 @@ module ActiveRecord
41
47
  end
42
48
 
43
49
  module ConnectionAdapters #:nodoc:
44
- class SQLite3Binary < Type::Binary # :nodoc:
45
- def cast_value(value)
46
- if value.encoding != Encoding::ASCII_8BIT
47
- value = value.force_encoding(Encoding::ASCII_8BIT)
48
- end
49
- value
50
- end
51
- end
52
-
53
50
  # The SQLite3 adapter works SQLite 3.6.16 or newer
54
51
  # with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
55
52
  #
@@ -57,11 +54,13 @@ module ActiveRecord
57
54
  #
58
55
  # * <tt>:database</tt> - Path to the database file.
59
56
  class SQLite3Adapter < AbstractAdapter
60
- ADAPTER_NAME = 'SQLite'.freeze
61
- include Savepoints
57
+ ADAPTER_NAME = "SQLite".freeze
58
+
59
+ include SQLite3::Quoting
60
+ include SQLite3::SchemaStatements
62
61
 
63
62
  NATIVE_DATABASE_TYPES = {
64
- primary_key: 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL',
63
+ primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
65
64
  string: { name: "varchar" },
66
65
  text: { name: "text" },
67
66
  integer: { name: "integer" },
@@ -71,59 +70,41 @@ module ActiveRecord
71
70
  time: { name: "time" },
72
71
  date: { name: "date" },
73
72
  binary: { name: "blob" },
74
- boolean: { name: "boolean" }
73
+ boolean: { name: "boolean" },
74
+ json: { name: "json" },
75
75
  }
76
76
 
77
- class StatementPool < ConnectionAdapters::StatementPool
78
- def initialize(connection, max)
79
- super
80
- @cache = Hash.new { |h,pid| h[pid] = {} }
81
- end
82
-
83
- def each(&block); cache.each(&block); end
84
- def key?(key); cache.key?(key); end
85
- def [](key); cache[key]; end
86
- def length; cache.length; end
87
-
88
- def []=(sql, key)
89
- while @max <= cache.size
90
- dealloc(cache.shift.last[:stmt])
91
- end
92
- cache[sql] = key
93
- end
94
-
95
- def clear
96
- cache.each_value do |hash|
97
- dealloc hash[:stmt]
98
- end
99
- cache.clear
100
- end
77
+ ##
78
+ # :singleton-method:
79
+ # Indicates whether boolean values are stored in sqlite3 databases as 1
80
+ # and 0 or 't' and 'f'. Leaving <tt>ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer</tt>
81
+ # set to false is deprecated. SQLite databases have used 't' and 'f' to
82
+ # serialize boolean values and must have old data converted to 1 and 0
83
+ # (its native boolean serialization) before setting this flag to true.
84
+ # Conversion can be accomplished by setting up a rake task which runs
85
+ #
86
+ # ExampleModel.where("boolean_column = 't'").update_all(boolean_column: 1)
87
+ # ExampleModel.where("boolean_column = 'f'").update_all(boolean_column: 0)
88
+ # for all models and all boolean columns, after which the flag must be set
89
+ # to true by adding the following to your <tt>application.rb</tt> file:
90
+ #
91
+ # Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true
92
+ class_attribute :represent_boolean_as_integer, default: false
101
93
 
94
+ class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
102
95
  private
103
- def cache
104
- @cache[$$]
105
- end
106
-
107
- def dealloc(stmt)
108
- stmt.close unless stmt.closed?
109
- end
96
+ def dealloc(stmt)
97
+ stmt[:stmt].close unless stmt[:stmt].closed?
98
+ end
110
99
  end
111
100
 
112
101
  def initialize(connection, logger, connection_options, config)
113
- super(connection, logger)
114
-
115
- @active = nil
116
- @statements = StatementPool.new(@connection,
117
- self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
118
- @config = config
102
+ super(connection, logger, config)
119
103
 
120
- @visitor = Arel::Visitors::SQLite.new self
104
+ @active = true
105
+ @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
121
106
 
122
- if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
123
- @prepared_statements = true
124
- else
125
- @prepared_statements = false
126
- end
107
+ configure_connection
127
108
  end
128
109
 
129
110
  def supports_ddl_transactions?
@@ -135,34 +116,35 @@ module ActiveRecord
135
116
  end
136
117
 
137
118
  def supports_partial_index?
138
- sqlite_version >= '3.8.0'
119
+ sqlite_version >= "3.8.0"
139
120
  end
140
121
 
141
- # Returns true, since this connection adapter supports prepared statement
142
- # caching.
143
- def supports_statement_cache?
122
+ def requires_reloading?
144
123
  true
145
124
  end
146
125
 
147
- # Returns true, since this connection adapter supports migrations.
148
- def supports_migrations? #:nodoc:
149
- true
126
+ def supports_foreign_keys_in_create?
127
+ sqlite_version >= "3.6.19"
150
128
  end
151
129
 
152
- def supports_primary_key? #:nodoc:
130
+ def supports_views?
153
131
  true
154
132
  end
155
133
 
156
- def requires_reloading?
134
+ def supports_datetime_with_precision?
157
135
  true
158
136
  end
159
137
 
160
- def supports_views?
138
+ def supports_json?
161
139
  true
162
140
  end
163
141
 
142
+ def supports_multi_insert?
143
+ sqlite_version >= "3.7.11"
144
+ end
145
+
164
146
  def active?
165
- @active != false
147
+ @active
166
148
  end
167
149
 
168
150
  # Disconnects from the database if already connected. Otherwise, this
@@ -183,7 +165,7 @@ module ActiveRecord
183
165
  end
184
166
 
185
167
  # Returns 62. SQLite supports index names up to 64
186
- # characters. The rest is used by rails internally to perform
168
+ # characters. The rest is used by Rails internally to perform
187
169
  # temporary rename operations
188
170
  def allowed_index_name_length
189
171
  index_name_length - 2
@@ -202,51 +184,16 @@ module ActiveRecord
202
184
  true
203
185
  end
204
186
 
205
- # QUOTING ==================================================
206
-
207
- def _quote(value) # :nodoc:
208
- case value
209
- when Type::Binary::Data
210
- "x'#{value.hex}'"
211
- else
212
- super
213
- end
214
- end
215
-
216
- def _type_cast(value) # :nodoc:
217
- case value
218
- when BigDecimal
219
- value.to_f
220
- when String
221
- if value.encoding == Encoding::ASCII_8BIT
222
- super(value.encode(Encoding::UTF_8))
223
- else
224
- super
225
- end
226
- else
227
- super
228
- end
229
- end
230
-
231
- def quote_string(s) #:nodoc:
232
- @connection.class.quote(s)
233
- end
234
-
235
- def quote_table_name_for_assignment(table, attr)
236
- quote_column_name(attr)
237
- end
187
+ # REFERENTIAL INTEGRITY ====================================
238
188
 
239
- def quote_column_name(name) #:nodoc:
240
- %Q("#{name.to_s.gsub('"', '""')}")
241
- end
189
+ def disable_referential_integrity # :nodoc:
190
+ old = query_value("PRAGMA foreign_keys")
242
191
 
243
- # Quote date/time values for use in SQL input. Includes microseconds
244
- # if the value is a Time responding to usec.
245
- def quoted_date(value) #:nodoc:
246
- if value.respond_to?(:usec)
247
- "#{super}.#{sprintf("%06d", value.usec)}"
248
- else
249
- super
192
+ begin
193
+ execute("PRAGMA foreign_keys = OFF")
194
+ yield
195
+ ensure
196
+ execute("PRAGMA foreign_keys = #{old}")
250
197
  end
251
198
  end
252
199
 
@@ -256,54 +203,43 @@ module ActiveRecord
256
203
 
257
204
  def explain(arel, binds = [])
258
205
  sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
259
- ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
260
- end
261
-
262
- class ExplainPrettyPrinter
263
- # Pretty prints the result of a EXPLAIN QUERY PLAN in a way that resembles
264
- # the output of the SQLite shell:
265
- #
266
- # 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
267
- # 0|1|1|SCAN TABLE posts (~100000 rows)
268
- #
269
- def pp(result) # :nodoc:
270
- result.rows.map do |row|
271
- row.join('|')
272
- end.join("\n") + "\n"
273
- end
274
- end
275
-
276
- def exec_query(sql, name = nil, binds = [])
277
- type_casted_binds = binds.map { |col, val|
278
- [col, type_cast(val, col)]
279
- }
280
-
281
- log(sql, name, type_casted_binds) do
282
- # Don't cache statements if they are not prepared
283
- if without_prepared_statement?(binds)
284
- stmt = @connection.prepare(sql)
285
- begin
286
- cols = stmt.columns
206
+ SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
207
+ end
208
+
209
+ def exec_query(sql, name = nil, binds = [], prepare: false)
210
+ type_casted_binds = type_casted_binds(binds)
211
+
212
+ log(sql, name, binds, type_casted_binds) do
213
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
214
+ # Don't cache statements if they are not prepared
215
+ unless prepare
216
+ stmt = @connection.prepare(sql)
217
+ begin
218
+ cols = stmt.columns
219
+ unless without_prepared_statement?(binds)
220
+ stmt.bind_params(type_casted_binds)
221
+ end
222
+ records = stmt.to_a
223
+ ensure
224
+ stmt.close
225
+ end
226
+ else
227
+ cache = @statements[sql] ||= {
228
+ stmt: @connection.prepare(sql)
229
+ }
230
+ stmt = cache[:stmt]
231
+ cols = cache[:cols] ||= stmt.columns
232
+ stmt.reset!
233
+ stmt.bind_params(type_casted_binds)
287
234
  records = stmt.to_a
288
- ensure
289
- stmt.close
290
235
  end
291
- stmt = records
292
- else
293
- cache = @statements[sql] ||= {
294
- :stmt => @connection.prepare(sql)
295
- }
296
- stmt = cache[:stmt]
297
- cols = cache[:cols] ||= stmt.columns
298
- stmt.reset!
299
- stmt.bind_params type_casted_binds.map { |_, val| val }
300
- end
301
236
 
302
- ActiveRecord::Result.new(cols, stmt.to_a)
237
+ ActiveRecord::Result.new(cols, records)
238
+ end
303
239
  end
304
240
  end
305
241
 
306
- def exec_delete(sql, name = 'SQL', binds = [])
242
+ def exec_delete(sql, name = "SQL", binds = [])
307
243
  exec_query(sql, name, binds)
308
244
  @connection.changes
309
245
  end
@@ -314,112 +250,34 @@ module ActiveRecord
314
250
  end
315
251
 
316
252
  def execute(sql, name = nil) #:nodoc:
317
- log(sql, name) { @connection.execute(sql) }
318
- end
319
-
320
- def update_sql(sql, name = nil) #:nodoc:
321
- super
322
- @connection.changes
323
- end
324
-
325
- def delete_sql(sql, name = nil) #:nodoc:
326
- sql += " WHERE 1=1" unless sql =~ /WHERE/i
327
- super sql, name
328
- end
329
-
330
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
331
- super
332
- id_value || @connection.last_insert_row_id
333
- end
334
- alias :create :insert_sql
335
-
336
- def select_rows(sql, name = nil, binds = [])
337
- exec_query(sql, name, binds).rows
253
+ log(sql, name) do
254
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
255
+ @connection.execute(sql)
256
+ end
257
+ end
338
258
  end
339
259
 
340
260
  def begin_db_transaction #:nodoc:
341
- log('begin transaction',nil) { @connection.transaction }
261
+ log("begin transaction", nil) { @connection.transaction }
342
262
  end
343
263
 
344
264
  def commit_db_transaction #:nodoc:
345
- log('commit transaction',nil) { @connection.commit }
265
+ log("commit transaction", nil) { @connection.commit }
346
266
  end
347
267
 
348
268
  def exec_rollback_db_transaction #:nodoc:
349
- log('rollback transaction',nil) { @connection.rollback }
269
+ log("rollback transaction", nil) { @connection.rollback }
350
270
  end
351
271
 
352
272
  # SCHEMA STATEMENTS ========================================
353
273
 
354
- def tables(name = nil, table_name = nil) #:nodoc:
355
- sql = <<-SQL
356
- SELECT name
357
- FROM sqlite_master
358
- WHERE (type = 'table' OR type = 'view') AND NOT name = 'sqlite_sequence'
359
- SQL
360
- sql << " AND name = #{quote_table_name(table_name)}" if table_name
361
-
362
- exec_query(sql, 'SCHEMA').map do |row|
363
- row['name']
364
- end
365
- end
366
- alias data_sources tables
367
-
368
- def table_exists?(table_name)
369
- table_name && tables(nil, table_name).any?
370
- end
371
- alias data_source_exists? table_exists?
372
-
373
- # Returns an array of +Column+ objects for the table specified by +table_name+.
374
- def columns(table_name) #:nodoc:
375
- table_structure(table_name).map do |field|
376
- case field["dflt_value"]
377
- when /^null$/i
378
- field["dflt_value"] = nil
379
- when /^'(.*)'$/m
380
- field["dflt_value"] = $1.gsub("''", "'")
381
- when /^"(.*)"$/m
382
- field["dflt_value"] = $1.gsub('""', '"')
383
- end
384
-
385
- sql_type = field['type']
386
- cast_type = lookup_cast_type(sql_type)
387
- new_column(field['name'], field['dflt_value'], cast_type, sql_type, field['notnull'].to_i == 0)
388
- end
389
- end
390
-
391
- # Returns an array of indexes for the given table.
392
- def indexes(table_name, name = nil) #:nodoc:
393
- exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').map do |row|
394
- sql = <<-SQL
395
- SELECT sql
396
- FROM sqlite_master
397
- WHERE name=#{quote(row['name'])} AND type='index'
398
- UNION ALL
399
- SELECT sql
400
- FROM sqlite_temp_master
401
- WHERE name=#{quote(row['name'])} AND type='index'
402
- SQL
403
- index_sql = exec_query(sql).first['sql']
404
- match = /\sWHERE\s+(.+)$/i.match(index_sql)
405
- where = match[1] if match
406
- IndexDefinition.new(
407
- table_name,
408
- row['name'],
409
- row['unique'] != 0,
410
- exec_query("PRAGMA index_info('#{row['name']}')", "SCHEMA").map { |col|
411
- col['name']
412
- }, nil, nil, where)
413
- end
414
- end
415
-
416
- def primary_key(table_name) #:nodoc:
417
- pks = table_structure(table_name).select { |f| f['pk'] > 0 }
418
- return nil unless pks.count == 1
419
- pks[0]['name']
274
+ def primary_keys(table_name) # :nodoc:
275
+ pks = table_structure(table_name).select { |f| f["pk"] > 0 }
276
+ pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
420
277
  end
421
278
 
422
- def remove_index!(table_name, index_name) #:nodoc:
279
+ def remove_index(table_name, options = {}) #:nodoc:
280
+ index_name = index_name_for_remove(table_name, options)
423
281
  exec_query "DROP INDEX #{quote_column_name(index_name)}"
424
282
  end
425
283
 
@@ -432,19 +290,18 @@ module ActiveRecord
432
290
  rename_table_indexes(table_name, new_name)
433
291
  end
434
292
 
435
- # See: http://www.sqlite.org/lang_altertable.html
436
- # SQLite has an additional restriction on the ALTER TABLE statement
437
- def valid_alter_table_type?(type)
438
- type.to_sym != :primary_key
293
+ def valid_alter_table_type?(type, options = {})
294
+ !invalid_alter_table_type?(type, options)
439
295
  end
296
+ deprecate :valid_alter_table_type?
440
297
 
441
298
  def add_column(table_name, column_name, type, options = {}) #:nodoc:
442
- if valid_alter_table_type?(type)
443
- super(table_name, column_name, type, options)
444
- else
299
+ if invalid_alter_table_type?(type, options)
445
300
  alter_table(table_name) do |definition|
446
301
  definition.column(column_name, type, options)
447
302
  end
303
+ else
304
+ super
448
305
  end
449
306
  end
450
307
 
@@ -454,13 +311,15 @@ module ActiveRecord
454
311
  end
455
312
  end
456
313
 
457
- def change_column_default(table_name, column_name, default) #:nodoc:
314
+ def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
315
+ default = extract_new_default_value(default_or_changes)
316
+
458
317
  alter_table(table_name) do |definition|
459
318
  definition[column_name].default = default
460
319
  end
461
320
  end
462
321
 
463
- def change_column_null(table_name, column_name, null, default = nil)
322
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
464
323
  unless null || default.nil?
465
324
  exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
466
325
  end
@@ -471,80 +330,127 @@ module ActiveRecord
471
330
 
472
331
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
473
332
  alter_table(table_name) do |definition|
474
- include_default = options_include_default?(options)
475
333
  definition[column_name].instance_eval do
476
334
  self.type = type
477
335
  self.limit = options[:limit] if options.include?(:limit)
478
- self.default = options[:default] if include_default
336
+ self.default = options[:default] if options.include?(:default)
479
337
  self.null = options[:null] if options.include?(:null)
480
338
  self.precision = options[:precision] if options.include?(:precision)
481
- self.scale = options[:scale] if options.include?(:scale)
339
+ self.scale = options[:scale] if options.include?(:scale)
340
+ self.collation = options[:collation] if options.include?(:collation)
482
341
  end
483
342
  end
484
343
  end
485
344
 
486
345
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
487
346
  column = column_for(table_name, column_name)
488
- alter_table(table_name, rename: {column.name => new_column_name.to_s})
347
+ alter_table(table_name, rename: { column.name => new_column_name.to_s })
489
348
  rename_column_indexes(table_name, column.name, new_column_name)
490
349
  end
491
350
 
492
- protected
351
+ def add_reference(table_name, ref_name, **options) # :nodoc:
352
+ super(table_name, ref_name, type: :integer, **options)
353
+ end
354
+ alias :add_belongs_to :add_reference
355
+
356
+ def foreign_keys(table_name)
357
+ fk_info = exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
358
+ fk_info.map do |row|
359
+ options = {
360
+ column: row["from"],
361
+ primary_key: row["to"],
362
+ on_delete: extract_foreign_key_action(row["on_delete"]),
363
+ on_update: extract_foreign_key_action(row["on_update"])
364
+ }
365
+ ForeignKeyDefinition.new(table_name, row["table"], options)
366
+ end
367
+ end
368
+
369
+ def insert_fixtures(rows, table_name)
370
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
371
+ `insert_fixtures` is deprecated and will be removed in the next version of Rails.
372
+ Consider using `insert_fixtures_set` for performance improvement.
373
+ MSG
374
+ insert_fixtures_set(table_name => rows)
375
+ end
376
+
377
+ def insert_fixtures_set(fixture_set, tables_to_delete = [])
378
+ disable_referential_integrity do
379
+ transaction(requires_new: true) do
380
+ tables_to_delete.each { |table| delete "DELETE FROM #{quote_table_name(table)}", "Fixture Delete" }
381
+
382
+ fixture_set.each do |table_name, rows|
383
+ rows.each { |row| insert_fixture(row, table_name) }
384
+ end
385
+ end
386
+ end
387
+ end
493
388
 
494
- def initialize_type_map(m)
389
+ private
390
+ def initialize_type_map(m = type_map)
495
391
  super
496
- m.register_type(/binary/i, SQLite3Binary.new)
392
+ register_class_with_limit m, %r(int)i, SQLite3Integer
497
393
  end
498
394
 
499
395
  def table_structure(table_name)
500
- structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash
396
+ structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
501
397
  raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
502
- structure
398
+ table_structure_with_collation(table_name, structure)
399
+ end
400
+ alias column_definitions table_structure
401
+
402
+ # See: https://www.sqlite.org/lang_altertable.html
403
+ # SQLite has an additional restriction on the ALTER TABLE statement
404
+ def invalid_alter_table_type?(type, options)
405
+ type.to_sym == :primary_key || options[:primary_key]
503
406
  end
504
407
 
505
- def alter_table(table_name, options = {}) #:nodoc:
408
+ def alter_table(table_name, options = {})
506
409
  altered_table_name = "a#{table_name}"
507
- caller = lambda {|definition| yield definition if block_given?}
410
+ caller = lambda { |definition| yield definition if block_given? }
508
411
 
509
412
  transaction do
510
413
  move_table(table_name, altered_table_name,
511
- options.merge(:temporary => true))
414
+ options.merge(temporary: true))
512
415
  move_table(altered_table_name, table_name, &caller)
513
416
  end
514
417
  end
515
418
 
516
- def move_table(from, to, options = {}, &block) #:nodoc:
419
+ def move_table(from, to, options = {}, &block)
517
420
  copy_table(from, to, options, &block)
518
421
  drop_table(from)
519
422
  end
520
423
 
521
- def copy_table(from, to, options = {}) #:nodoc:
424
+ def copy_table(from, to, options = {})
522
425
  from_primary_key = primary_key(from)
523
426
  options[:id] = false
524
427
  create_table(to, options) do |definition|
525
428
  @definition = definition
526
- @definition.primary_key(from_primary_key) if from_primary_key.present?
429
+ if from_primary_key.is_a?(Array)
430
+ @definition.primary_keys from_primary_key
431
+ end
527
432
  columns(from).each do |column|
528
433
  column_name = options[:rename] ?
529
434
  (options[:rename][column.name] ||
530
435
  options[:rename][column.name.to_sym] ||
531
436
  column.name) : column.name
532
- next if column_name == from_primary_key
533
437
 
534
438
  @definition.column(column_name, column.type,
535
- :limit => column.limit, :default => column.default,
536
- :precision => column.precision, :scale => column.scale,
537
- :null => column.null)
439
+ limit: column.limit, default: column.default,
440
+ precision: column.precision, scale: column.scale,
441
+ null: column.null, collation: column.collation,
442
+ primary_key: column_name == from_primary_key
443
+ )
538
444
  end
539
445
  yield @definition if block_given?
540
446
  end
541
447
  copy_table_indexes(from, to, options[:rename] || {})
542
448
  copy_table_contents(from, to,
543
- @definition.columns.map {|column| column.name},
449
+ @definition.columns.map(&:name),
544
450
  options[:rename] || {})
545
451
  end
546
452
 
547
- def copy_table_indexes(from, to, rename = {}) #:nodoc:
453
+ def copy_table_indexes(from, to, rename = {})
548
454
  indexes(from).each do |index|
549
455
  name = index.name
550
456
  if to == "a#{from}"
@@ -553,8 +459,8 @@ module ActiveRecord
553
459
  name = name[1..-1]
554
460
  end
555
461
 
556
- to_column_names = columns(to).map { |c| c.name }
557
- columns = index.columns.map {|c| rename[c] || c }.select do |column|
462
+ to_column_names = columns(to).map(&:name)
463
+ columns = index.columns.map { |c| rename[c] || c }.select do |column|
558
464
  to_column_names.include?(column)
559
465
  end
560
466
 
@@ -562,37 +468,27 @@ module ActiveRecord
562
468
  # index name can't be the same
563
469
  opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
564
470
  opts[:unique] = true if index.unique
471
+ opts[:where] = index.where if index.where
565
472
  add_index(to, columns, opts)
566
473
  end
567
474
  end
568
475
  end
569
476
 
570
- def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
571
- column_mappings = Hash[columns.map {|name| [name, name]}]
477
+ def copy_table_contents(from, to, columns, rename = {})
478
+ column_mappings = Hash[columns.map { |name| [name, name] }]
572
479
  rename.each { |a| column_mappings[a.last] = a.first }
573
- from_columns = columns(from).collect {|col| col.name}
574
- columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
575
- quoted_columns = columns.map { |col| quote_column_name(col) } * ','
576
-
577
- quoted_to = quote_table_name(to)
578
-
579
- raw_column_mappings = Hash[columns(from).map { |c| [c.name, c] }]
580
-
581
- exec_query("SELECT * FROM #{quote_table_name(from)}").each do |row|
582
- sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
583
-
584
- column_values = columns.map do |col|
585
- quote(row[column_mappings[col]], raw_column_mappings[col])
586
- end
587
-
588
- sql << column_values * ', '
589
- sql << ')'
590
- exec_query sql
591
- end
480
+ from_columns = columns(from).collect(&:name)
481
+ columns = columns.find_all { |col| from_columns.include?(column_mappings[col]) }
482
+ from_columns_to_copy = columns.map { |col| column_mappings[col] }
483
+ quoted_columns = columns.map { |col| quote_column_name(col) } * ","
484
+ quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
485
+
486
+ exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
487
+ SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
592
488
  end
593
489
 
594
490
  def sqlite_version
595
- @sqlite_version ||= SQLite3Adapter::Version.new(select_value('select sqlite_version(*)'))
491
+ @sqlite_version ||= SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
596
492
  end
597
493
 
598
494
  def translate_exception(exception, message)
@@ -602,11 +498,76 @@ module ActiveRecord
602
498
  # Older versions of SQLite return:
603
499
  # column *column_name* is not unique
604
500
  when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
605
- RecordNotUnique.new(message, exception)
501
+ RecordNotUnique.new(message)
502
+ when /.* may not be NULL/, /NOT NULL constraint failed: .*/
503
+ NotNullViolation.new(message)
504
+ when /FOREIGN KEY constraint failed/i
505
+ InvalidForeignKey.new(message)
606
506
  else
607
507
  super
608
508
  end
609
509
  end
510
+
511
+ COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
512
+
513
+ def table_structure_with_collation(table_name, basic_structure)
514
+ collation_hash = {}
515
+ sql = <<-SQL
516
+ SELECT sql FROM
517
+ (SELECT * FROM sqlite_master UNION ALL
518
+ SELECT * FROM sqlite_temp_master)
519
+ WHERE type = 'table' AND name = #{quote(table_name)}
520
+ SQL
521
+
522
+ # Result will have following sample string
523
+ # CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
524
+ # "password_digest" varchar COLLATE "NOCASE");
525
+ result = exec_query(sql, "SCHEMA").first
526
+
527
+ if result
528
+ # Splitting with left parentheses and discarding the first part will return all
529
+ # columns separated with comma(,).
530
+ columns_string = result["sql"].split("(", 2).last
531
+
532
+ columns_string.split(",").each do |column_string|
533
+ # This regex will match the column name and collation type and will save
534
+ # the value in $1 and $2 respectively.
535
+ collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
536
+ end
537
+
538
+ basic_structure.map! do |column|
539
+ column_name = column["name"]
540
+
541
+ if collation_hash.has_key? column_name
542
+ column["collation"] = collation_hash[column_name]
543
+ end
544
+
545
+ column
546
+ end
547
+ else
548
+ basic_structure.to_hash
549
+ end
550
+ end
551
+
552
+ def arel_visitor
553
+ Arel::Visitors::SQLite.new(self)
554
+ end
555
+
556
+ def configure_connection
557
+ execute("PRAGMA foreign_keys = ON", "SCHEMA")
558
+ end
559
+
560
+ class SQLite3Integer < Type::Integer # :nodoc:
561
+ private
562
+ def _limit
563
+ # INTEGER storage class can be stored 8 bytes value.
564
+ # See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
565
+ limit || 8
566
+ end
567
+ end
568
+
569
+ ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
610
570
  end
571
+ ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
611
572
  end
612
573
  end