activerecord 3.2.22.5 → 5.2.8

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 (275) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +657 -621
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +41 -46
  5. data/examples/performance.rb +55 -42
  6. data/examples/simple.rb +6 -5
  7. data/lib/active_record/aggregations.rb +264 -236
  8. data/lib/active_record/association_relation.rb +40 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -42
  10. data/lib/active_record/associations/association.rb +127 -75
  11. data/lib/active_record/associations/association_scope.rb +126 -92
  12. data/lib/active_record/associations/belongs_to_association.rb +78 -27
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -4
  14. data/lib/active_record/associations/builder/association.rb +117 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +135 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -54
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +120 -42
  18. data/lib/active_record/associations/builder/has_many.rb +10 -64
  19. data/lib/active_record/associations/builder/has_one.rb +19 -51
  20. data/lib/active_record/associations/builder/singular_association.rb +28 -18
  21. data/lib/active_record/associations/collection_association.rb +226 -293
  22. data/lib/active_record/associations/collection_proxy.rb +1067 -69
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +83 -47
  25. data/lib/active_record/associations/has_many_through_association.rb +98 -65
  26. data/lib/active_record/associations/has_one_association.rb +57 -20
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +48 -126
  29. data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
  30. data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
  31. data/lib/active_record/associations/join_dependency.rb +212 -164
  32. data/lib/active_record/associations/preloader/association.rb +95 -89
  33. data/lib/active_record/associations/preloader/through_association.rb +84 -44
  34. data/lib/active_record/associations/preloader.rb +123 -111
  35. data/lib/active_record/associations/singular_association.rb +33 -24
  36. data/lib/active_record/associations/through_association.rb +60 -26
  37. data/lib/active_record/associations.rb +1759 -1506
  38. data/lib/active_record/attribute_assignment.rb +60 -193
  39. data/lib/active_record/attribute_decorators.rb +90 -0
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +55 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +113 -74
  42. data/lib/active_record/attribute_methods/primary_key.rb +106 -77
  43. data/lib/active_record/attribute_methods/query.rb +8 -5
  44. data/lib/active_record/attribute_methods/read.rb +63 -114
  45. data/lib/active_record/attribute_methods/serialization.rb +60 -90
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -43
  47. data/lib/active_record/attribute_methods/write.rb +43 -45
  48. data/lib/active_record/attribute_methods.rb +366 -149
  49. data/lib/active_record/attributes.rb +266 -0
  50. data/lib/active_record/autosave_association.rb +312 -225
  51. data/lib/active_record/base.rb +114 -505
  52. data/lib/active_record/callbacks.rb +145 -67
  53. data/lib/active_record/coders/json.rb +15 -0
  54. data/lib/active_record/coders/yaml_column.rb +32 -23
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +883 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +16 -2
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +350 -200
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -27
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +150 -65
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +477 -284
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1100 -310
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +450 -118
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +657 -446
  69. data/lib/active_record/connection_adapters/column.rb +50 -255
  70. data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
  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 +59 -210
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
  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 +92 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  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 +41 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +620 -1080
  117. data/lib/active_record/connection_adapters/schema_cache.rb +85 -36
  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 +545 -27
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +145 -0
  128. data/lib/active_record/core.rb +559 -0
  129. data/lib/active_record/counter_cache.rb +200 -105
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +107 -69
  132. data/lib/active_record/enum.rb +244 -0
  133. data/lib/active_record/errors.rb +245 -60
  134. data/lib/active_record/explain.rb +35 -71
  135. data/lib/active_record/explain_registry.rb +32 -0
  136. data/lib/active_record/explain_subscriber.rb +18 -9
  137. data/lib/active_record/fixture_set/file.rb +82 -0
  138. data/lib/active_record/fixtures.rb +418 -275
  139. data/lib/active_record/gem_version.rb +17 -0
  140. data/lib/active_record/inheritance.rb +209 -100
  141. data/lib/active_record/integration.rb +116 -21
  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 +9 -1
  145. data/lib/active_record/locking/optimistic.rb +107 -94
  146. data/lib/active_record/locking/pessimistic.rb +20 -8
  147. data/lib/active_record/log_subscriber.rb +99 -34
  148. data/lib/active_record/migration/command_recorder.rb +199 -64
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +17 -0
  151. data/lib/active_record/migration.rb +893 -296
  152. data/lib/active_record/model_schema.rb +328 -175
  153. data/lib/active_record/nested_attributes.rb +338 -242
  154. data/lib/active_record/no_touching.rb +58 -0
  155. data/lib/active_record/null_relation.rb +68 -0
  156. data/lib/active_record/persistence.rb +557 -170
  157. data/lib/active_record/query_cache.rb +14 -43
  158. data/lib/active_record/querying.rb +36 -24
  159. data/lib/active_record/railtie.rb +147 -52
  160. data/lib/active_record/railties/console_sandbox.rb +5 -4
  161. data/lib/active_record/railties/controller_runtime.rb +13 -6
  162. data/lib/active_record/railties/databases.rake +206 -488
  163. data/lib/active_record/readonly_attributes.rb +4 -6
  164. data/lib/active_record/reflection.rb +734 -228
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +249 -52
  167. data/lib/active_record/relation/calculations.rb +330 -284
  168. data/lib/active_record/relation/delegation.rb +135 -37
  169. data/lib/active_record/relation/finder_methods.rb +450 -287
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +193 -0
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  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 +19 -0
  179. data/lib/active_record/relation/predicate_builder.rb +132 -43
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +1037 -221
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +48 -151
  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 +451 -359
  187. data/lib/active_record/result.rb +129 -20
  188. data/lib/active_record/runtime_registry.rb +24 -0
  189. data/lib/active_record/sanitization.rb +164 -136
  190. data/lib/active_record/schema.rb +31 -19
  191. data/lib/active_record/schema_dumper.rb +154 -107
  192. data/lib/active_record/schema_migration.rb +56 -0
  193. data/lib/active_record/scoping/default.rb +108 -98
  194. data/lib/active_record/scoping/named.rb +125 -112
  195. data/lib/active_record/scoping.rb +77 -123
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +10 -6
  198. data/lib/active_record/statement_cache.rb +121 -0
  199. data/lib/active_record/store.rb +175 -16
  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 +337 -0
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
  206. data/lib/active_record/timestamp.rb +80 -41
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +240 -119
  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 +9 -0
  212. data/lib/active_record/type/date_time.rb +9 -0
  213. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  214. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  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 +71 -0
  218. data/lib/active_record/type/text.rb +11 -0
  219. data/lib/active_record/type/time.rb +21 -0
  220. data/lib/active_record/type/type_map.rb +62 -0
  221. data/lib/active_record/type/unsigned_integer.rb +17 -0
  222. data/lib/active_record/type.rb +79 -0
  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 +35 -18
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +68 -0
  230. data/lib/active_record/validations/uniqueness.rb +133 -75
  231. data/lib/active_record/validations.rb +53 -43
  232. data/lib/active_record/version.rb +7 -7
  233. data/lib/active_record.rb +89 -57
  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 +61 -8
  237. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  238. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
  239. data/lib/rails/generators/active_record/migration.rb +28 -8
  240. data/lib/rails/generators/active_record/model/model_generator.rb +23 -22
  241. data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
  243. data/lib/rails/generators/active_record.rb +10 -16
  244. metadata +141 -62
  245. data/examples/associations.png +0 -0
  246. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  247. data/lib/active_record/associations/join_helper.rb +0 -55
  248. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  249. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  250. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  251. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  252. data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
  253. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  254. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  255. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  256. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  257. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  258. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  259. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  260. data/lib/active_record/dynamic_finder_match.rb +0 -68
  261. data/lib/active_record/dynamic_scope_match.rb +0 -23
  262. data/lib/active_record/fixtures/file.rb +0 -65
  263. data/lib/active_record/identity_map.rb +0 -162
  264. data/lib/active_record/observer.rb +0 -121
  265. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  266. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  267. data/lib/active_record/session_store.rb +0 -360
  268. data/lib/active_record/test_case.rb +0 -73
  269. data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
  270. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  271. data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
  272. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  273. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  274. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  275. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,48 +1,178 @@
