activerecord 4.2.0 → 5.2.8.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 +5 -5
  2. data/CHANGELOG.md +640 -928
  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 +264 -247
  8. data/lib/active_record/association_relation.rb +24 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +87 -41
  11. data/lib/active_record/associations/association_scope.rb +106 -132
  12. data/lib/active_record/associations/belongs_to_association.rb +55 -36
  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 +14 -23
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
  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 +145 -266
  22. data/lib/active_record/associations/collection_proxy.rb +242 -138
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +35 -75
  25. data/lib/active_record/associations/has_many_through_association.rb +51 -69
  26. data/lib/active_record/associations/has_one_association.rb +39 -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 -81
  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 +134 -154
  32. data/lib/active_record/associations/preloader/association.rb +85 -116
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +83 -93
  35. data/lib/active_record/associations/singular_association.rb +27 -40
  36. data/lib/active_record/associations/through_association.rb +48 -23
  37. data/lib/active_record/associations.rb +1732 -1596
  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 +12 -5
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -125
  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 +62 -36
  47. data/lib/active_record/attribute_methods/write.rb +31 -46
  48. data/lib/active_record/attribute_methods.rb +170 -117
  49. data/lib/active_record/attributes.rb +201 -74
  50. data/lib/active_record/autosave_association.rb +118 -45
  51. data/lib/active_record/base.rb +60 -48
  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 +37 -13
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -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 +617 -212
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
  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 +42 -195
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
  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 -57
  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 +5 -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 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -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 +7 -9
  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 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
  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 +466 -280
  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 +439 -330
  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 -324
  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 +205 -202
  129. data/lib/active_record/counter_cache.rb +80 -37
  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 +136 -90
  133. data/lib/active_record/errors.rb +180 -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 +11 -6
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +193 -135
  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 +48 -0
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +92 -98
  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 +594 -267
  152. data/lib/active_record/model_schema.rb +292 -111
  153. data/lib/active_record/nested_attributes.rb +266 -214
  154. data/lib/active_record/no_touching.rb +8 -2
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +350 -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 +117 -35
  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 +160 -174
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +447 -288
  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 +259 -244
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +290 -253
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +91 -68
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
  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 +118 -92
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +446 -389
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +18 -16
  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 -339
  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 -19
  193. data/lib/active_record/scoping/default.rb +102 -84
  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 +136 -95
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
  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 +208 -123
  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 -41
  212. data/lib/active_record/type/date_time.rb +4 -38
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
  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 +30 -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 +41 -32
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +36 -21
  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 -6
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
  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.rb +7 -5
  243. metadata +77 -53
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  251. data/lib/active_record/attribute.rb +0 -149
  252. data/lib/active_record/attribute_set/builder.rb +0 -86
  253. data/lib/active_record/attribute_set.rb +0 -77
  254. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  255. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  256. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  257. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  258. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  259. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  260. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  261. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  262. data/lib/active_record/type/big_integer.rb +0 -13
  263. data/lib/active_record/type/binary.rb +0 -50
  264. data/lib/active_record/type/boolean.rb +0 -30
  265. data/lib/active_record/type/decimal.rb +0 -40
  266. data/lib/active_record/type/decorator.rb +0 -14
  267. data/lib/active_record/type/float.rb +0 -19
  268. data/lib/active_record/type/integer.rb +0 -55
  269. data/lib/active_record/type/mutable.rb +0 -16
  270. data/lib/active_record/type/numeric.rb +0 -36
  271. data/lib/active_record/type/string.rb +0 -36
  272. data/lib/active_record/type/time_value.rb +0 -38
  273. data/lib/active_record/type/value.rb +0 -101
  274. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -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,25 +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
- class SQLite3String < Type::String # :nodoc:
54
- def type_cast_for_database(value)
55
- if value.is_a?(::String) && value.encoding == Encoding::ASCII_8BIT
56
- value.encode(Encoding::UTF_8)
57
- else
58
- super
59
- end
60
- end
61
- end
62
-
63
50
  # The SQLite3 adapter works SQLite 3.6.16 or newer
