activerecord 4.2.9 → 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 (274) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +614 -1572
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -11
  5. data/examples/performance.rb +32 -31
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +263 -249
  8. data/lib/active_record/association_relation.rb +11 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +77 -43
  11. data/lib/active_record/associations/association_scope.rb +106 -133
  12. data/lib/active_record/associations/belongs_to_association.rb +52 -41
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +29 -38
  15. data/lib/active_record/associations/builder/belongs_to.rb +77 -30
  16. data/lib/active_record/associations/builder/collection_association.rb +9 -22
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -35
  18. data/lib/active_record/associations/builder/has_many.rb +6 -4
  19. data/lib/active_record/associations/builder/has_one.rb +13 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +15 -11
  21. data/lib/active_record/associations/collection_association.rb +139 -280
  22. data/lib/active_record/associations/collection_proxy.rb +231 -133
  23. data/lib/active_record/associations/foreign_association.rb +3 -1
  24. data/lib/active_record/associations/has_many_association.rb +34 -89
  25. data/lib/active_record/associations/has_many_through_association.rb +49 -76
  26. data/lib/active_record/associations/has_one_association.rb +38 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +40 -89
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +133 -159
  32. data/lib/active_record/associations/preloader/association.rb +85 -120
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +81 -91
  35. data/lib/active_record/associations/singular_association.rb +27 -34
  36. data/lib/active_record/associations/through_association.rb +38 -18
  37. data/lib/active_record/associations.rb +1732 -1597
  38. data/lib/active_record/attribute_assignment.rb +58 -182
  39. data/lib/active_record/attribute_decorators.rb +39 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +10 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -135
  42. data/lib/active_record/attribute_methods/primary_key.rb +86 -71
  43. data/lib/active_record/attribute_methods/query.rb +4 -2
  44. data/lib/active_record/attribute_methods/read.rb +45 -63
  45. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
  47. data/lib/active_record/attribute_methods/write.rb +30 -45
  48. data/lib/active_record/attribute_methods.rb +166 -109
  49. data/lib/active_record/attributes.rb +201 -82
  50. data/lib/active_record/autosave_association.rb +94 -36
  51. data/lib/active_record/base.rb +57 -44
  52. data/lib/active_record/callbacks.rb +97 -57
  53. data/lib/active_record/coders/json.rb +3 -1
  54. data/lib/active_record/coders/yaml_column.rb +24 -12
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -290
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +237 -90
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +71 -21
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +118 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +318 -217
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +570 -228
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +138 -70
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +325 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +542 -593
  69. data/lib/active_record/connection_adapters/column.rb +50 -41
  70. data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +41 -188
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +45 -114
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +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 -5
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +55 -53
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -284
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +432 -323
  117. data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -308
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +40 -27
  128. data/lib/active_record/core.rb +178 -198
  129. data/lib/active_record/counter_cache.rb +79 -36
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +87 -105
  132. data/lib/active_record/enum.rb +135 -88
  133. data/lib/active_record/errors.rb +179 -52
  134. data/lib/active_record/explain.rb +23 -11
  135. data/lib/active_record/explain_registry.rb +4 -2
  136. data/lib/active_record/explain_subscriber.rb +10 -5
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +188 -132
  139. data/lib/active_record/gem_version.rb +4 -2
  140. data/lib/active_record/inheritance.rb +148 -112
  141. data/lib/active_record/integration.rb +70 -28
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +88 -96
  146. data/lib/active_record/locking/pessimistic.rb +15 -3
  147. data/lib/active_record/log_subscriber.rb +95 -33
  148. data/lib/active_record/migration/command_recorder.rb +133 -90
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +8 -6
  151. data/lib/active_record/migration.rb +581 -282
  152. data/lib/active_record/model_schema.rb +290 -111
  153. data/lib/active_record/nested_attributes.rb +264 -222
  154. data/lib/active_record/no_touching.rb +7 -1
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +347 -119
  157. data/lib/active_record/query_cache.rb +13 -24
  158. data/lib/active_record/querying.rb +19 -17
  159. data/lib/active_record/railtie.rb +94 -32
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +9 -3
  162. data/lib/active_record/railties/databases.rake +149 -156
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +414 -267
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +204 -55
  167. data/lib/active_record/relation/calculations.rb +256 -248
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +288 -239
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +86 -86
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -24
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  179. data/lib/active_record/relation/predicate_builder.rb +116 -119
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +448 -393
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +11 -13
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +287 -340
  187. data/lib/active_record/result.rb +54 -36
  188. data/lib/active_record/runtime_registry.rb +6 -4
  189. data/lib/active_record/sanitization.rb +155 -124
  190. data/lib/active_record/schema.rb +30 -24
  191. data/lib/active_record/schema_dumper.rb +91 -87
  192. data/lib/active_record/schema_migration.rb +19 -16
  193. data/lib/active_record/scoping/default.rb +102 -85
  194. data/lib/active_record/scoping/named.rb +81 -32
  195. data/lib/active_record/scoping.rb +45 -26
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +5 -5
  198. data/lib/active_record/statement_cache.rb +45 -35
  199. data/lib/active_record/store.rb +42 -36
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +134 -96
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +56 -100
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +83 -41
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
  206. data/lib/active_record/timestamp.rb +70 -38
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +199 -124
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +4 -45
  212. data/lib/active_record/type/date_time.rb +4 -49
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +24 -15
  218. data/lib/active_record/type/text.rb +2 -2
  219. data/lib/active_record/type/time.rb +11 -16
  220. data/lib/active_record/type/type_map.rb +15 -17
  221. data/lib/active_record/type/unsigned_integer.rb +9 -7
  222. data/lib/active_record/type.rb +79 -23
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +13 -4
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +14 -13
  230. data/lib/active_record/validations/uniqueness.rb +40 -41
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +34 -22
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
  237. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -3
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -1
  239. data/lib/rails/generators/active_record/migration.rb +18 -1
  240. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  243. data/lib/rails/generators/active_record.rb +7 -5
  244. metadata +72 -50
  245. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  246. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  247. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  248. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  249. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  250. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  251. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  252. data/lib/active_record/attribute.rb +0 -163
  253. data/lib/active_record/attribute_set/builder.rb +0 -106
  254. data/lib/active_record/attribute_set.rb +0 -81
  255. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  256. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  257. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  258. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  259. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  260. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  261. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  262. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  263. data/lib/active_record/type/big_integer.rb +0 -13
  264. data/lib/active_record/type/binary.rb +0 -50
  265. data/lib/active_record/type/boolean.rb +0 -31
  266. data/lib/active_record/type/decimal.rb +0 -64
  267. data/lib/active_record/type/decorator.rb +0 -14
  268. data/lib/active_record/type/float.rb +0 -19
  269. data/lib/active_record/type/integer.rb +0 -59
  270. data/lib/active_record/type/mutable.rb +0 -16
  271. data/lib/active_record/type/numeric.rb +0 -36
  272. data/lib/active_record/type/string.rb +0 -40
  273. data/lib/active_record/type/time_value.rb +0 -38
  274. data/lib/active_record/type/value.rb +0 -110