1
- require 'active_record/connection_adapters/sqlite_adapter'
1
+ # frozen_string_literal: true
2
2
 
3
- gem 'sqlite3', '~> 1.3.5'
4
- 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"
5
14
 
6
15
  module ActiveRecord
7
- class Base
8
- # sqlite3 adapter reuses sqlite_connection.
9
- def self.sqlite3_connection(config) # :nodoc:
16
+ module ConnectionHandling # :nodoc:
17
+ def sqlite3_connection(config)
10
18
  # Require database.
11
19
  unless config[:database]
12
20
  raise ArgumentError, "No database file specified. Missing argument: database"
13
21
  end
14
22
 
15
- # Allow database path relative to Rails.root, but only if
16
- # the database path is not the special path that tells
17
- # Sqlite to build a database only in memory.
18
- if defined?(Rails.root) && ':memory:' != config[:database]
19
- config[:database] = File.expand_path(config[:database], Rails.root)
20
- end
21
-
22
- unless 'sqlite3' == config[:adapter]
23
- raise ArgumentError, 'adapter name should be "sqlite3"'
23
+ # Allow database path relative to Rails.root, but only if the database
24
+ # path is not the special path that tells sqlite to build a database only
25
+ # in memory.
26
+ if ":memory:" != config[:database]
27
+ config[:database] = File.expand_path(config[:database], Rails.root) if defined?(Rails.root)
28
+ dirname = File.dirname(config[:database])
29
+ Dir.mkdir(dirname) unless File.directory?(dirname)
24
30
  end
