activerecord 5.2.6 → 6.1.3.2

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 (316) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1038 -571
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +7 -5
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +13 -12
  7. data/lib/active_record/aggregations.rb +9 -8
  8. data/lib/active_record/association_relation.rb +30 -10
  9. data/lib/active_record/associations.rb +137 -25
  10. data/lib/active_record/associations/alias_tracker.rb +19 -16
  11. data/lib/active_record/associations/association.rb +95 -42
  12. data/lib/active_record/associations/association_scope.rb +23 -21
  13. data/lib/active_record/associations/belongs_to_association.rb +54 -46
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -6
  15. data/lib/active_record/associations/builder/association.rb +45 -22
  16. data/lib/active_record/associations/builder/belongs_to.rb +29 -59
  17. data/lib/active_record/associations/builder/collection_association.rb +8 -17
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
  19. data/lib/active_record/associations/builder/has_many.rb +8 -2
  20. data/lib/active_record/associations/builder/has_one.rb +33 -2
  21. data/lib/active_record/associations/builder/singular_association.rb +3 -1
  22. data/lib/active_record/associations/collection_association.rb +31 -29
  23. data/lib/active_record/associations/collection_proxy.rb +25 -21
  24. data/lib/active_record/associations/foreign_association.rb +20 -0
  25. data/lib/active_record/associations/has_many_association.rb +26 -13
  26. data/lib/active_record/associations/has_many_through_association.rb +24 -18
  27. data/lib/active_record/associations/has_one_association.rb +43 -31
  28. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  29. data/lib/active_record/associations/join_dependency.rb +91 -60
  30. data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
  31. data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
  32. data/lib/active_record/associations/preloader.rb +47 -34
  33. data/lib/active_record/associations/preloader/association.rb +71 -43
  34. data/lib/active_record/associations/preloader/through_association.rb +49 -40
  35. data/lib/active_record/associations/singular_association.rb +3 -17
  36. data/lib/active_record/associations/through_association.rb +1 -1
  37. data/lib/active_record/attribute_assignment.rb +17 -19
  38. data/lib/active_record/attribute_methods.rb +81 -143
  39. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
  40. data/lib/active_record/attribute_methods/dirty.rb +101 -40
  41. data/lib/active_record/attribute_methods/primary_key.rb +20 -25
  42. data/lib/active_record/attribute_methods/query.rb +4 -8
  43. data/lib/active_record/attribute_methods/read.rb +14 -56
  44. data/lib/active_record/attribute_methods/serialization.rb +12 -7
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  46. data/lib/active_record/attribute_methods/write.rb +18 -34
  47. data/lib/active_record/attributes.rb +46 -9
  48. data/lib/active_record/autosave_association.rb +57 -42
  49. data/lib/active_record/base.rb +4 -17
  50. data/lib/active_record/callbacks.rb +158 -43
  51. data/lib/active_record/coders/yaml_column.rb +1 -2
  52. data/lib/active_record/connection_adapters.rb +50 -0
  53. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +272 -130
  54. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
  55. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
  56. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -14
  57. data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
  58. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  59. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
  60. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +207 -90
  61. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +385 -144
  63. data/lib/active_record/connection_adapters/abstract/transaction.rb +155 -68
  64. data/lib/active_record/connection_adapters/abstract_adapter.rb +228 -98
  65. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
  66. data/lib/active_record/connection_adapters/column.rb +30 -12
  67. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  68. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  69. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  70. data/lib/active_record/connection_adapters/mysql/database_statements.rb +86 -32
  71. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +59 -7
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +18 -7
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +139 -19
  77. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
  79. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  80. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +38 -54
  83. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  87. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  91. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  97. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  99. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
  100. data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
  101. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  102. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
  103. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  104. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  105. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
  106. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
  107. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  108. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -120
  109. data/lib/active_record/connection_adapters/schema_cache.rb +127 -21
  110. data/lib/active_record/connection_adapters/sql_type_metadata.rb +19 -6
  111. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
  112. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  113. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  114. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +174 -186
  116. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  117. data/lib/active_record/connection_handling.rb +293 -33
  118. data/lib/active_record/core.rb +323 -97
  119. data/lib/active_record/counter_cache.rb +8 -30
  120. data/lib/active_record/database_configurations.rb +272 -0
  121. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  122. data/lib/active_record/database_configurations/database_config.rb +80 -0
  123. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  124. data/lib/active_record/database_configurations/url_config.rb +53 -0
  125. data/lib/active_record/delegated_type.rb +209 -0
  126. data/lib/active_record/destroy_association_async_job.rb +36 -0
  127. data/lib/active_record/dynamic_matchers.rb +3 -4
  128. data/lib/active_record/enum.rb +111 -37
  129. data/lib/active_record/errors.rb +62 -19
  130. data/lib/active_record/explain.rb +10 -6
  131. data/lib/active_record/explain_subscriber.rb +1 -1
  132. data/lib/active_record/fixture_set/file.rb +10 -17
  133. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  134. data/lib/active_record/fixture_set/render_context.rb +17 -0
  135. data/lib/active_record/fixture_set/table_row.rb +152 -0
  136. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  137. data/lib/active_record/fixtures.rb +200 -481
  138. data/lib/active_record/gem_version.rb +4 -4
  139. data/lib/active_record/inheritance.rb +53 -24
  140. data/lib/active_record/insert_all.rb +208 -0
  141. data/lib/active_record/integration.rb +67 -17
  142. data/lib/active_record/internal_metadata.rb +26 -9
  143. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  144. data/lib/active_record/locking/optimistic.rb +37 -23
  145. data/lib/active_record/locking/pessimistic.rb +9 -5
  146. data/lib/active_record/log_subscriber.rb +35 -35
  147. data/lib/active_record/middleware/database_selector.rb +77 -0
  148. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  149. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  150. data/lib/active_record/migration.rb +206 -157
  151. data/lib/active_record/migration/command_recorder.rb +96 -44
  152. data/lib/active_record/migration/compatibility.rb +142 -64
  153. data/lib/active_record/migration/join_table.rb +0 -1
  154. data/lib/active_record/model_schema.rb +148 -22
  155. data/lib/active_record/nested_attributes.rb +4 -7
  156. data/lib/active_record/no_touching.rb +8 -1
  157. data/lib/active_record/null_relation.rb +0 -1
  158. data/lib/active_record/persistence.rb +267 -59
  159. data/lib/active_record/query_cache.rb +21 -4
  160. data/lib/active_record/querying.rb +40 -23
  161. data/lib/active_record/railtie.rb +115 -58
  162. data/lib/active_record/railties/console_sandbox.rb +2 -4
  163. data/lib/active_record/railties/controller_runtime.rb +30 -35
  164. data/lib/active_record/railties/databases.rake +408 -78
  165. data/lib/active_record/readonly_attributes.rb +4 -0
  166. data/lib/active_record/reflection.rb +109 -93
  167. data/lib/active_record/relation.rb +374 -104
  168. data/lib/active_record/relation/batches.rb +44 -35
  169. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  170. data/lib/active_record/relation/calculations.rb +153 -90
  171. data/lib/active_record/relation/delegation.rb +35 -50
  172. data/lib/active_record/relation/finder_methods.rb +64 -39
  173. data/lib/active_record/relation/from_clause.rb +5 -1
  174. data/lib/active_record/relation/merger.rb +32 -40
  175. data/lib/active_record/relation/predicate_builder.rb +62 -45
  176. data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
  177. data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
  178. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  179. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
  180. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  181. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  182. data/lib/active_record/relation/query_attribute.rb +13 -8
  183. data/lib/active_record/relation/query_methods.rb +475 -186
  184. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  185. data/lib/active_record/relation/spawn_methods.rb +9 -9
  186. data/lib/active_record/relation/where_clause.rb +111 -61
  187. data/lib/active_record/result.rb +64 -38
  188. data/lib/active_record/runtime_registry.rb +2 -2
  189. data/lib/active_record/sanitization.rb +22 -41
  190. data/lib/active_record/schema.rb +2 -11
  191. data/lib/active_record/schema_dumper.rb +54 -9
  192. data/lib/active_record/schema_migration.rb +7 -9
  193. data/lib/active_record/scoping.rb +8 -9
  194. data/lib/active_record/scoping/default.rb +4 -6
  195. data/lib/active_record/scoping/named.rb +17 -24
  196. data/lib/active_record/secure_token.rb +16 -8
  197. data/lib/active_record/serialization.rb +5 -3
  198. data/lib/active_record/signed_id.rb +116 -0
  199. data/lib/active_record/statement_cache.rb +49 -6
  200. data/lib/active_record/store.rb +88 -9
  201. data/lib/active_record/suppressor.rb +2 -2
  202. data/lib/active_record/table_metadata.rb +42 -43
  203. data/lib/active_record/tasks/database_tasks.rb +277 -81
  204. data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
  205. data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
  206. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
  207. data/lib/active_record/test_databases.rb +24 -0
  208. data/lib/active_record/test_fixtures.rb +246 -0
  209. data/lib/active_record/timestamp.rb +43 -32
  210. data/lib/active_record/touch_later.rb +23 -22
  211. data/lib/active_record/transactions.rb +62 -118
  212. data/lib/active_record/translation.rb +1 -1
  213. data/lib/active_record/type.rb +10 -5
  214. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  215. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  216. data/lib/active_record/type/serialized.rb +6 -3
  217. data/lib/active_record/type/time.rb +10 -0
  218. data/lib/active_record/type/type_map.rb +0 -1
  219. data/lib/active_record/type/unsigned_integer.rb +0 -1
  220. data/lib/active_record/type_caster/connection.rb +15 -15
  221. data/lib/active_record/type_caster/map.rb +8 -8
  222. data/lib/active_record/validations.rb +4 -3
  223. data/lib/active_record/validations/associated.rb +1 -2
  224. data/lib/active_record/validations/numericality.rb +35 -0
  225. data/lib/active_record/validations/uniqueness.rb +38 -30
  226. data/lib/arel.rb +54 -0
  227. data/lib/arel/alias_predication.rb +9 -0
  228. data/lib/arel/attributes/attribute.rb +41 -0
  229. data/lib/arel/collectors/bind.rb +29 -0
  230. data/lib/arel/collectors/composite.rb +39 -0
  231. data/lib/arel/collectors/plain_string.rb +20 -0
  232. data/lib/arel/collectors/sql_string.rb +27 -0
  233. data/lib/arel/collectors/substitute_binds.rb +35 -0
  234. data/lib/arel/crud.rb +42 -0
  235. data/lib/arel/delete_manager.rb +18 -0
  236. data/lib/arel/errors.rb +9 -0
  237. data/lib/arel/expressions.rb +29 -0
  238. data/lib/arel/factory_methods.rb +49 -0
  239. data/lib/arel/insert_manager.rb +49 -0
  240. data/lib/arel/math.rb +45 -0
  241. data/lib/arel/nodes.rb +70 -0
  242. data/lib/arel/nodes/and.rb +32 -0
  243. data/lib/arel/nodes/ascending.rb +23 -0
  244. data/lib/arel/nodes/binary.rb +126 -0
  245. data/lib/arel/nodes/bind_param.rb +44 -0
  246. data/lib/arel/nodes/case.rb +55 -0
  247. data/lib/arel/nodes/casted.rb +62 -0
  248. data/lib/arel/nodes/comment.rb +29 -0
  249. data/lib/arel/nodes/count.rb +12 -0
  250. data/lib/arel/nodes/delete_statement.rb +45 -0
  251. data/lib/arel/nodes/descending.rb +23 -0
  252. data/lib/arel/nodes/equality.rb +15 -0
  253. data/lib/arel/nodes/extract.rb +24 -0
  254. data/lib/arel/nodes/false.rb +16 -0
  255. data/lib/arel/nodes/full_outer_join.rb +8 -0
  256. data/lib/arel/nodes/function.rb +44 -0
  257. data/lib/arel/nodes/grouping.rb +11 -0
  258. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  259. data/lib/arel/nodes/in.rb +15 -0
  260. data/lib/arel/nodes/infix_operation.rb +92 -0
  261. data/lib/arel/nodes/inner_join.rb +8 -0
  262. data/lib/arel/nodes/insert_statement.rb +37 -0
  263. data/lib/arel/nodes/join_source.rb +20 -0
  264. data/lib/arel/nodes/matches.rb +18 -0
  265. data/lib/arel/nodes/named_function.rb +23 -0
  266. data/lib/arel/nodes/node.rb +51 -0
  267. data/lib/arel/nodes/node_expression.rb +13 -0
  268. data/lib/arel/nodes/ordering.rb +27 -0
  269. data/lib/arel/nodes/outer_join.rb +8 -0
  270. data/lib/arel/nodes/over.rb +15 -0
  271. data/lib/arel/nodes/regexp.rb +16 -0
  272. data/lib/arel/nodes/right_outer_join.rb +8 -0
  273. data/lib/arel/nodes/select_core.rb +67 -0
  274. data/lib/arel/nodes/select_statement.rb +41 -0
  275. data/lib/arel/nodes/sql_literal.rb +19 -0
  276. data/lib/arel/nodes/string_join.rb +11 -0
  277. data/lib/arel/nodes/table_alias.rb +31 -0
  278. data/lib/arel/nodes/terminal.rb +16 -0
  279. data/lib/arel/nodes/true.rb +16 -0
  280. data/lib/arel/nodes/unary.rb +44 -0
  281. data/lib/arel/nodes/unary_operation.rb +20 -0
  282. data/lib/arel/nodes/unqualified_column.rb +22 -0
  283. data/lib/arel/nodes/update_statement.rb +41 -0
  284. data/lib/arel/nodes/values_list.rb +9 -0
  285. data/lib/arel/nodes/window.rb +126 -0
  286. data/lib/arel/nodes/with.rb +11 -0
  287. data/lib/arel/order_predications.rb +13 -0
  288. data/lib/arel/predications.rb +250 -0
  289. data/lib/arel/select_manager.rb +270 -0
  290. data/lib/arel/table.rb +118 -0
  291. data/lib/arel/tree_manager.rb +72 -0
  292. data/lib/arel/update_manager.rb +34 -0
  293. data/lib/arel/visitors.rb +13 -0
  294. data/lib/arel/visitors/dot.rb +308 -0
  295. data/lib/arel/visitors/mysql.rb +93 -0
  296. data/lib/arel/visitors/postgresql.rb +120 -0
  297. data/lib/arel/visitors/sqlite.rb +38 -0
  298. data/lib/arel/visitors/to_sql.rb +899 -0
  299. data/lib/arel/visitors/visitor.rb +45 -0
  300. data/lib/arel/window_predications.rb +9 -0
  301. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  302. data/lib/rails/generators/active_record/migration.rb +19 -2
  303. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
  304. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
  305. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
  306. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  307. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  308. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  309. metadata +119 -34
  310. data/lib/active_record/attribute_decorators.rb +0 -90
  311. data/lib/active_record/collection_cache_key.rb +0 -53
  312. data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
  313. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
  314. data/lib/active_record/define_callbacks.rb +0 -22
  315. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
  316. data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -1,8 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/file/atomic"
