activerecord 4.2.0 → 5.2.8.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (274) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +640 -928
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -11
  5. data/examples/performance.rb +32 -31
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +264 -247
  8. data/lib/active_record/association_relation.rb +24 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +87 -41
  11. data/lib/active_record/associations/association_scope.rb +106 -132
  12. data/lib/active_record/associations/belongs_to_association.rb +55 -36
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +29 -38
  15. data/lib/active_record/associations/builder/belongs_to.rb +77 -30
  16. data/lib/active_record/associations/builder/collection_association.rb +14 -23
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
  18. data/lib/active_record/associations/builder/has_many.rb +6 -4
  19. data/lib/active_record/associations/builder/has_one.rb +13 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +15 -11
  21. data/lib/active_record/associations/collection_association.rb +145 -266
  22. data/lib/active_record/associations/collection_proxy.rb +242 -138
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +35 -75
  25. data/lib/active_record/associations/has_many_through_association.rb +51 -69
  26. data/lib/active_record/associations/has_one_association.rb +39 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +40 -81
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +134 -154
  32. data/lib/active_record/associations/preloader/association.rb +85 -116
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +83 -93
  35. data/lib/active_record/associations/singular_association.rb +27 -40
  36. data/lib/active_record/associations/through_association.rb +48 -23
  37. data/lib/active_record/associations.rb +1732 -1596
  38. data/lib/active_record/attribute_assignment.rb +58 -182
  39. data/lib/active_record/attribute_decorators.rb +39 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +12 -5
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -125
  42. data/lib/active_record/attribute_methods/primary_key.rb +86 -71
  43. data/lib/active_record/attribute_methods/query.rb +4 -2
  44. data/lib/active_record/attribute_methods/read.rb +45 -63
  45. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +62 -36
  47. data/lib/active_record/attribute_methods/write.rb +31 -46
  48. data/lib/active_record/attribute_methods.rb +170 -117
  49. data/lib/active_record/attributes.rb +201 -74
  50. data/lib/active_record/autosave_association.rb +118 -45
  51. data/lib/active_record/base.rb +60 -48
  52. data/lib/active_record/callbacks.rb +97 -57
  53. data/lib/active_record/coders/json.rb +3 -1
  54. data/lib/active_record/coders/yaml_column.rb +37 -13
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -217
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +617 -212
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
  69. data/lib/active_record/connection_adapters/column.rb +50 -41
  70. data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +42 -195
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -57
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +5 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  99. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +466 -280
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +439 -330
  117. data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -324
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +40 -27
  128. data/lib/active_record/core.rb +205 -202
  129. data/lib/active_record/counter_cache.rb +80 -37
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +87 -105
  132. data/lib/active_record/enum.rb +136 -90
  133. data/lib/active_record/errors.rb +180 -52
  134. data/lib/active_record/explain.rb +23 -11
  135. data/lib/active_record/explain_registry.rb +4 -2
  136. data/lib/active_record/explain_subscriber.rb +11 -6
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +193 -135
  139. data/lib/active_record/gem_version.rb +5 -3
  140. data/lib/active_record/inheritance.rb +148 -112
  141. data/lib/active_record/integration.rb +70 -28
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +92 -98
  146. data/lib/active_record/locking/pessimistic.rb +15 -3
  147. data/lib/active_record/log_subscriber.rb +95 -33
  148. data/lib/active_record/migration/command_recorder.rb +133 -90
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +8 -6
  151. data/lib/active_record/migration.rb +594 -267
  152. data/lib/active_record/model_schema.rb +292 -111
  153. data/lib/active_record/nested_attributes.rb +266 -214
  154. data/lib/active_record/no_touching.rb +8 -2
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +350 -119
  157. data/lib/active_record/query_cache.rb +13 -24
  158. data/lib/active_record/querying.rb +19 -17
  159. data/lib/active_record/railtie.rb +117 -35
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +9 -3
  162. data/lib/active_record/railties/databases.rake +160 -174
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +447 -288
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +204 -55
  167. data/lib/active_record/relation/calculations.rb +259 -244
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +290 -253
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +91 -68
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  179. data/lib/active_record/relation/predicate_builder.rb +118 -92
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +446 -389
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +18 -16
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +287 -339
  187. data/lib/active_record/result.rb +54 -36
  188. data/lib/active_record/runtime_registry.rb +6 -4
  189. data/lib/active_record/sanitization.rb +155 -124
  190. data/lib/active_record/schema.rb +30 -24
  191. data/lib/active_record/schema_dumper.rb +91 -87
  192. data/lib/active_record/schema_migration.rb +19 -19
  193. data/lib/active_record/scoping/default.rb +102 -84
  194. data/lib/active_record/scoping/named.rb +81 -32
  195. data/lib/active_record/scoping.rb +45 -26
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +5 -5
  198. data/lib/active_record/statement_cache.rb +45 -35
  199. data/lib/active_record/store.rb +42 -36
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +136 -95
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
  206. data/lib/active_record/timestamp.rb +70 -38
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +208 -123
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +4 -41
  212. data/lib/active_record/type/date_time.rb +4 -38
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +30 -15
  218. data/lib/active_record/type/text.rb +2 -2
  219. data/lib/active_record/type/time.rb +11 -16
  220. data/lib/active_record/type/type_map.rb +15 -17
  221. data/lib/active_record/type/unsigned_integer.rb +9 -7
  222. data/lib/active_record/type.rb +79 -23
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +13 -4
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +14 -13
  230. data/lib/active_record/validations/uniqueness.rb +41 -32
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +36 -21
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
  237. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -6
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
  239. data/lib/rails/generators/active_record/migration.rb +18 -1
  240. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
  242. data/lib/rails/generators/active_record.rb +7 -5
  243. metadata +77 -53
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  251. data/lib/active_record/attribute.rb +0 -149
  252. data/lib/active_record/attribute_set/builder.rb +0 -86
  253. data/lib/active_record/attribute_set.rb +0 -77
  254. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  255. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  256. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  257. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  258. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  259. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  260. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  261. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  262. data/lib/active_record/type/big_integer.rb +0 -13
  263. data/lib/active_record/type/binary.rb +0 -50
  264. data/lib/active_record/type/boolean.rb +0 -30
  265. data/lib/active_record/type/decimal.rb +0 -40
  266. data/lib/active_record/type/decorator.rb +0 -14
  267. data/lib/active_record/type/float.rb +0 -19
  268. data/lib/active_record/type/integer.rb +0 -55
  269. data/lib/active_record/type/mutable.rb +0 -16
  270. data/lib/active_record/type/numeric.rb +0 -36
  271. data/lib/active_record/type/string.rb +0 -36
  272. data/lib/active_record/type/time_value.rb +0 -38
  273. data/lib/active_record/type/value.rb +0 -101
  274. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,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,91 +39,77 @@ module ActiveRecord