25
31
 
26
32
  db = SQLite3::Database.new(
27
- config[:database],
28
- :results_as_hash => true
33
+ config[:database].to_s,
34
+ results_as_hash: true
29
35
  )
30
36
 
31
- db.busy_timeout(config[:timeout]) if config[:timeout]
37
+ db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]
32
38
 
33
- ConnectionAdapters::SQLite3Adapter.new(db, logger, config)
39
+ ConnectionAdapters::SQLite3Adapter.new(db, logger, nil, config)
40
+ rescue Errno::ENOENT => error
41
+ if error.message.include?("No such file or directory")
42
+ raise ActiveRecord::NoDatabaseError
43
+ else
44
+ raise
45
+ end
34
46
  end
35
47
  end
36
48
 
37
49
  module ConnectionAdapters #:nodoc:
38
- class SQLite3Adapter < SQLiteAdapter # :nodoc:
39
- def quote(value, column = nil)
40
- if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
41
- s = column.class.string_to_binary(value).unpack("H*")[0]
42
- "x'#{s}'"
43
- else
44
- super
45
- end
50
+ # The SQLite3 adapter works SQLite 3.6.16 or newer
51
+ # with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
52
+ #
53
+ # Options:
54
+ #
55
+ # * <tt>:database</tt> - Path to the database file.
56
+ class SQLite3Adapter < AbstractAdapter
57
+ ADAPTER_NAME = "SQLite".freeze
58
+
59
+ include SQLite3::Quoting
60
+ include SQLite3::SchemaStatements
61
+
62
+ NATIVE_DATABASE_TYPES = {
63
+ primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
64
+ string: { name: "varchar" },
65
+ text: { name: "text" },
66
+ integer: { name: "integer" },
67
+ float: { name: "float" },
68
+ decimal: { name: "decimal" },
69
+ datetime: { name: "datetime" },
70
+ time: { name: "time" },
71
+ date: { name: "date" },
72
+ binary: { name: "blob" },
73
+ boolean: { name: "boolean" },
74
+ json: { name: "json" },
75
+ }
76
+
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
93
+
94
+ class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
95
+ private
96
+ def dealloc(stmt)
97
+ stmt[:stmt].close unless stmt[:stmt].closed?
98
+ end
99
+ end
100
+
101
+ def initialize(connection, logger, connection_options, config)
102
+ super(connection, logger, config)
103
+
104
+ @active = true
105
+ @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
106
+
107
+ configure_connection
108
+ end
109
+
110
+ def supports_ddl_transactions?
111
+ true
112
+ end
113
+
114
+ def supports_savepoints?
115
+ true
116
+ end
117
+
118
+ def supports_partial_index?
119
+ sqlite_version >= "3.8.0"
120
+ end
121
+
122
+ def requires_reloading?
123
+ true
124
+ end
125
+
126
+ def supports_foreign_keys_in_create?
127
+ sqlite_version >= "3.6.19"
128
+ end
129
+
130
+ def supports_views?
131
+ true
132
+ end
133
+
134
+ def supports_datetime_with_precision?
135
+ true
136
+ end
137
+
138
+ def supports_json?
139
+ true
140
+ end
141
+
142
+ def supports_multi_insert?
143
+ sqlite_version >= "3.7.11"
144
+ end
145
+
146
+ def active?
147
+ @active
148
+ end
149
+
150
+ # Disconnects from the database if already connected. Otherwise, this
151
+ # method does nothing.
152
+ def disconnect!
153
+ super
154
+ @active = false
155
+ @connection.close rescue nil
156
+ end
157
+
158
+ # Clears the prepared statements cache.
159
+ def clear_cache!
160
+ @statements.clear
161
+ end
162
+
163
+ def supports_index_sort_order?
164
+ true
165
+ end
166
+
167
+ # Returns 62. SQLite supports index names up to 64
168
+ # characters. The rest is used by Rails internally to perform
169
+ # temporary rename operations
170
+ def allowed_index_name_length
171
+ index_name_length - 2
172
+ end
173
+
174
+ def native_database_types #:nodoc:
175
+ NATIVE_DATABASE_TYPES
46
176
  end