64
51
  # with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
65
52
  #
@@ -67,11 +54,13 @@ module ActiveRecord
67
54
  #
68
55
  # * <tt>:database</tt> - Path to the database file.
69
56
  class SQLite3Adapter < AbstractAdapter
70
- ADAPTER_NAME = 'SQLite'.freeze
71
- include Savepoints
57
+ ADAPTER_NAME = "SQLite".freeze
58
+
59
+ include SQLite3::Quoting
60
+ include SQLite3::SchemaStatements
72
61
 
73
62
  NATIVE_DATABASE_TYPES = {
74
- primary_key: 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL',
63
+ primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
75
64
  string: { name: "varchar" },
76
65
  text: { name: "text" },
77
66
  integer: { name: "integer" },
@@ -81,71 +70,41 @@ module ActiveRecord
81
70
  time: { name: "time" },
82
71
  date: { name: "date" },
83
72
  binary: { name: "blob" },
84
- boolean: { name: "boolean" }
73
+ boolean: { name: "boolean" },
74
+ json: { name: "json" },
85
75
  }
86
76
 
87
- class Version
88
- include Comparable
89
-
90
- def initialize(version_string)
91
- @version = version_string.split('.').map { |v| v.to_i }
92
- end
93
-
94
- def <=>(version_string)
95
- @version <=> version_string.split('.').map { |v| v.to_i }
96
- end
97
- end
98
-
99
- class StatementPool < ConnectionAdapters::StatementPool
100
- def initialize(connection, max)
101
- super
102
- @cache = Hash.new { |h,pid| h[pid] = {} }
103
- end
104
-
105
- def each(&block); cache.each(&block); end
106
- def key?(key); cache.key?(key); end
107
- def [](key); cache[key]; end
108
- def length; cache.length; end
109
-
110
- def []=(sql, key)
111
- while @max <= cache.size
112
- dealloc(cache.shift.last[:stmt])
113
- end
114
- cache[sql] = key
115
- end
116
-
117
- def clear
118
- cache.each_value do |hash|
119
- dealloc hash[:stmt]
120
- end
121
- cache.clear
122
- 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
123
93
 
94
+ class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
124
95
  private
125
- def cache
126
- @cache[$$]
127
- end
128
-
129
- def dealloc(stmt)
130
- stmt.close unless stmt.closed?
131
- end
96
+ def dealloc(stmt)
97
+ stmt[:stmt].close unless stmt[:stmt].closed?
98
+ end
132
99
  end
133
100
 
134
101
  def initialize(connection, logger, connection_options, config)