4
+
3
5
  module ActiveRecord
4
6
  module ConnectionAdapters
5
7
  class SchemaCache
8
+ def self.load_from(filename)
9
+ return unless File.file?(filename)
10
+
11
+ read(filename) do |file|
12
+ filename.include?(".dump") ? Marshal.load(file) : YAML.load(file)
13
+ end
14
+ end
15
+
16
+ def self.read(filename, &block)
17
+ if File.extname(filename) == ".gz"
18
+ Zlib::GzipReader.open(filename) { |gz|
19
+ yield gz.read
20
+ }
21
+ else
22
+ yield File.read(filename)
23
+ end
24
+ end
25
+ private_class_method :read
26
+
6
27
  attr_reader :version
7
28
  attr_accessor :connection
8
29
 
@@ -13,6 +34,7 @@ module ActiveRecord
13
34
  @columns_hash = {}
14
35
  @primary_keys = {}
15
36
  @data_sources = {}
37
+ @indexes = {}
16
38
  end
17
39
 
18
40
  def initialize_dup(other)
@@ -21,26 +43,37 @@ module ActiveRecord
21
43
  @columns_hash = @columns_hash.dup
22
44
  @primary_keys = @primary_keys.dup
23
45
  @data_sources = @data_sources.dup
46
+ @indexes = @indexes.dup
24
47
  end