@@ -1,10 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Tasks # :nodoc:
3
5
  class MySQLDatabaseTasks # :nodoc:
4
- DEFAULT_CHARSET = ENV['CHARSET'] || 'utf8'
5
- DEFAULT_COLLATION = ENV['COLLATION'] || 'utf8_unicode_ci'
6
- ACCESS_DENIED_ERROR = 1045
7
-
8
6
  delegate :connection, :establish_connection, to: ActiveRecord::Base
9
7
 
10
8
  def initialize(configuration)
@@ -13,38 +11,24 @@ module ActiveRecord
13
11
 
14
12
  def create
15
13
  establish_connection configuration_without_database
16
- connection.create_database configuration['database'], creation_options
14
+ connection.create_database configuration["database"], creation_options
17
15
  establish_connection configuration
18
16
  rescue ActiveRecord::StatementInvalid => error
19
- if /database exists/ === error.message
17
+ if error.message.include?("database exists")
20
18
  raise DatabaseAlreadyExists
21
19
  else
22
20
  raise
23
21
  end
24
- rescue error_class => error
25
- if error.respond_to?(:errno) && error.errno == ACCESS_DENIED_ERROR
26
- $stdout.print error.error
27
- establish_connection root_configuration_without_database
28
- connection.create_database configuration['database'], creation_options
29
- if configuration['username'] != 'root'
30
- connection.execute grant_statement.gsub(/\s+/, ' ').strip
31
- end
32
- establish_connection configuration
33
- else
34
- $stderr.puts error.inspect
35
- $stderr.puts "Couldn't create database for #{configuration.inspect}, #{creation_options.inspect}"
36
- $stderr.puts "(If you set the charset manually, make sure you have a matching collation)" if configuration['encoding']
37
- end
38
22
  end