135
- super(connection, logger)
136
-
137
- @active = nil
138
- @statements = StatementPool.new(@connection,
139
- self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
140
- @config = config
102
+ super(connection, logger, config)
141
103
 
142
- @visitor = Arel::Visitors::SQLite.new self
104
+ @active = true
105
+ @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
143
106
 
144
- if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
145
- @prepared_statements = true
146
- else
147
- @prepared_statements = false
148
- end
107
+ configure_connection
149
108
  end
150
109
 
151
110
  def supports_ddl_transactions?
@@ -157,34 +116,35 @@ module ActiveRecord
157
116
  end
158
117
 
159
118
  def supports_partial_index?
160
- sqlite_version >= '3.8.0'
119
+ sqlite_version >= "3.8.0"
161
120
  end
162
121
 
163
- # Returns true, since this connection adapter supports prepared statement
164
- # caching.
165
- def supports_statement_cache?
122
+ def requires_reloading?
166
123
  true
167
124
  end
168
125
 
169
- # Returns true, since this connection adapter supports migrations.
170
- def supports_migrations? #:nodoc:
171
- true
126
+ def supports_foreign_keys_in_create?
127
+ sqlite_version >= "3.6.19"
172
128
  end
173
129
 
174
- def supports_primary_key? #:nodoc:
130
+ def supports_views?
175
131
  true
176
132
  end
177
133
 
178
- def requires_reloading?
134
+ def supports_datetime_with_precision?
179
135
  true
180
136
  end
181
137
 
182
- def supports_views?
138
+ def supports_json?
183
139
  true
184
140
  end
185
141
 
142
+ def supports_multi_insert?
143
+ sqlite_version >= "3.7.11"
144
+ end
145
+
186
146
  def active?
187
- @active != false
147
+ @active
188
148
  end
189
149
 
190
150
  # Disconnects from the database if already connected. Otherwise, this
@@ -205,7 +165,7 @@ module ActiveRecord
205
165
  end
206
166
 
207
167
  # Returns 62. SQLite supports index names up to 64
208
- # characters. The rest is used by rails internally to perform
168
+ # characters. The rest is used by Rails internally to perform
209
169
  # temporary rename operations
210
170
  def allowed_index_name_length
211
171
  index_name_length - 2
@@ -224,45 +184,16 @@ module ActiveRecord
224
184
  true
225
185
  end
226
186
 
227
- # QUOTING ==================================================
228
-
229
- def _quote(value) # :nodoc:
230
- case value
231
- when Type::Binary::Data
232
- "x'#{value.hex}'"
233
- else
234
- super
235
- end
236
- end
237
-
238
- def _type_cast(value) # :nodoc:
239
- case value
240
- when BigDecimal
241
- value.to_f
242
- else
243
- super
244
- end
245
- end
246
-
247
- def quote_string(s) #:nodoc:
248
- @connection.class.quote(s)
249
- end
187
+ # REFERENTIAL INTEGRITY ====================================
250
188
 
251
- def quote_table_name_for_assignment(table, attr)
252
- quote_column_name(attr)
253
- end
189
+ def disable_referential_integrity # :nodoc:
190
+ old = query_value("PRAGMA foreign_keys")
254
191
 
255
- def quote_column_name(name) #:nodoc:
256
- %Q("#{name.to_s.gsub('"', '""')}")
257
- end
258
-
259
- # Quote date/time values for use in SQL input. Includes microseconds
260
- # if the value is a Time responding to usec.
261
- def quoted_date(value) #:nodoc:
262
- if value.respond_to?(:usec)
263
- "#{super}.#{sprintf("%06d", value.usec)}"
264
- else
265
- super
192
+ begin
193
+ execute("PRAGMA foreign_keys = OFF")
194
+ yield
195
+ ensure
196
+ execute("PRAGMA foreign_keys = #{old}")
266
197
  end
267
198
  end
268
199
 
@@ -272,54 +203,43 @@ module ActiveRecord
272
203
 
273
204
  def explain(arel, binds = [])
274
205
  sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
275
- ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
276
- end
277
-
278
- class ExplainPrettyPrinter
279
- # Pretty prints the result of a EXPLAIN QUERY PLAN in a way that resembles
280
- # the output of the SQLite shell:
281
- #
282
- # 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
283
- # 0|1|1|SCAN TABLE posts (~100000 rows)
284
- #
285
- def pp(result) # :nodoc:
286
- result.rows.map do |row|
287
- row.join('|')
288
- end.join("\n") + "\n"
289
- end
290
- end
291
-
292
- def exec_query(sql, name = nil, binds = [])
293
- type_casted_binds = binds.map { |col, val|
294
- [col, type_cast(val, col)]
295
- }
296
-
297
- log(sql, name, type_casted_binds) do
298
- # Don't cache statements if they are not prepared
299
- if without_prepared_statement?(binds)
300
- stmt = @connection.prepare(sql)
301
- begin
302
- 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)
303
234
  records = stmt.to_a
304
- ensure
305
- stmt.close
306
235
  end
307
- stmt = records
308
- else
309
- cache = @statements[sql] ||= {
310
- :stmt => @connection.prepare(sql)
311
- }
312
- stmt = cache[:stmt]
313
- cols = cache[:cols] ||= stmt.columns
314
- stmt.reset!
315
- stmt.bind_params type_casted_binds.map { |_, val| val }
316
- end
317
236
 
318
- ActiveRecord::Result.new(cols, stmt.to_a)
237
+ ActiveRecord::Result.new(cols, records)
238
+ end
319
239
  end
320
240
  end
321
241
 
322
- def exec_delete(sql, name = 'SQL', binds = [])
242
+ def exec_delete(sql, name = "SQL", binds = [])
323
243
  exec_query(sql, name, binds)
324
244
  @connection.changes
325
245
  end
@@ -330,111 +250,34 @@ module ActiveRecord
330
250
  end
331
251
 
332
252
  def execute(sql, name = nil) #:nodoc:
333
- log(sql, name) { @connection.execute(sql) }
334
- end
335
-
336
- def update_sql(sql, name = nil) #:nodoc:
337
- super
338
- @connection.changes
339
- end
340
-
341
- def delete_sql(sql, name = nil) #:nodoc:
342
- sql += " WHERE 1=1" unless sql =~ /WHERE/i
343
- super sql, name
344
- end
345
-
346
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
347
- super
348
- id_value || @connection.last_insert_row_id
349
- end
350
- alias :create :insert_sql
351
-
352
- def select_rows(sql, name = nil, binds = [])
353
- 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
354
258
  end
355
259
 
356
260
  def begin_db_transaction #:nodoc:
357
- log('begin transaction',nil) { @connection.transaction }
261
+ log("begin transaction", nil) { @connection.transaction }
358
262
  end
359
263
 
360
264
  def commit_db_transaction #:nodoc:
361
- log('commit transaction',nil) { @connection.commit }
265
+ log("commit transaction", nil) { @connection.commit }
362
266
  end
363
267
 
364
- def rollback_db_transaction #:nodoc:
365
- log('rollback transaction',nil) { @connection.rollback }
268
+ def exec_rollback_db_transaction #:nodoc:
269
+ log("rollback transaction", nil) { @connection.rollback }
366
270
  end
367
271
 
368
272
  # SCHEMA STATEMENTS ========================================
369
273
 
370
- def tables(name = nil, table_name = nil) #:nodoc:
371
- sql = <<-SQL
372
- SELECT name
373
- FROM sqlite_master
374
- WHERE (type = 'table' OR type = 'view') AND NOT name = 'sqlite_sequence'
375
- SQL
376
- sql << " AND name = #{quote_table_name(table_name)}" if table_name
377
-
378
- exec_query(sql, 'SCHEMA').map do |row|
379
- row['name']
380
- end
381
- end
382
-
383
- def table_exists?(table_name)
384
- table_name && tables(nil, table_name).any?
385
- end
386
-
387
- # Returns an array of +Column+ objects for the table specified by +table_name+.
388
- def columns(table_name) #:nodoc:
389
- table_structure(table_name).map do |field|
390
- case field["dflt_value"]
391
- when /^null$/i
392
- field["dflt_value"] = nil
393
- when /^'(.*)'$/m
394
- field["dflt_value"] = $1.gsub("''", "'")
395
- when /^"(.*)"$/m
396
- field["dflt_value"] = $1.gsub('""', '"')
397
- end
398
-
399
- sql_type = field['type']
400
- cast_type = lookup_cast_type(sql_type)
401
- new_column(field['name'], field['dflt_value'], cast_type, sql_type, field['notnull'].to_i == 0)
402
- end
403
- end
404
-
405
- # Returns an array of indexes for the given table.
406
- def indexes(table_name, name = nil) #:nodoc:
407
- exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').map do |row|
408
- sql = <<-SQL
409
- SELECT sql
410
- FROM sqlite_master
411
- WHERE name=#{quote(row['name'])} AND type='index'
412
- UNION ALL
413
- SELECT sql
414
- FROM sqlite_temp_master
415
- WHERE name=#{quote(row['name'])} AND type='index'
416
- SQL
417
- index_sql = exec_query(sql).first['sql']
418
- match = /\sWHERE\s+(.+)$/i.match(index_sql)
419
- where = match[1] if match
420
- IndexDefinition.new(
421
- table_name,
422
- row['name'],
423
- row['unique'] != 0,
424
- exec_query("PRAGMA index_info('#{row['name']}')", "SCHEMA").map { |col|
425
- col['name']
426
- }, nil, nil, where)
427
- end
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"] }
428
277
  end
429
278
 
430
- def primary_key(table_name) #:nodoc:
431
- column = table_structure(table_name).find { |field|
432
- field['pk'] == 1
433
- }
434
- column && column['name']
435
- end
436
-
437
- 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)
438
281
  exec_query "DROP INDEX #{quote_column_name(index_name)}"