25
48
 
26
49
  def encode_with(coder)
27
- coder["columns"] = @columns
28
- coder["columns_hash"] = @columns_hash
29
- coder["primary_keys"] = @primary_keys
30
- coder["data_sources"] = @data_sources
31
- coder["version"] = connection.migration_context.current_version
50
+ reset_version!
51
+
52
+ coder["columns"] = @columns
53
+ coder["primary_keys"] = @primary_keys
54
+ coder["data_sources"] = @data_sources
55
+ coder["indexes"] = @indexes
56
+ coder["version"] = @version
57
+ coder["database_version"] = database_version
32
58
  end
33
59
 
34
60
  def init_with(coder)
35
- @columns = coder["columns"]
36
- @columns_hash = coder["columns_hash"]
37
- @primary_keys = coder["primary_keys"]
38
- @data_sources = coder["data_sources"]
39
- @version = coder["version"]
61
+ @columns = coder["columns"]
62
+ @primary_keys = coder["primary_keys"]
63
+ @data_sources = coder["data_sources"]
64
+ @indexes = coder["indexes"] || {}
65
+ @version = coder["version"]
66
+ @database_version = coder["database_version"]
67
+
68
+ derive_columns_hash_and_deduplicate_values
40
69
  end
41
70
 
42
71
  def primary_keys(table_name)