39
23
 
40
24
  def drop
41
25
  establish_connection configuration
42
- connection.drop_database configuration['database']
26
+ connection.drop_database configuration["database"]
43
27
  end
44
28
 
45
29
  def purge
46
30
  establish_connection configuration
47
- connection.recreate_database configuration['database'], creation_options
31
+ connection.recreate_database configuration["database"], creation_options
48
32
  end
49
33
 
50
34
  def charset
@@ -55,105 +39,77 @@ module ActiveRecord
55
39
  connection.collation
56
40
  end
57
41
 
58
- def structure_dump(filename)
42
+ def structure_dump(filename, extra_flags)
59
43
  args = prepare_command_options
60
44
  args.concat(["--result-file", "#{filename}"])
61
45
  args.concat(["--no-data"])
46
+ args.concat(["--routines"])
47
+ args.concat(["--skip-comments"])
48
+
49
+ ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
50
+ if ignore_tables.any?
51
+ args += ignore_tables.map { |table| "--ignore-table=#{configuration['database']}.#{table}" }
52
+ end
53
+
62
54
  args.concat(["#{configuration['database']}"])
55
+ args.unshift(*extra_flags) if extra_flags
63
56
 
64
- run_cmd('mysqldump', args, 'dumping')
57
+ run_cmd("mysqldump", args, "dumping")
65
58
  end
66
59
 
67
- def structure_load(filename)
60
+ def structure_load(filename, extra_flags)
68
61
  args = prepare_command_options
69
- args.concat(['--execute', %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}])
62
+ args.concat(["--execute", %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}])
70
63
  args.concat(["--database", "#{configuration['database']}"])
64
+ args.unshift(*extra_flags) if extra_flags
71
65
 
72
- run_cmd('mysql', args, 'loading')
66
+ run_cmd("mysql", args, "loading")
73
67
  end
74
68
 
75
69
  private
76
70
 
77
- def configuration
78
- @configuration
79
- end
80
-
81
- def configuration_without_database
82
- configuration.merge('database' => nil)
83
- end
84
-
85
- def creation_options
86
- Hash.new.tap do |options|
87
- options[:charset] = configuration['encoding'] if configuration.include? 'encoding'
88
- options[:collation] = configuration['collation'] if configuration.include? 'collation'
89
-
90
- # Set default charset only when collation isn't set.
91
- options[:charset] ||= DEFAULT_CHARSET unless options[:collation]
92
-
93
- # Set default collation only when charset is also default.
94
- options[:collation] ||= DEFAULT_COLLATION if options[:charset] == DEFAULT_CHARSET
71
+ def configuration
72
+ @configuration
95
73
  end
96
- end
97
74
 
98
- def error_class
99
- if configuration['adapter'] =~ /jdbc/
100
- require 'active_record/railties/jdbcmysql_error'
101
- ArJdbcMySQL::Error
102
- elsif defined?(Mysql2)
103
- Mysql2::Error
104
- elsif defined?(Mysql)
105
- Mysql::Error
106
- else
107
- StandardError
75
+ def configuration_without_database
76
+ configuration.merge("database" => nil)
108
77
  end