439
282
  end
440
283
 
@@ -447,19 +290,18 @@ module ActiveRecord
447
290
  rename_table_indexes(table_name, new_name)
448
291
  end
449
292
 
450
- # See: http://www.sqlite.org/lang_altertable.html
451
- # SQLite has an additional restriction on the ALTER TABLE statement
452
- def valid_alter_table_type?(type)
453
- type.to_sym != :primary_key
293
+ def valid_alter_table_type?(type, options = {})
294
+ !invalid_alter_table_type?(type, options)
454
295
  end
296
+ deprecate :valid_alter_table_type?
455
297
 
456
298
  def add_column(table_name, column_name, type, options = {}) #:nodoc:
457
- if valid_alter_table_type?(type)
458
- super(table_name, column_name, type, options)
459
- else
299
+ if invalid_alter_table_type?(type, options)
460
300
  alter_table(table_name) do |definition|
461
301
  definition.column(column_name, type, options)
462
302
  end
303
+ else
304
+ super
463
305
  end
464
306
  end
465
307
 
@@ -469,13 +311,15 @@ module ActiveRecord
469
311
  end
470
312
  end
471
313
 
472
- 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
+
473
317
  alter_table(table_name) do |definition|
474
318
  definition[column_name].default = default
475
319
  end
476
320
  end