55
39
  connection.collation
56
40
  end
57
41
 
58
- def structure_dump(filename)
59
- args = prepare_command_options('mysqldump')
42
+ def structure_dump(filename, extra_flags)
43
+ args = prepare_command_options
60
44
  args.concat(["--result-file", "#{filename}"])
61
45
  args.concat(["--no-data"])
62
- args.concat(["#{configuration['database']}"])
63
- unless Kernel.system(*args)
64
- $stderr.puts "Could not dump the database structure. "\
65
- "Make sure `mysqldump` is in your PATH and check the command output for warnings."
66
- end
67
- end
46
+ args.concat(["--routines"])
47
+ args.concat(["--skip-comments"])
68
48
 
69
- def structure_load(filename)
70
- args = prepare_command_options('mysql')
71
- args.concat(['--execute', %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}])
72
- args.concat(["--database", "#{configuration['database']}"])
73
- Kernel.system(*args)
74
- end
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
75
53
 
76
- private
54
+ args.concat(["#{configuration['database']}"])
55
+ args.unshift(*extra_flags) if extra_flags
77
56
 
78
- def configuration
79
- @configuration
57
+ run_cmd("mysqldump", args, "dumping")
80
58
  end
81
59
 
82
- def configuration_without_database
83
- configuration.merge('database' => nil)
84
- end
60
+ def structure_load(filename, extra_flags)
61
+ args = prepare_command_options
62
+ args.concat(["--execute", %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}])
63
+ args.concat(["--database", "#{configuration['database']}"])
64
+ args.unshift(*extra_flags) if extra_flags
85
65
 
86
- def creation_options
87
- Hash.new.tap do |options|
88
- options[:charset] = configuration['encoding'] if configuration.include? 'encoding'
89
- options[:collation] = configuration['collation'] if configuration.include? 'collation'
66
+ run_cmd("mysql", args, "loading")
67
+ end
90
68
 
91
- # Set default charset only when collation isn't set.
92
- options[:charset] ||= DEFAULT_CHARSET unless options[:collation]
69
+ private
93
70
 
94
- # Set default collation only when charset is also default.
95
- options[:collation] ||= DEFAULT_COLLATION if options[:charset] == DEFAULT_CHARSET
71
+ def configuration
72
+ @configuration
96
73
  end
97
- end
98
74
 