43
- @primary_keys[table_name] ||= data_source_exists?(table_name) ? connection.primary_key(table_name) : nil
72
+ @primary_keys.fetch(table_name) do
73
+ if data_source_exists?(table_name)
74
+ @primary_keys[deep_deduplicate(table_name)] = deep_deduplicate(connection.primary_key(table_name))
75
+ end
76
+ end
44
77
  end
45
78
 
46
79
  # A cached lookup for table existence.
@@ -48,7 +81,7 @@ module ActiveRecord
48
81
  prepare_data_sources if @data_sources.empty?
49
82
  return @data_sources[name] if @data_sources.key? name
50
83
 
51
- @data_sources[name] = connection.data_source_exists?(name)
84
+ @data_sources[deep_deduplicate(name)] = connection.data_source_exists?(name)
52
85
  end
53
86
 
54
87
  # Add internal cache for table with +table_name+.
@@ -57,6 +90,7 @@ module ActiveRecord
57
90
  primary_keys(table_name)
58
91
  columns(table_name)
59
92
  columns_hash(table_name)
93
+ indexes(table_name)
60
94
  end
61
95
  end
62
96
 
@@ -66,15 +100,32 @@ module ActiveRecord
66
100
 
67
101
  # Get the columns for a table
68
102
  def columns(table_name)