109
- end
110
-
111
- def grant_statement
112
- <<-SQL
113
- GRANT ALL PRIVILEGES ON #{configuration['database']}.*
114
- TO '#{configuration['username']}'@'localhost'
115
- IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
116
- SQL
117
- end
118
78
 
119
- def root_configuration_without_database
120
- configuration_without_database.merge(
121
- 'username' => 'root',
122
- 'password' => root_password
123
- )
124
- end
125
-
126
- def root_password
127
- $stdout.print "Please provide the root password for your MySQL installation\n>"
128
- $stdin.gets.strip
129
- end
79
+ def creation_options
80
+ Hash.new.tap do |options|
81
+ options[:charset] = configuration["encoding"] if configuration.include? "encoding"
82
+ options[:collation] = configuration["collation"] if configuration.include? "collation"
83
+ end
84
+ end
130
85
 
131
- def prepare_command_options
132
- {
133
- 'host' => '--host',
134
- 'port' => '--port',
135
- 'socket' => '--socket',
136
- 'username' => '--user',
137
- 'password' => '--password',
138
- 'encoding' => '--default-character-set',
139
- 'sslca' => '--ssl-ca',
140
- 'sslcert' => '--ssl-cert',
141
- 'sslcapath' => '--ssl-capath',
142
- 'sslcipher' => '--ssl-cipher',
143
- 'sslkey' => '--ssl-key'
144
- }.map { |opt, arg| "#{arg}=#{configuration[opt]}" if configuration[opt] }.compact
145
- end
86
+ def prepare_command_options
87
+ args = {
88
+ "host" => "--host",
89
+ "port" => "--port",
90
+ "socket" => "--socket",
91
+ "username" => "--user",
92
+ "password" => "--password",
93
+ "encoding" => "--default-character-set",
94
+ "sslca" => "--ssl-ca",
95
+ "sslcert" => "--ssl-cert",
96
+ "sslcapath" => "--ssl-capath",
97
+ "sslcipher" => "--ssl-cipher",
98
+ "sslkey" => "--ssl-key"
99
+ }.map { |opt, arg| "#{arg}=#{configuration[opt]}" if configuration[opt] }.compact
100
+
101
+ args
102
+ end
146
103
 
147
- def run_cmd(cmd, args, action)
148
- fail run_cmd_error(cmd, args, action) unless Kernel.system(cmd, *args)
149
- end
104
+ def run_cmd(cmd, args, action)
105
+ fail run_cmd_error(cmd, args, action) unless Kernel.system(cmd, *args)
106
+ end
150
107
 
151
- def run_cmd_error(cmd, args, action)
152
- msg = "failed to execute:\n"
153
- msg << "#{cmd}"
154
- msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
155
- msg
156
- end
108
+ def run_cmd_error(cmd, args, action)
109
+ msg = "failed to execute: `#{cmd}`\n".dup
110
+ msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
111
+ msg
112
+ end
157
113
  end
158
114
  end
159
115
  end
@@ -1,7 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tempfile"
4
+
1
5
  module ActiveRecord
2
6
  module Tasks # :nodoc:
3
7
  class PostgreSQLDatabaseTasks # :nodoc:
4
- DEFAULT_ENCODING = ENV['CHARSET'] || 'utf8'
8
+ DEFAULT_ENCODING = ENV["CHARSET"] || "utf8"
9
+ ON_ERROR_STOP_1 = "ON_ERROR_STOP=1".freeze
10
+ SQL_COMMENT_BEGIN = "--".freeze
5
11
 
6
12
  delegate :connection, :establish_connection, :clear_active_connections!,
7
13
  to: ActiveRecord::Base
@@ -12,11 +18,11 @@ module ActiveRecord
12
18
 
13
19
  def create(master_established = false)
14
20
  establish_master_connection unless master_established