477
321
 
478
- def change_column_null(table_name, column_name, null, default = nil)
322
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
479
323
  unless null || default.nil?
480
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")
481
325
  end
@@ -486,81 +330,127 @@ module ActiveRecord
486
330
 
487
331
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
488
332
  alter_table(table_name) do |definition|
489
- include_default = options_include_default?(options)
490
333
  definition[column_name].instance_eval do
491
334
  self.type = type
492
335
  self.limit = options[:limit] if options.include?(:limit)
493
- self.default = options[:default] if include_default
336
+ self.default = options[:default] if options.include?(:default)
494
337
  self.null = options[:null] if options.include?(:null)
495
338
  self.precision = options[:precision] if options.include?(:precision)
496
- 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)
497
341
  end
498
342
  end
499
343
  end
500
344
 
501
345
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
502
346
  column = column_for(table_name, column_name)
503
- 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 })
504
348
  rename_column_indexes(table_name, column.name, new_column_name)
505
349
  end
506
350
 
507
- 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
508
388
 
509
- def initialize_type_map(m)
389
+ private
390
+ def initialize_type_map(m = type_map)
510
391
  super
511
- m.register_type(/binary/i, SQLite3Binary.new)
512
- register_class_with_limit m, %r(char)i, SQLite3String
392
+ register_class_with_limit m, %r(int)i, SQLite3Integer
513
393
  end
514
394
 
515
395
  def table_structure(table_name)
516
- 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")
517
397
  raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
518
- structure
398
+ table_structure_with_collation(table_name, structure)
519
399
  end
400
+ alias column_definitions table_structure
520
401
 
521
- def alter_table(table_name, options = {}) #:nodoc:
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]
406
+ end
407
+
408
+ def alter_table(table_name, options = {})
522
409
  altered_table_name = "a#{table_name}"
523
- caller = lambda {|definition| yield definition if block_given?}
410
+ caller = lambda { |definition| yield definition if block_given? }
524
411
 