69
- @columns[table_name] ||= connection.columns(table_name)
103
+ @columns.fetch(table_name) do
104
+ @columns[deep_deduplicate(table_name)] = deep_deduplicate(connection.columns(table_name))
105
+ end
70
106
  end
71
107
 
72
108
  # Get the columns for a table as a hash, key is the column name
73
109
  # value is the column object.
74
110
  def columns_hash(table_name)
75
- @columns_hash[table_name] ||= Hash[columns(table_name).map { |col|
76
- [col.name, col]
77
- }]
111
+ @columns_hash.fetch(table_name) do
112
+ @columns_hash[deep_deduplicate(table_name)] = columns(table_name).index_by(&:name).freeze
113
+ end
114
+ end
115
+
116
+ # Checks whether the columns hash is already cached for a table.
117
+ def columns_hash?(table_name)
118
+ @columns_hash.key?(table_name)
119
+ end
120
+
121
+ def indexes(table_name)
122
+ @indexes.fetch(table_name) do
123
+ @indexes[deep_deduplicate(table_name)] = deep_deduplicate(connection.indexes(table_name))
124
+ end
125
+ end
126
+
127
+ def database_version # :nodoc:
128
+ @database_version ||= connection.get_database_version
78
129
  end
79
130
 
80
131
  # Clears out internal caches