99
- def error_class
100
- if configuration['adapter'] =~ /jdbc/
101
- require 'active_record/railties/jdbcmysql_error'
102
- ArJdbcMySQL::Error
103
- elsif defined?(Mysql2)
104
- Mysql2::Error
105
- elsif defined?(Mysql)
106
- Mysql::Error
107
- else
108
- StandardError
75
+ def configuration_without_database
76
+ configuration.merge("database" => nil)
109
77
  end
110
- end
111
-
112
- def grant_statement
113
- <<-SQL
114
- GRANT ALL PRIVILEGES ON #{configuration['database']}.*
115
- TO '#{configuration['username']}'@'localhost'
116
- IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
117
- SQL
118
- end
119
78
 
120
- def root_configuration_without_database
121
- configuration_without_database.merge(
122
- 'username' => 'root',
123
- 'password' => root_password
124
- )
125
- 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
126
85
 
127
- def root_password
128
- $stdout.print "Please provide the root password for your MySQL installation\n>"
129
- $stdin.gets.strip
130
- 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
131
103
 
132
- def prepare_command_options(command)
133
- args = [command]
134
- args.concat(['--user', configuration['username']]) if configuration['username']
135
- args << "--password=#{configuration['password']}" if configuration['password']
136
- args.concat(['--default-character-set', configuration['encoding']]) if configuration['encoding']
137
- configuration.slice('host', 'port', 'socket').each do |k, v|
138
- args.concat([ "--#{k}", v.to_s ]) if v
104
+ def run_cmd(cmd, args, action)
105
+ fail run_cmd_error(cmd, args, action) unless Kernel.system(cmd, *args)
139
106
  end
140
107
 
141
- args
142
- 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
143
113
  end
144
114
  end
145
115
  end
@@ -1,9 +1,13 @@
1
- require 'shellwords'
1
+ # frozen_string_literal: true
2
+
3
+ require "tempfile"
2
4
 
3
5
  module ActiveRecord
4
6
  module Tasks # :nodoc:
5
7
  class PostgreSQLDatabaseTasks # :nodoc:
6
- 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
7
11
 
8
12
  delegate :connection, :establish_connection, :clear_active_connections!,
9
13
  to: ActiveRecord::Base
@@ -14,11 +18,11 @@ module ActiveRecord
14
18
 
15
19
  def create(master_established = false)
16
20
  establish_master_connection unless master_established
17
- connection.create_database configuration['database'],
18
- configuration.merge('encoding' => encoding)
21
+ connection.create_database configuration["database"],
22
+ configuration.merge("encoding" => encoding)
19
23
  establish_connection configuration
20
24
  rescue ActiveRecord::StatementInvalid => error
21
- if /database .* already exists/ === error.message
25
+ if error.cause.is_a?(PG::DuplicateDatabase)
22
26
  raise DatabaseAlreadyExists
23
27
  else
24
28
  raise
@@ -27,7 +31,7 @@ module ActiveRecord
27
31
 
28
32
  def drop
29
33
  establish_master_connection
30
- connection.drop_database configuration['database']
34
+ connection.drop_database configuration["database"]
31
35
  end
32
36
 
33
37
  def charset
@@ -44,47 +48,96 @@ module ActiveRecord
44
48
  create true
45
49
  end
46
50
 
47
- def structure_dump(filename)
51
+ def structure_dump(filename, extra_flags)
48
52
  set_psql_env
49
- 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
50
66
  unless search_path.blank?
51
- search_path = search_path.split(",").map{|search_path_part| "--schema=#{Shellwords.escape(search_path_part.strip)}" }.join(" ")
67
+ args += search_path.split(",").map do |part|
68
+ "--schema=#{part.strip}"
69
+ end
52
70
  end
53
71
 
54
- command = "pg_dump -i -s -x -O -f #{Shellwords.escape(filename)} #{search_path} #{Shellwords.escape(configuration['database'])}"
55
- raise 'Error dumping database' unless Kernel.system(command)
72
+ ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
73
+ if ignore_tables.any?
74
+ args += ignore_tables.flat_map { |table| ["-T", table] }
75
+ end
56
76
 
77
+ args << configuration["database"]
78
+ run_cmd("pg_dump", args, "dumping")
79
+ remove_sql_header_comments(filename)
57
80
  File.open(filename, "a") { |f| f << "SET search_path TO #{connection.schema_search_path};\n\n" }
58
81
  end
59
82
 
60
- def structure_load(filename)
83
+ def structure_load(filename, extra_flags)
61
84
  set_psql_env
62
- Kernel.system("psql -q -f #{Shellwords.escape(filename)} #{configuration['database']}")
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
114
+
115
+ def run_cmd(cmd, args, action)
116
+ fail run_cmd_error(cmd, args, action) unless Kernel.system(cmd, *args)
117
+ end
118
+
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
88
141
  end
89
142
  end
90
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|