15
- connection.create_database configuration['database'],
16
- configuration.merge('encoding' => encoding)
21
+ connection.create_database configuration["database"],
22
+ configuration.merge("encoding" => encoding)
17
23
  establish_connection configuration
18
24
  rescue ActiveRecord::StatementInvalid => error
19
- if /database .* already exists/ === error.message
25
+ if error.cause.is_a?(PG::DuplicateDatabase)
20
26
  raise DatabaseAlreadyExists
21
27
  else
22
28
  raise
@@ -25,7 +31,7 @@ module ActiveRecord
25
31
 
26
32
  def drop
27
33
  establish_master_connection
28
- connection.drop_database configuration['database']
34
+ connection.drop_database configuration["database"]
29
35
  end
30
36
 
31
37
  def charset
@@ -42,60 +48,96 @@ module ActiveRecord
42
48
  create true
43
49
  end
44
50
 
45
- def structure_dump(filename)
51
+ def structure_dump(filename, extra_flags)
46
52
  set_psql_env
47
- args = ['-s', '-x', '-O', '-f', filename]
48
- search_path = configuration['schema_search_path']
53
+
54
+ search_path = \
55
+ case ActiveRecord::Base.dump_schemas
56
+ when :schema_search_path
57
+ configuration["schema_search_path"]
58
+ when :all
59
+ nil
60
+ when String
61
+ ActiveRecord::Base.dump_schemas
62
+ end
63
+
64
+ args = ["-s", "-x", "-O", "-f", filename]
65
+ args.concat(Array(extra_flags)) if extra_flags
49
66
  unless search_path.blank?
50
- args += search_path.split(',').map do |part|
67
+ args += search_path.split(",").map do |part|
51
68
  "--schema=#{part.strip}"
52
69
  end
53
70
  end
54
- args << configuration['database']
55
- run_cmd('pg_dump', args, 'dumping')
71
+
72
+ ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
73
+ if ignore_tables.any?
74
+ args += ignore_tables.flat_map { |table| ["-T", table] }
75
+ end
76
+
77
+ args << configuration["database"]
78
+ run_cmd("pg_dump", args, "dumping")
79
+ remove_sql_header_comments(filename)
56
80
  File.open(filename, "a") { |f| f << "SET search_path TO #{connection.schema_search_path};\n\n" }
57
81
  end
58
82
 
59
- def structure_load(filename)
83
+ def structure_load(filename, extra_flags)
60
84
  set_psql_env
61
- args = [ '-q', '-f', filename, configuration['database'] ]
62
- run_cmd('psql', args, 'loading')
85
+ args = ["-v", ON_ERROR_STOP_1, "-q", "-f", filename]
86
+ args.concat(Array(extra_flags)) if extra_flags
87
+ args << configuration["database"]
88
+ run_cmd("psql", args, "loading")
63
89
  end
64
90
 
65
91
  private
66
92
 
67
- def configuration
68
- @configuration
69
- end
93
+ def configuration
94
+ @configuration
95
+ end
70
96
 
71
- def encoding
72
- configuration['encoding'] || DEFAULT_ENCODING
73
- end
97
+ def encoding
98
+ configuration["encoding"] || DEFAULT_ENCODING
99
+ end
74
100
 
75
- def establish_master_connection
76
- establish_connection configuration.merge(
77
- 'database' => 'postgres',
78
- 'schema_search_path' => 'public'
79
- )
80
- end
101
+ def establish_master_connection
102
+ establish_connection configuration.merge(
103
+ "database" => "postgres",
104
+ "schema_search_path" => "public"
105
+ )
106
+ end
81
107
 
82
- def set_psql_env
83
- ENV['PGHOST'] = configuration['host'] if configuration['host']
84
- ENV['PGPORT'] = configuration['port'].to_s if configuration['port']
85
- ENV['PGPASSWORD'] = configuration['password'].to_s if configuration['password']
86
- ENV['PGUSER'] = configuration['username'].to_s if configuration['username']
87
- end
108
+ def set_psql_env
109
+ ENV["PGHOST"] = configuration["host"] if configuration["host"]
110
+ ENV["PGPORT"] = configuration["port"].to_s if configuration["port"]
111
+ ENV["PGPASSWORD"] = configuration["password"].to_s if configuration["password"]
112
+ ENV["PGUSER"] = configuration["username"].to_s if configuration["username"]
113
+ end
88
114
 