47
177
 
48
178
  # Returns the current database encoding format as a string, eg: 'UTF-8'
@@ -50,6 +180,394 @@ module ActiveRecord
50
180
  @connection.encoding.to_s
51
181
  end
52
182
 
183
+ def supports_explain?
184
+ true
185
+ end
186
+
187
+ # REFERENTIAL INTEGRITY ====================================
188
+
189
+ def disable_referential_integrity # :nodoc:
190
+ old = query_value("PRAGMA foreign_keys")
191
+
192
+ begin
193
+ execute("PRAGMA foreign_keys = OFF")
194
+ yield
195
+ ensure
196
+ execute("PRAGMA foreign_keys = #{old}")
197
+ end
198
+ end
199
+
200
+ #--
201
+ # DATABASE STATEMENTS ======================================
202
+ #++
203
+
204
+ def explain(arel, binds = [])
205
+ sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
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)
234
+ records = stmt.to_a
235
+ end
236
+
237
+ ActiveRecord::Result.new(cols, records)
238
+ end
239
+ end
240
+ end
241
+
242
+ def exec_delete(sql, name = "SQL", binds = [])
243
+ exec_query(sql, name, binds)
244
+ @connection.changes
245
+ end
246
+ alias :exec_update :exec_delete
247
+
248
+ def last_inserted_id(result)
249
+ @connection.last_insert_row_id
250
+ end
251
+
252
+ def execute(sql, name = nil) #:nodoc:
253
+ log(sql, name) do
254
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
255
+ @connection.execute(sql)
256
+ end
257
+ end
258
+ end
259
+
260
+ def begin_db_transaction #:nodoc:
261
+ log("begin transaction", nil) { @connection.transaction }
262
+ end
263
+
264
+ def commit_db_transaction #:nodoc:
265
+ log("commit transaction", nil) { @connection.commit }
266
+ end
267
+
268
+ def exec_rollback_db_transaction #:nodoc:
269
+ log("rollback transaction", nil) { @connection.rollback }
270
+ end
271
+
272
+ # SCHEMA STATEMENTS ========================================
273
+
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"] }
277
+ end
278
+
279
+ def remove_index(table_name, options = {}) #:nodoc:
280
+ index_name = index_name_for_remove(table_name, options)
281
+ exec_query "DROP INDEX #{quote_column_name(index_name)}"
282
+ end
283
+
284
+ # Renames a table.
285
+ #
286
+ # Example:
287
+ # rename_table('octopuses', 'octopi')
288
+ def rename_table(table_name, new_name)
289
+ exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
290
+ rename_table_indexes(table_name, new_name)
291
+ end
292
+
293
+ def valid_alter_table_type?(type, options = {})
294
+ !invalid_alter_table_type?(type, options)
295
+ end
296
+ deprecate :valid_alter_table_type?
297
+
298
+ def add_column(table_name, column_name, type, options = {}) #:nodoc:
299
+ if invalid_alter_table_type?(type, options)
300
+ alter_table(table_name) do |definition|
301
+ definition.column(column_name, type, options)
302
+ end
303
+ else
304
+ super
305
+ end
306
+ end
307
+
308
+ def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
309
+ alter_table(table_name) do |definition|
310
+ definition.remove_column column_name
311
+ end
312
+ end
313
+
314
+ def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
315
+ default = extract_new_default_value(default_or_changes)
316
+
317
+ alter_table(table_name) do |definition|
318
+ definition[column_name].default = default
319
+ end
320
+ end
321
+
322
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
323
+ unless null || default.nil?
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")
325
+ end
326
+ alter_table(table_name) do |definition|
327
+ definition[column_name].null = null
328
+ end
329
+ end
330
+
331
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
332
+ alter_table(table_name) do |definition|
333
+ definition[column_name].instance_eval do
334
+ self.type = type
335
+ self.limit = options[:limit] if options.include?(:limit)
336
+ self.default = options[:default] if options.include?(:default)
337
+ self.null = options[:null] if options.include?(:null)
338
+ self.precision = options[:precision] if options.include?(:precision)
339
+ self.scale = options[:scale] if options.include?(:scale)
340
+ self.collation = options[:collation] if options.include?(:collation)
341
+ end
342
+ end
343
+ end
344
+
345
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
346
+ column = column_for(table_name, column_name)
347
+ alter_table(table_name, rename: { column.name => new_column_name.to_s })
348
+ rename_column_indexes(table_name, column.name, new_column_name)
349
+ end
350
+
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
388
+
389
+ private
390
+ def initialize_type_map(m = type_map)
391
+ super
392
+ register_class_with_limit m, %r(int)i, SQLite3Integer
393
+ end
394
+
395
+ def table_structure(table_name)
396
+ structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
397
+ raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
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]
406
+ end
407
+
408
+ def alter_table(table_name, options = {})
409
+ altered_table_name = "a#{table_name}"
410
+ caller = lambda { |definition| yield definition if block_given? }
411
+
412
+ transaction do
413
+ move_table(table_name, altered_table_name,
414
+ options.merge(temporary: true))
415
+ move_table(altered_table_name, table_name, &caller)
416
+ end
417
+ end
418
+
419
+ def move_table(from, to, options = {}, &block)
420
+ copy_table(from, to, options, &block)
421
+ drop_table(from)
422
+ end
423
+
424
+ def copy_table(from, to, options = {})
425
+ from_primary_key = primary_key(from)
426
+ options[:id] = false
427
+ create_table(to, options) do |definition|
428
+ @definition = definition
429
+ if from_primary_key.is_a?(Array)
430
+ @definition.primary_keys from_primary_key
431
+ end
432
+ columns(from).each do |column|
433
+ column_name = options[:rename] ?
434
+ (options[:rename][column.name] ||
435
+ options[:rename][column.name.to_sym] ||
436
+ column.name) : column.name
437
+
438
+ @definition.column(column_name, column.type,
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
+ )
444
+ end
445
+ yield @definition if block_given?
446
+ end
447
+ copy_table_indexes(from, to, options[:rename] || {})
448
+ copy_table_contents(from, to,
449
+ @definition.columns.map(&:name),
450
+ options[:rename] || {})
451
+ end
452
+
453
+ def copy_table_indexes(from, to, rename = {})
454
+ indexes(from).each do |index|
455
+ name = index.name
456
+ if to == "a#{from}"
457
+ name = "t#{name}"
458
+ elsif from == "a#{to}"
459
+ name = name[1..-1]
460
+ end
461
+
462
+ to_column_names = columns(to).map(&:name)
463
+ columns = index.columns.map { |c| rename[c] || c }.select do |column|
464
+ to_column_names.include?(column)
465
+ end
466
+
467
+ unless columns.empty?
468
+ # index name can't be the same
469
+ opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
470
+ opts[:unique] = true if index.unique
471
+ opts[:where] = index.where if index.where
472
+ add_index(to, columns, opts)
473
+ end
474
+ end
475
+ end
476
+
477
+ def copy_table_contents(from, to, columns, rename = {})
478
+ column_mappings = Hash[columns.map { |name| [name, name] }]
479
+ rename.each { |a| column_mappings[a.last] = a.first }
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)}")
488
+ end
489
+
490
+ def sqlite_version
491
+ @sqlite_version ||= SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
492
+ end
493
+
494
+ def translate_exception(exception, message)
495
+ case exception.message
496
+ # SQLite 3.8.2 returns a newly formatted error message:
497
+ # UNIQUE constraint failed: *table_name*.*column_name*
498
+ # Older versions of SQLite return:
499
+ # column *column_name* is not unique
500
+ when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
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)
506
+ else
507
+ super
508
+ end
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)
53
570
  end