@@ -83,11 +134,13 @@ module ActiveRecord
83
134
  @columns_hash.clear
84
135
  @primary_keys.clear
85
136
  @data_sources.clear
137
+ @indexes.clear
86
138
  @version = nil
139
+ @database_version = nil
87
140
  end
88
141
 
89
142
  def size
90
- [@columns, @columns_hash, @primary_keys, @data_sources].map(&:size).inject :+
143
+ [@columns, @columns_hash, @primary_keys, @data_sources].sum(&:size)
91
144
  end
92
145
 
93
146
  # Clear out internal caches for the data source +name+.
@@ -96,23 +149,76 @@ module ActiveRecord
96
149
  @columns_hash.delete name
97
150
  @primary_keys.delete name
98
151
  @data_sources.delete name
152
+ @indexes.delete name
153
+ end
154
+
155
+ def dump_to(filename)
156
+ clear!
157
+ connection.data_sources.each { |table| add(table) }
158
+ open(filename) { |f|
159
+ if filename.include?(".dump")
160
+ f.write(Marshal.dump(self))
161
+ else
162
+ f.write(YAML.dump(self))
163
+ end
164
+ }
99
165
  end
100
166
 
101
167
  def marshal_dump
102
- # if we get current version during initialization, it happens stack over flow.
103
- @version = connection.migration_context.current_version
104
- [@version, @columns, @columns_hash, @primary_keys, @data_sources]
168
+ reset_version!
169
+
170
+ [@version, @columns, {}, @primary_keys, @data_sources, @indexes, database_version]
105
171
  end
106
172
 
107
173
  def marshal_load(array)
108
- @version, @columns, @columns_hash, @primary_keys, @data_sources = array
174
+ @version, @columns, _columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
175
+ @indexes ||= {}
176
+
177
+ derive_columns_hash_and_deduplicate_values
109
178
  end
110
179
 
111
180
  private
181
+ def reset_version!
182
+ @version = connection.migration_context.current_version
183
+ end
184
+
185
+ def derive_columns_hash_and_deduplicate_values
186
+ @columns = deep_deduplicate(@columns)
187
+ @columns_hash = @columns.transform_values { |columns| columns.index_by(&:name) }
188
+ @primary_keys = deep_deduplicate(@primary_keys)
189
+ @data_sources = deep_deduplicate(@data_sources)
190
+ @indexes = deep_deduplicate(@indexes)
191
+ end
192
+
193
+ def deep_deduplicate(value)
194
+ case value
195
+ when Hash
196
+ value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
197
+ when Array
198
+ value.map { |i| deep_deduplicate(i) }
199
+ when String, Deduplicable
200
+ -value
201
+ else
202
+ value
203
+ end
204
+ end
112
205
 
113
206
  def prepare_data_sources
114
207
  connection.data_sources.each { |source| @data_sources[source] = true }
115
208
  end
209
+
210
+ def open(filename)
211
+ File.atomic_write(filename) do |file|
212
+ if File.extname(filename) == ".gz"
213
+ zipper = Zlib::GzipWriter.new file
214
+ yield zipper
215
+ zipper.flush
216
+ zipper.close
217
+ else
218
+ yield file
219
+ end
220
+ end
221
+ end
116
222
  end
117
223
  end
118
224
  end
@@ -1,9 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_record/connection_adapters/deduplicable"
4
+
3
5
  module ActiveRecord