89
- def run_cmd(cmd, args, action)
90
- fail run_cmd_error(cmd, args, action) unless Kernel.system(cmd, *args)
91
- end
115
+ def run_cmd(cmd, args, action)
116
+ fail run_cmd_error(cmd, args, action) unless Kernel.system(cmd, *args)
117
+ end
92
118
 
93
- def run_cmd_error(cmd, args, action)
94
- msg = "failed to execute:\n"
95
- msg << "#{cmd} #{args.join(' ')}\n\n"
96
- msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
97
- msg
98
- end
119
+ def run_cmd_error(cmd, args, action)
120
+ msg = "failed to execute:\n".dup
121
+ msg << "#{cmd} #{args.join(' ')}\n\n"
122
+ msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
123
+ msg
124
+ end
125
+
126
+ def remove_sql_header_comments(filename)
127
+ removing_comments = true
128
+ tempfile = Tempfile.open("uncommented_structure.sql")
129
+ begin
130
+ File.foreach(filename) do |line|
131
+ unless removing_comments && (line.start_with?(SQL_COMMENT_BEGIN) || line.blank?)
132
+ tempfile << line
133
+ removing_comments = false
134
+ end
135
+ end
136
+ ensure
137
+ tempfile.close
138
+ end
139
+ FileUtils.cp(tempfile.path, filename)
140
+ end
99
141
  end
100
142
  end
101
143
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Tasks # :nodoc:
3
5
  class SQLiteDatabaseTasks # :nodoc:
@@ -8,22 +10,26 @@ module ActiveRecord
8
10
  end
9
11
 
10
12
  def create
11
- raise DatabaseAlreadyExists if File.exist?(configuration['database'])
13
+ raise DatabaseAlreadyExists if File.exist?(configuration["database"])
12
14
 
13
15
  establish_connection configuration
14
16
  connection
15
17
  end
16
18
 
17
19
  def drop
18
- require 'pathname'
19
- path = Pathname.new configuration['database']
20
+ require "pathname"
21
+ path = Pathname.new configuration["database"]
20
22
  file = path.absolute? ? path.to_s : File.join(root, path)
21
23
 
22
- FileUtils.rm(file) if File.exist?(file)
24
+ FileUtils.rm(file)
25
+ rescue Errno::ENOENT => error
26
+ raise NoDatabaseError.new(error.message)
23
27
  end
24
28
 
25
29
  def purge
26
30
  drop
31
+ rescue NoDatabaseError
32
+ ensure
27
33
  create
28
34
  end
29
35
 
@@ -31,25 +37,47 @@ module ActiveRecord
31
37
  connection.encoding
32
38
  end
33
39
 
34
- def structure_dump(filename)
35
- dbfile = configuration['database']
36
- `sqlite3 #{dbfile} .schema > #{filename}`
40
+ def structure_dump(filename, extra_flags)
41
+ args = []
42
+ args.concat(Array(extra_flags)) if extra_flags
43
+ args << configuration["database"]
44
+
45
+ ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
46
+ if ignore_tables.any?
47
+ condition = ignore_tables.map { |table| connection.quote(table) }.join(", ")
48
+ args << "SELECT sql FROM sqlite_master WHERE tbl_name NOT IN (#{condition}) ORDER BY tbl_name, type DESC, name"
49
+ else
50
+ args << ".schema"
51
+ end
52
+ run_cmd("sqlite3", args, filename)
37
53
  end
38
54
 
39
- def structure_load(filename)
40
- dbfile = configuration['database']
41
- `sqlite3 #{dbfile} < "#{filename}"`
55
+ def structure_load(filename, extra_flags)
56
+ dbfile = configuration["database"]
57
+ flags = extra_flags.join(" ") if extra_flags
58
+ `sqlite3 #{flags} #{dbfile} < "#{filename}"`
42
59
  end