525
412
  transaction do
526
413
  move_table(table_name, altered_table_name,
527
- options.merge(:temporary => true))
414
+ options.merge(temporary: true))
528
415
  move_table(altered_table_name, table_name, &caller)
529
416
  end
530
417
  end
531
418
 
532
- def move_table(from, to, options = {}, &block) #:nodoc:
419
+ def move_table(from, to, options = {}, &block)
533
420
  copy_table(from, to, options, &block)
534
421
  drop_table(from)
535
422
  end
536
423
 
537
- def copy_table(from, to, options = {}) #:nodoc:
424
+ def copy_table(from, to, options = {})
538
425
  from_primary_key = primary_key(from)
539
426
  options[:id] = false
540
427
  create_table(to, options) do |definition|
541
428
  @definition = definition
542
- @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
543
432
  columns(from).each do |column|
544
433
  column_name = options[:rename] ?
545
434
  (options[:rename][column.name] ||
546
435
  options[:rename][column.name.to_sym] ||
547
436
  column.name) : column.name
548
- next if column_name == from_primary_key
549
437
 
550
438
  @definition.column(column_name, column.type,
551
- :limit => column.limit, :default => column.default,
552
- :precision => column.precision, :scale => column.scale,
553
- :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
+ )
554
444
  end
555
445
  yield @definition if block_given?
556
446
  end
557
447
  copy_table_indexes(from, to, options[:rename] || {})
558
448
  copy_table_contents(from, to,
559
- @definition.columns.map {|column| column.name},
449
+ @definition.columns.map(&:name),
560
450
  options[:rename] || {})
561
451
  end
562
452
 
563
- def copy_table_indexes(from, to, rename = {}) #:nodoc:
453
+ def copy_table_indexes(from, to, rename = {})
564
454
  indexes(from).each do |index|
565
455
  name = index.name
566
456
  if to == "a#{from}"
@@ -569,8 +459,8 @@ module ActiveRecord
569
459
  name = name[1..-1]
570
460
  end
571
461
 
572
- to_column_names = columns(to).map { |c| c.name }
573
- 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|
574
464
  to_column_names.include?(column)
575
465
  end
576
466
 
@@ -578,37 +468,27 @@ module ActiveRecord
578
468
  # index name can't be the same
579
469
  opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
580
470
  opts[:unique] = true if index.unique
471
+ opts[:where] = index.where if index.where
581
472
  add_index(to, columns, opts)
582
473
  end
583
474
  end
584
475
  end
585
476
 
586
- def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
587
- 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] }]
588
479
  rename.each { |a| column_mappings[a.last] = a.first }
589
- from_columns = columns(from).collect {|col| col.name}
590
- columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
591
- quoted_columns = columns.map { |col| quote_column_name(col) } * ','
592
-
593
- quoted_to = quote_table_name(to)
594
-
595
- raw_column_mappings = Hash[columns(from).map { |c| [c.name, c] }]
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) } * ","
596
485
 
597
- exec_query("SELECT * FROM #{quote_table_name(from)}").each do |row|
598
- sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
599
-
600
- column_values = columns.map do |col|
601
- quote(row[column_mappings[col]], raw_column_mappings[col])
602
- end
603
-
604
- sql << column_values * ', '
605
- sql << ')'
606
- exec_query sql
607
- end
486
+ exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
487
+ SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
608
488
  end
609
489
 
610
490
  def sqlite_version
611
- @sqlite_version ||= SQLite3Adapter::Version.new(select_value('select sqlite_version(*)'))
491
+ @sqlite_version ||= SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
612
492
  end
613
493
 
614
494
  def translate_exception(exception, message)
@@ -618,11 +498,76 @@ module ActiveRecord
618
498
  # Older versions of SQLite return:
619
499
  # column *column_name* is not unique
620
500
  when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
621
- 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)
622
506
  else
623
507
  super
624
508
  end
625
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)
626
570
  end
571
+ ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
627
572
  end
628
573
  end