571
+ ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
54
572
  end
55
573
  end
@@ -1,40 +1,61 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
- class StatementPool
5
+ class StatementPool # :nodoc:
4
6
  include Enumerable
5
7
 
6
- def initialize(connection, max = 1000)
7
- @connection = connection
8
- @max = max
8
+ DEFAULT_STATEMENT_LIMIT = 1000
9
+
10
+ def initialize(statement_limit = nil)
11
+ @cache = Hash.new { |h, pid| h[pid] = {} }
12
+ @statement_limit = statement_limit || DEFAULT_STATEMENT_LIMIT
9
13
  end
10
14
 
11
- def each
12
- raise NotImplementedError
15
+ def each(&block)
16
+ cache.each(&block)
13
17
  end
14
18
 
15
19
  def key?(key)
16
- raise NotImplementedError
20
+ cache.key?(key)
17
21
  end
18
22
 
19
23
  def [](key)
20
- raise NotImplementedError
24
+ cache[key]
21
25
  end
22
26
 
23
27
  def length
24
- raise NotImplementedError
28
+ cache.length
25
29
  end
26
30
 
27
- def []=(sql, key)
28
- raise NotImplementedError
31
+ def []=(sql, stmt)
32
+ while @statement_limit <= cache.size
33
+ dealloc(cache.shift.last)
34
+ end
35
+ cache[sql] = stmt
29
36
  end
30
37
 
31
38
  def clear
32
- raise NotImplementedError
39
+ cache.each_value do |stmt|
40
+ dealloc stmt
41
+ end
42
+ cache.clear
33
43
  end
34
44
 
35
45
  def delete(key)
36
- raise NotImplementedError
46
+ dealloc cache[key]
47
+ cache.delete(key)
37
48
  end
49
+
50
+ private
51
+
52
+ def cache
53
+ @cache[Process.pid]
54
+ end
55
+
56
+ def dealloc(stmt)
57
+ raise NotImplementedError
58
+ end
38
59
  end
39
60
  end
40
61
  end