43
60
 
44
61
  private
45
62
 
46
- def configuration
47
- @configuration
48
- end
63
+ def configuration
64
+ @configuration
65
+ end
49
66
 
50
- def root
51
- @root
52
- end
67
+ def root
68
+ @root
69
+ end
70
+
71
+ def run_cmd(cmd, args, out)
72
+ fail run_cmd_error(cmd, args) unless Kernel.system(cmd, *args, out: out)
73
+ end
74
+
75
+ def run_cmd_error(cmd, args)
76
+ msg = "failed to execute:\n".dup
77
+ msg << "#{cmd} #{args.join(' ')}\n\n"
78
+ msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
79
+ msg
80
+ end
53
81
  end
54
82
  end
55
83
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
- # = Active Record Timestamp
4
+ # = Active Record \Timestamp
3
5
  #
4
6
  # Active Record automatically timestamps create and update operations if the
5
7
  # table has fields named <tt>created_at/created_on</tt> or
@@ -15,14 +17,25 @@ module ActiveRecord
15
17
  #
16
18
  # == Time Zone aware attributes
17
19
  #
18
- # By default, ActiveRecord::Base keeps all the datetime columns time zone aware by executing following code.
20
+ # Active Record keeps all the <tt>datetime</tt> and <tt>time</tt> columns
21
+ # timezone aware. By default, these values are stored in the database as UTC
22
+ # and converted back to the current <tt>Time.zone</tt> when pulled from the database.
23
+ #
24
+ # This feature can be turned off completely by setting:
25
+ #
26
+ # config.active_record.time_zone_aware_attributes = false
27
+ #
28
+ # You can also specify that only <tt>datetime</tt> columns should be time-zone
29
+ # aware (while <tt>time</tt> should not) by setting:
19
30
  #
20
- # config.active_record.time_zone_aware_attributes = true
31
+ # ActiveRecord::Base.time_zone_aware_types = [:datetime]
21
32
  #
22
- # This feature can easily be turned off by assigning value <tt>false</tt> .
33
+ # You can also add database specific timezone aware types. For example, for PostgreSQL:
23
34
  #
24
- # If your attributes are time zone aware and you desire to skip time zone conversion to the current Time.zone
25
- # when reading certain attributes then you can do following:
35
+ # ActiveRecord::Base.time_zone_aware_types += [:tsrange, :tstzrange]
36
+ #
37
+ # Finally, you can indicate specific attributes of a model for which time zone
38
+ # conversion should not applied, for instance by setting:
26
39
  #
27
40
  # class Topic < ActiveRecord::Base
28
41
  # self.skip_time_zone_conversion_for_attributes = [:written_on]
@@ -31,8 +44,7 @@ module ActiveRecord
31
44
  extend ActiveSupport::Concern
32
45
 
33
46
  included do
34
- class_attribute :record_timestamps
35
- self.record_timestamps = true
47
+ class_attribute :record_timestamps, default: true
36
48
  end
37
49
 
38
50
  def initialize_dup(other) # :nodoc:
@@ -40,16 +52,49 @@ module ActiveRecord
40
52
  clear_timestamp_attributes
41
53
  end
42
54
 
55
+ module ClassMethods # :nodoc:
56
+ def touch_attributes_with_time(*names, time: nil)
57
+ attribute_names = timestamp_attributes_for_update_in_model
58
+ attribute_names |= names.map(&:to_s)
59
+ time ||= current_time_from_proper_timezone
60
+ attribute_names.each_with_object({}) { |attr_name, result| result[attr_name] = time }
61
+ end
62
+
63
+ private
64
+ def timestamp_attributes_for_create_in_model
65
+ timestamp_attributes_for_create.select { |c| column_names.include?(c) }
66
+ end
67
+
68
+ def timestamp_attributes_for_update_in_model
69
+ timestamp_attributes_for_update.select { |c| column_names.include?(c) }
70
+ end
71
+
72
+ def all_timestamp_attributes_in_model
73
+ timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model
74
+ end
75
+
76
+ def timestamp_attributes_for_create
77
+ ["created_at", "created_on"]
78
+ end
79
+
80
+ def timestamp_attributes_for_update
81
+ ["updated_at", "updated_on"]
82
+ end
83
+
84
+ def current_time_from_proper_timezone
85
+ default_timezone == :utc ? Time.now.utc : Time.now
86
+ end
87
+ end
88
+
43
89
  private