4
6
  # :stopdoc:
5
7
  module ConnectionAdapters
6
8
  class SqlTypeMetadata
9
+ include Deduplicable
10
+
7
11
  attr_reader :sql_type, :type, :limit, :precision, :scale
8
12
 
9
13
  def initialize(sql_type: nil, type: nil, limit: nil, precision: nil, scale: nil)
@@ -16,18 +20,27 @@ module ActiveRecord
16
20
 
17
21
  def ==(other)
18
22
  other.is_a?(SqlTypeMetadata) &&
19
- attributes_for_hash == other.attributes_for_hash
23
+ sql_type == other.sql_type &&
24
+ type == other.type &&
25
+ limit == other.limit &&
26
+ precision == other.precision &&
27
+ scale == other.scale
20
28
  end
21
29
  alias eql? ==
22
30
 
23
31
  def hash
24
- attributes_for_hash.hash
32
+ SqlTypeMetadata.hash ^
33
+ sql_type.hash ^
34
+ type.hash ^
35
+ limit.hash ^
36
+ precision.hash >> 1 ^
37
+ scale.hash >> 2
25
38
  end
26
39
 
27
- protected
28
-
29
- def attributes_for_hash
30
- [self.class, sql_type, type, limit, precision, scale]
40
+ private
41
+ def deduplicated
42
+ @sql_type = -sql_type
43
+ super
31
44
  end
32
45
  end
33
46
  end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module SQLite3
6
+ module DatabaseStatements
7
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
8
+ :pragma
9
+ ) # :nodoc:
10
+ private_constant :READ_QUERY
11
+
12
+ def write_query?(sql) # :nodoc:
13
+ !READ_QUERY.match?(sql)
14
+ end
15
+
16
+ def explain(arel, binds = [])
17
+ sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
18
+ SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
19
+ end
20
+
21
+ def execute(sql, name = nil) #:nodoc:
22
+ if preventing_writes? && write_query?(sql)
23
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
24
+ end
25
+
26
+ materialize_transactions
27
+ mark_transaction_written_if_write(sql)
28
+
29
+ log(sql, name) do
30
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
31
+ @connection.execute(sql)
32
+ end
33
+ end
34
+ end
35
+
36
+ def exec_query(sql, name = nil, binds = [], prepare: false)
37
+ if preventing_writes? && write_query?(sql)
38
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
39
+ end
40
+
41
+ materialize_transactions
42
+ mark_transaction_written_if_write(sql)
43
+
44
+ type_casted_binds = type_casted_binds(binds)
45
+
46
+ log(sql, name, binds, type_casted_binds) do
47
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
48
+ # Don't cache statements if they are not prepared
49
+ unless prepare
50
+ stmt = @connection.prepare(sql)
51
+ begin
52
+ cols = stmt.columns
53
+ unless without_prepared_statement?(binds)
54
+ stmt.bind_params(type_casted_binds)
55
+ end
56
+ records = stmt.to_a
57
+ ensure
58
+ stmt.close
59
+ end
60
+ else
61
+ stmt = @statements[sql] ||= @connection.prepare(sql)
62
+ cols = stmt.columns
63
+ stmt.reset!
64
+ stmt.bind_params(type_casted_binds)
65
+ records = stmt.to_a
66
+ end
67
+
68
+ build_result(columns: cols, rows: records)
69
+ end
70
+ end
71
+ end
72
+
73
+ def exec_delete(sql, name = "SQL", binds = [])
74
+ exec_query(sql, name, binds)
75
+ @connection.changes
76
+ end
77
+ alias :exec_update :exec_delete
78
+
79
+ def begin_isolated_db_transaction(isolation) #:nodoc
80
+ raise TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
81
+ raise StandardError, "You need to enable the shared-cache mode in SQLite mode before attempting to change the transaction isolation level" unless shared_cache?
82
+
83
+ Thread.current.thread_variable_set("read_uncommitted", @connection.get_first_value("PRAGMA read_uncommitted"))
84
+ @connection.read_uncommitted = true
85
+ begin_db_transaction
86
+ end
87
+
88
+ def begin_db_transaction #:nodoc:
89
+ log("begin transaction", "TRANSACTION") { @connection.transaction }
90
+ end
91
+
92
+ def commit_db_transaction #:nodoc:
93
+ log("commit transaction", "TRANSACTION") { @connection.commit }
94
+ reset_read_uncommitted
95
+ end
96
+
97
+ def exec_rollback_db_transaction #:nodoc:
98
+ log("rollback transaction", "TRANSACTION") { @connection.rollback }
99
+ reset_read_uncommitted
100
+ end
101
+
102
+ private
103
+ def reset_read_uncommitted
104
+ read_uncommitted = Thread.current.thread_variable_get("read_uncommitted")
105
+ return unless read_uncommitted
106
+
107
+ @connection.read_uncommitted = read_uncommitted
108
+ end
109
+
110
+ def execute_batch(statements, name = nil)
111
+ sql = combine_multi_statements(statements)
112
+
113
+ if preventing_writes? && write_query?(sql)
114
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
115
+ end
116
+
117
+ materialize_transactions
118
+ mark_transaction_written_if_write(sql)
119
+
120
+ log(sql, name) do
121
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
122
+ @connection.execute_batch2(sql)
123
+ end
124
+ end
125
+ end
126
+
127
+ def last_inserted_id(result)
128
+ @connection.last_insert_row_id
129
+ end
130
+
131
+ def build_fixture_statements(fixture_set)
132
+ fixture_set.flat_map do |table_name, fixtures|
133
+ next if fixtures.empty?
134
+ fixtures.map { |fixture| build_fixture_sql([fixture], table_name) }
135
+ end.compact
136
+ end
137
+
138
+ def build_truncate_statement(table_name)
139
+ "DELETE FROM #{quote_table_name(table_name)}"
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -13,11 +13,11 @@ module ActiveRecord
13
13
  end