44
90
 
45
91
  def _create_record
46
- if self.record_timestamps
92
+ if record_timestamps
47
93
  current_time = current_time_from_proper_timezone
48
94
 
49
- all_timestamp_attributes.each do |column|
50
- column = column.to_s
51
- if has_attribute?(column) && !attribute_present?(column)
52
- write_attribute(column, current_time)
95
+ all_timestamp_attributes_in_model.each do |column|
96
+ if !attribute_present?(column)
97
+ _write_attribute(column, current_time)
53
98
  end
54
99
  end
55
100
  end
@@ -57,48 +102,39 @@ module ActiveRecord
57
102
  super
58
103
  end
59
104
 
60
- def _update_record(*args)
61
- if should_record_timestamps?
105
+ def _update_record(*args, touch: true, **options)
106
+ if touch && should_record_timestamps?
62
107
  current_time = current_time_from_proper_timezone
63
108
 
64
109
  timestamp_attributes_for_update_in_model.each do |column|
65
- column = column.to_s
66
- next if attribute_changed?(column)
67
- write_attribute(column, current_time)
110
+ next if will_save_change_to_attribute?(column)
111
+ _write_attribute(column, current_time)
68
112
  end
69
113
  end
70
- super
114
+ super(*args)
71
115
  end
72
116
 
73
117
  def should_record_timestamps?
74
- self.record_timestamps && (!partial_writes? || changed?)
118
+ record_timestamps && (!partial_writes? || has_changes_to_save?)
75
119
  end
76
120
 
77
121
  def timestamp_attributes_for_create_in_model
78
- timestamp_attributes_for_create.select { |c| self.class.column_names.include?(c.to_s) }
122
+ self.class.send(:timestamp_attributes_for_create_in_model)
79
123
  end
80
124
 
81
125
  def timestamp_attributes_for_update_in_model
82
- timestamp_attributes_for_update.select { |c| self.class.column_names.include?(c.to_s) }
126
+ self.class.send(:timestamp_attributes_for_update_in_model)
83
127
  end
84
128
 
85
129
  def all_timestamp_attributes_in_model
86
- timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model
87
- end
88
-
89
- def timestamp_attributes_for_update
90
- [:updated_at, :updated_on]
91
- end
92
-
93
- def timestamp_attributes_for_create
94
- [:created_at, :created_on]
130
+ self.class.send(:all_timestamp_attributes_in_model)
95
131
  end
96
132
 
97
- def all_timestamp_attributes
98
- timestamp_attributes_for_create + timestamp_attributes_for_update
133
+ def current_time_from_proper_timezone
134
+ self.class.send(:current_time_from_proper_timezone)
99
135
  end
100
136
 
101
- def max_updated_column_timestamp(timestamp_names = timestamp_attributes_for_update)
137
+ def max_updated_column_timestamp(timestamp_names = timestamp_attributes_for_update_in_model)
102
138
  timestamp_names
103
139
  .map { |attr| self[attr] }
104
140
  .compact
@@ -106,10 +142,6 @@ module ActiveRecord
106
142
  .max
107
143
  end
108
144
 
109
- def current_time_from_proper_timezone
110
- self.class.default_timezone == :utc ? Time.now.utc : Time.now
111
- end
112
-
113
145
  # Clear attributes and changed_attributes
114
146
  def clear_timestamp_attributes
115
147
  all_timestamp_attributes_in_model.each do |attribute_name|