14
14
 
15
15
  def quote_table_name(name)
16
- @quoted_table_names[name] ||= super.gsub(".", "\".\"").freeze
16
+ self.class.quoted_table_names[name] ||= super.gsub(".", "\".\"").freeze
17
17
  end
18
18
 
19
19
  def quote_column_name(name)
20
- @quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}").freeze
20
+ self.class.quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}")
21
21
  end
22
22
 
23
23
  def quoted_time(value)
@@ -30,23 +30,58 @@ module ActiveRecord
30
30
  end
31
31
 
32
32
  def quoted_true
33
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? "1".freeze : "'t'".freeze
33
+ "1"
34
34
  end
35
35
 
36
36
  def unquoted_true
37
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? 1 : "t".freeze
37
+ 1
38
38
  end
39
39
 
40
40
  def quoted_false
41
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? "0".freeze : "'f'".freeze
41
+ "0"
42
42
  end
43
43
 
44
44
  def unquoted_false
45
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? 0 : "f".freeze
45
+ 0
46
46
  end
47
47
 
48
- private
48
+ def column_name_matcher
49
+ COLUMN_NAME
50
+ end
51
+
52
+ def column_name_with_order_matcher
53
+ COLUMN_NAME_WITH_ORDER
54
+ end
49
55
 
56
+ COLUMN_NAME = /
57
+ \A
58
+ (
59
+ (?:
60
+ # "table_name"."column_name" | function(one or no argument)
61
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
62
+ )
63
+ (?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
64
+ )
65
+ (?:\s*,\s*\g<1>)*
66
+ \z
67
+ /ix
68
+
69
+ COLUMN_NAME_WITH_ORDER = /
70
+ \A
71
+ (
72
+ (?:
73
+ # "table_name"."column_name" | function(one or no argument)
74
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
75
+ )
76
+ (?:\s+ASC|\s+DESC)?
77
+ )
78
+ (?:\s*,\s*\g<1>)*
79
+ \z
80
+ /ix
81
+
82
+ private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
83
+
84
+ private
50
85
  def _type_cast(value)
51
86
  case value
52
87
  when BigDecimal