activerecord 4.2.0 → 5.0.0

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 (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1537 -789
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -8
  5. data/examples/performance.rb +2 -3
  6. data/examples/simple.rb +0 -1
  7. data/lib/active_record/aggregations.rb +37 -23
  8. data/lib/active_record/association_relation.rb +16 -3
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +23 -9
  11. data/lib/active_record/associations/association_scope.rb +74 -102
  12. data/lib/active_record/associations/belongs_to_association.rb +26 -29
  13. data/lib/active_record/associations/builder/association.rb +28 -34
  14. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  15. data/lib/active_record/associations/builder/collection_association.rb +12 -20
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +22 -15
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +11 -6
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  20. data/lib/active_record/associations/collection_association.rb +61 -33
  21. data/lib/active_record/associations/collection_proxy.rb +81 -35
  22. data/lib/active_record/associations/foreign_association.rb +11 -0
  23. data/lib/active_record/associations/has_many_association.rb +21 -57
  24. data/lib/active_record/associations/has_many_through_association.rb +15 -45
  25. data/lib/active_record/associations/has_one_association.rb +13 -5
  26. data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
  27. data/lib/active_record/associations/join_dependency.rb +37 -21
  28. data/lib/active_record/associations/preloader/association.rb +51 -53
  29. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  30. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  31. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  32. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  33. data/lib/active_record/associations/preloader.rb +18 -8
  34. data/lib/active_record/associations/singular_association.rb +8 -8
  35. data/lib/active_record/associations/through_association.rb +22 -9
  36. data/lib/active_record/associations.rb +321 -212
  37. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  38. data/lib/active_record/attribute.rb +79 -15
  39. data/lib/active_record/attribute_assignment.rb +20 -141
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +6 -1
  42. data/lib/active_record/attribute_methods/dirty.rb +51 -81
  43. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  44. data/lib/active_record/attribute_methods/query.rb +2 -2
  45. data/lib/active_record/attribute_methods/read.rb +31 -59
  46. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -14
  48. data/lib/active_record/attribute_methods/write.rb +14 -38
  49. data/lib/active_record/attribute_methods.rb +70 -45
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set/builder.rb +37 -15
  52. data/lib/active_record/attribute_set.rb +34 -3
  53. data/lib/active_record/attributes.rb +199 -73
  54. data/lib/active_record/autosave_association.rb +73 -25
  55. data/lib/active_record/base.rb +35 -27
  56. data/lib/active_record/callbacks.rb +39 -43
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +20 -8
  59. data/lib/active_record/collection_cache_key.rb +40 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +457 -181
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -59
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -3
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
  65. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -4
  66. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +246 -185
  68. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  69. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +438 -136
  70. data/lib/active_record/connection_adapters/abstract/transaction.rb +53 -40
  71. data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -66
  72. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +429 -335
  73. data/lib/active_record/connection_adapters/column.rb +28 -43
  74. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  75. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  78. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  79. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  83. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -177
  85. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  86. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +11 -73
  87. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -13
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  95. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  98. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  99. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  101. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +17 -5
  102. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  103. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  106. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  107. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  108. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  109. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +248 -154
  111. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  112. data/lib/active_record/connection_adapters/postgresql_adapter.rb +258 -170
  113. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  114. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  115. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  116. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +150 -209
  119. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  120. data/lib/active_record/connection_handling.rb +38 -15
  121. data/lib/active_record/core.rb +109 -114
  122. data/lib/active_record/counter_cache.rb +14 -25
  123. data/lib/active_record/dynamic_matchers.rb +1 -20
  124. data/lib/active_record/enum.rb +115 -79
  125. data/lib/active_record/errors.rb +88 -48
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +2 -2
  128. data/lib/active_record/fixture_set/file.rb +26 -5
  129. data/lib/active_record/fixtures.rb +84 -46
  130. data/lib/active_record/gem_version.rb +2 -2
  131. data/lib/active_record/inheritance.rb +32 -40
  132. data/lib/active_record/integration.rb +4 -4
  133. data/lib/active_record/internal_metadata.rb +56 -0
  134. data/lib/active_record/legacy_yaml_adapter.rb +46 -0
  135. data/lib/active_record/locale/en.yml +3 -2
  136. data/lib/active_record/locking/optimistic.rb +27 -25
  137. data/lib/active_record/locking/pessimistic.rb +1 -1
  138. data/lib/active_record/log_subscriber.rb +43 -21
  139. data/lib/active_record/migration/command_recorder.rb +59 -18
  140. data/lib/active_record/migration/compatibility.rb +126 -0
  141. data/lib/active_record/migration.rb +372 -114
  142. data/lib/active_record/model_schema.rb +128 -38
  143. data/lib/active_record/nested_attributes.rb +71 -32
  144. data/lib/active_record/no_touching.rb +1 -1
  145. data/lib/active_record/null_relation.rb +16 -8
  146. data/lib/active_record/persistence.rb +124 -80
  147. data/lib/active_record/query_cache.rb +15 -18
  148. data/lib/active_record/querying.rb +10 -9
  149. data/lib/active_record/railtie.rb +28 -19
  150. data/lib/active_record/railties/controller_runtime.rb +1 -1
  151. data/lib/active_record/railties/databases.rake +67 -51
  152. data/lib/active_record/readonly_attributes.rb +1 -1
  153. data/lib/active_record/reflection.rb +318 -139
  154. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  155. data/lib/active_record/relation/batches.rb +139 -34
  156. data/lib/active_record/relation/calculations.rb +80 -102
  157. data/lib/active_record/relation/delegation.rb +7 -20
  158. data/lib/active_record/relation/finder_methods.rb +167 -97
  159. data/lib/active_record/relation/from_clause.rb +32 -0
  160. data/lib/active_record/relation/merger.rb +38 -41
  161. data/lib/active_record/relation/predicate_builder/array_handler.rb +12 -16
  162. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  163. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  164. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  165. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  166. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  167. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  168. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  169. data/lib/active_record/relation/predicate_builder.rb +124 -82
  170. data/lib/active_record/relation/query_attribute.rb +19 -0
  171. data/lib/active_record/relation/query_methods.rb +323 -257
  172. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  173. data/lib/active_record/relation/spawn_methods.rb +11 -10
  174. data/lib/active_record/relation/where_clause.rb +174 -0
  175. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  176. data/lib/active_record/relation.rb +176 -115
  177. data/lib/active_record/result.rb +4 -3
  178. data/lib/active_record/runtime_registry.rb +1 -1
  179. data/lib/active_record/sanitization.rb +95 -66
  180. data/lib/active_record/schema.rb +26 -22
  181. data/lib/active_record/schema_dumper.rb +62 -38
  182. data/lib/active_record/schema_migration.rb +11 -17
  183. data/lib/active_record/scoping/default.rb +24 -9
  184. data/lib/active_record/scoping/named.rb +49 -28
  185. data/lib/active_record/scoping.rb +32 -15
  186. data/lib/active_record/secure_token.rb +38 -0
  187. data/lib/active_record/serialization.rb +2 -4
  188. data/lib/active_record/statement_cache.rb +16 -14
  189. data/lib/active_record/store.rb +8 -3
  190. data/lib/active_record/suppressor.rb +58 -0
  191. data/lib/active_record/table_metadata.rb +68 -0
  192. data/lib/active_record/tasks/database_tasks.rb +59 -42
  193. data/lib/active_record/tasks/mysql_database_tasks.rb +32 -26
  194. data/lib/active_record/tasks/postgresql_database_tasks.rb +29 -9
  195. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  196. data/lib/active_record/timestamp.rb +20 -9
  197. data/lib/active_record/touch_later.rb +58 -0
  198. data/lib/active_record/transactions.rb +159 -67
  199. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  200. data/lib/active_record/type/date.rb +2 -41
  201. data/lib/active_record/type/date_time.rb +2 -38
  202. data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
  203. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  204. data/lib/active_record/type/internal/timezone.rb +15 -0
  205. data/lib/active_record/type/serialized.rb +21 -14
  206. data/lib/active_record/type/time.rb +10 -16
  207. data/lib/active_record/type/type_map.rb +4 -4
  208. data/lib/active_record/type.rb +66 -17
  209. data/lib/active_record/type_caster/connection.rb +29 -0
  210. data/lib/active_record/type_caster/map.rb +19 -0
  211. data/lib/active_record/type_caster.rb +7 -0
  212. data/lib/active_record/validations/absence.rb +23 -0
  213. data/lib/active_record/validations/associated.rb +10 -3
  214. data/lib/active_record/validations/length.rb +24 -0
  215. data/lib/active_record/validations/presence.rb +11 -12
  216. data/lib/active_record/validations/uniqueness.rb +29 -18
  217. data/lib/active_record/validations.rb +33 -32
  218. data/lib/active_record.rb +9 -2
  219. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  220. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -6
  221. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -7
  222. data/lib/rails/generators/active_record/migration.rb +7 -0
  223. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  224. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  225. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  226. metadata +60 -34
  227. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  228. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  229. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  231. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  232. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  233. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  234. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  235. data/lib/active_record/type/big_integer.rb +0 -13
  236. data/lib/active_record/type/binary.rb +0 -50
  237. data/lib/active_record/type/boolean.rb +0 -30
  238. data/lib/active_record/type/decimal.rb +0 -40
  239. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  240. data/lib/active_record/type/decorator.rb +0 -14
  241. data/lib/active_record/type/float.rb +0 -19
  242. data/lib/active_record/type/integer.rb +0 -55
  243. data/lib/active_record/type/mutable.rb +0 -16
  244. data/lib/active_record/type/numeric.rb +0 -36
  245. data/lib/active_record/type/string.rb +0 -36
  246. data/lib/active_record/type/text.rb +0 -11
  247. data/lib/active_record/type/time_value.rb +0 -38
  248. data/lib/active_record/type/unsigned_integer.rb +0 -15
  249. data/lib/active_record/type/value.rb +0 -101
@@ -1,4 +1,6 @@
1
1
  require 'active_record/migration/join_table'
2
+ require 'active_support/core_ext/string/access'
3
+ require 'digest'
2
4
 
3
5
  module ActiveRecord
4
6
  module ConnectionAdapters # :nodoc:
@@ -12,11 +14,39 @@ module ActiveRecord
12
14
  {}
13
15
  end
14
16
 
17
+ def table_options(table_name)
18
+ nil
19
+ end
20
+
21
+ # Returns the table comment that's stored in database metadata.
22
+ def table_comment(table_name)
23
+ nil
24
+ end
25
+
15
26
  # Truncates a table alias according to the limits of the current adapter.
16
27
  def table_alias_for(table_name)
17
28
  table_name[0...table_alias_length].tr('.', '_')
18
29
  end
19
30
 
31
+ # Returns the relation names useable to back Active Record models.
32
+ # For most adapters this means all #tables and #views.
33
+ def data_sources
34
+ tables | views
35
+ end
36
+
37
+ # Checks to see if the data source +name+ exists on the database.
38
+ #
39
+ # data_source_exists?(:ebooks)
40
+ #
41
+ def data_source_exists?(name)
42
+ data_sources.include?(name.to_s)
43
+ end
44
+
45
+ # Returns an array of table names defined in the database.
46
+ def tables(name = nil)
47
+ raise NotImplementedError, "#tables is not implemented"
48
+ end
49
+
20
50
  # Checks to see if the table +table_name+ exists on the database.
21
51
  #
22
52
  # table_exists?(:developers)
@@ -25,6 +55,19 @@ module ActiveRecord
25
55
  tables.include?(table_name.to_s)
26
56
  end
27
57
 
58
+ # Returns an array of view names defined in the database.
59
+ def views
60
+ raise NotImplementedError, "#views is not implemented"
61
+ end
62
+
63
+ # Checks to see if the view +view_name+ exists on the database.
64
+ #
65
+ # view_exists?(:ebooks)
66
+ #
67
+ def view_exists?(view_name)
68
+ views.include?(view_name.to_s)
69
+ end
70
+
28
71
  # Returns an array of indexes for the given table.
29
72
  # def indexes(table_name, name = nil) end
30
73
 
@@ -44,18 +87,19 @@ module ActiveRecord
44
87
  #
45
88
  def index_exists?(table_name, column_name, options = {})
46
89
  column_names = Array(column_name).map(&:to_s)
47
- index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, column: column_names)
48
90
  checks = []
49
- checks << lambda { |i| i.name == index_name }
50
91
  checks << lambda { |i| i.columns == column_names }
51
92
  checks << lambda { |i| i.unique } if options[:unique]
93
+ checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
52
94
 
53
95
  indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
54
96
  end
55
97
 
56
98
  # Returns an array of Column objects for the table specified by +table_name+.
57
99
  # See the concrete implementation for details on the expected parameter values.
58
- def columns(table_name) end
100
+ def columns(table_name)
101
+ raise NotImplementedError, "#columns is not implemented"
102
+ end
59
103
 
60
104
  # Checks to see if a column exists in a given table.
61
105
  #
@@ -73,19 +117,32 @@ module ActiveRecord
73
117
  #
74
118
  def column_exists?(table_name, column_name, type = nil, options = {})
75
119
  column_name = column_name.to_s
76
- columns(table_name).any?{ |c| c.name == column_name &&
77
- (!type || c.type == type) &&
78
- (!options.key?(:limit) || c.limit == options[:limit]) &&
79
- (!options.key?(:precision) || c.precision == options[:precision]) &&
80
- (!options.key?(:scale) || c.scale == options[:scale]) &&
81
- (!options.key?(:default) || c.default == options[:default]) &&
82
- (!options.key?(:null) || c.null == options[:null]) }
120
+ checks = []
121
+ checks << lambda { |c| c.name == column_name }
122
+ checks << lambda { |c| c.type == type } if type
123
+ (migration_keys - [:name]).each do |attr|
124
+ checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
125
+ end
126
+
127
+ columns(table_name).any? { |c| checks.all? { |check| check[c] } }
128
+ end
129
+
130
+ # Returns just a table's primary key
131
+ def primary_key(table_name)
132
+ pks = primary_keys(table_name)
133
+ warn <<-WARNING.strip_heredoc if pks.count > 1
134
+ WARNING: Rails does not support composite primary key.
135
+
136
+ #{table_name} has composite primary key. Composite primary key is ignored.
137
+ WARNING
138
+
139
+ pks.first if pks.one?
83
140
  end
84
141
 
85
142
  # Creates a new table with the name +table_name+. +table_name+ may either
86
143
  # be a String or a Symbol.
87
144
  #
88
- # There are two ways to work with +create_table+. You can use the block
145
+ # There are two ways to work with #create_table. You can use the block
89
146
  # form or the regular form, like this:
90
147
  #
91
148
  # === Block form
@@ -117,13 +174,16 @@ module ActiveRecord
117
174
  # The +options+ hash can include the following keys:
118
175
  # [<tt>:id</tt>]
119
176
  # Whether to automatically add a primary key column. Defaults to true.
120
- # Join tables for +has_and_belongs_to_many+ should set it to false.
177
+ # Join tables for {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many] should set it to false.
178
+ #
179
+ # A Symbol can be used to specify the type of the generated primary key column.
121
180
  # [<tt>:primary_key</tt>]
122
181
  # The name of the primary key, if one is to be added automatically.
123
182
  # Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
124
183
  #
125
184
  # Note that Active Record models will automatically detect their
126
- # primary key. This can be avoided by using +self.primary_key=+ on the model
185
+ # primary key. This can be avoided by using
186
+ # {self.primary_key=}[rdoc-ref:AttributeMethods::PrimaryKey::ClassMethods#primary_key=] on the model
127
187
  # to define the key explicitly.
128
188
  #
129
189
  # [<tt>:options</tt>]
@@ -145,7 +205,7 @@ module ActiveRecord
145
205
  # generates:
146
206
  #
147
207
  # CREATE TABLE suppliers (
148
- # id int(11) DEFAULT NULL auto_increment PRIMARY KEY
208
+ # id int auto_increment PRIMARY KEY
149
209
  # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
150
210
  #
151
211
  # ====== Rename the primary key column
@@ -157,10 +217,23 @@ module ActiveRecord
157
217
  # generates:
158
218
  #
159
219
  # CREATE TABLE objects (
160
- # guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
220
+ # guid int auto_increment PRIMARY KEY,
161
221
  # name varchar(80)
162
222
  # )
163
223
  #
224
+ # ====== Change the primary key column type
225
+ #
226
+ # create_table(:tags, id: :string) do |t|
227
+ # t.column :label, :string
228
+ # end
229
+ #
230
+ # generates:
231
+ #
232
+ # CREATE TABLE tags (
233
+ # id varchar PRIMARY KEY,
234
+ # label varchar
235
+ # )
236
+ #
164
237
  # ====== Do not add a primary key column
165
238
  #
166
239
  # create_table(:categories_suppliers, id: false) do |t|
@@ -186,25 +259,43 @@ module ActiveRecord
186
259
  # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
187
260
  #
188
261
  # See also TableDefinition#column for details on how to create columns.
189
- def create_table(table_name, options = {})
190
- td = create_table_definition table_name, options[:temporary], options[:options], options[:as]
262
+ def create_table(table_name, comment: nil, **options)
263
+ td = create_table_definition table_name, options[:temporary], options[:options], options[:as], comment: comment
191
264
 
192
265
  if options[:id] != false && !options[:as]
193
266
  pk = options.fetch(:primary_key) do
194
267
  Base.get_primary_key table_name.to_s.singularize
195
268
  end
196
269
 
197
- td.primary_key pk, options.fetch(:id, :primary_key), options
270
+ if pk.is_a?(Array)
271
+ td.primary_keys pk
272
+ else
273
+ td.primary_key pk, options.fetch(:id, :primary_key), options
274
+ end
198
275
  end
199
276
 
200
277
  yield td if block_given?
201
278
 
202
- if options[:force] && table_exists?(table_name)
279
+ if options[:force] && data_source_exists?(table_name)
203
280
  drop_table(table_name, options)
204
281
  end
205
282
 
206
283
  result = execute schema_creation.accept td
207
- td.indexes.each_pair { |c, o| add_index(table_name, c, o) } unless supports_indexes_in_create?
284
+
285
+ unless supports_indexes_in_create?
286
+ td.indexes.each_pair do |column_name, index_options|
287
+ add_index(table_name, column_name, index_options)
288
+ end
289
+ end
290
+
291
+ if supports_comments? && !supports_comments_in_create?
292
+ change_table_comment(table_name, comment) if comment
293
+
294
+ td.columns.each do |column|
295
+ change_column_comment(table_name, column.name, column.comment) if column.comment
296
+ end
297
+ end
298
+
208
299
  result
209
300
  end
210
301
 
@@ -227,7 +318,7 @@ module ActiveRecord
227
318
  # Set to true to drop the table before creating it.
228
319
  # Defaults to false.
229
320
  #
230
- # Note that +create_join_table+ does not create any indices by default; you can use
321
+ # Note that #create_join_table does not create any indices by default; you can use
231
322
  # its block form to do so yourself:
232
323
  #
233
324
  # create_join_table :products, :categories do |t|
@@ -251,22 +342,23 @@ module ActiveRecord
251
342
 
252
343
  column_options = options.delete(:column_options) || {}
253
344
  column_options.reverse_merge!(null: false)
345
+ type = column_options.delete(:type) || :integer
254
346
 
255
347
  t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
256
348
 
257
349
  create_table(join_table_name, options.merge!(id: false)) do |td|
258
- td.integer t1_column, column_options
259
- td.integer t2_column, column_options
350
+ td.send type, t1_column, column_options
351
+ td.send type, t2_column, column_options
260
352
  yield td if block_given?
261
353
  end
262
354
  end
263
355
 
264
356
  # Drops the join table specified by the given arguments.
265
- # See +create_join_table+ for details.
357
+ # See #create_join_table for details.
266
358
  #
267
359
  # Although this command ignores the block if one is given, it can be helpful
268
360
  # to provide one in a migration's +change+ method so it can be reverted.
269
- # In that case, the block will be used by create_join_table.
361
+ # In that case, the block will be used by #create_join_table.
270
362
  def drop_join_table(table_1, table_2, options = {})
271
363
  join_table_name = find_join_table_name(table_1, table_2, options)
272
364
  drop_table(join_table_name)
@@ -284,7 +376,7 @@ module ActiveRecord
284
376
  # [<tt>:bulk</tt>]
285
377
  # Set this to true to make this a bulk alter query, such as
286
378
  #
287
- # ALTER TABLE `users` ADD COLUMN age INT(11), ADD COLUMN birthdate DATETIME ...
379
+ # ALTER TABLE `users` ADD COLUMN age INT, ADD COLUMN birthdate DATETIME ...
288
380
  #
289
381
  # Defaults to false.
290
382
  #
@@ -365,16 +457,90 @@ module ActiveRecord
365
457
  # [<tt>:force</tt>]
366
458
  # Set to +:cascade+ to drop dependent objects as well.
367
459
  # Defaults to false.
460
+ # [<tt>:if_exists</tt>]
461
+ # Set to +true+ to only drop the table if it exists.
462
+ # Defaults to false.
368
463
  #
369
464
  # Although this command ignores most +options+ and the block if one is given,
370
465
  # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
371
- # In that case, +options+ and the block will be used by create_table.
466
+ # In that case, +options+ and the block will be used by #create_table.
372
467
  def drop_table(table_name, options = {})
373
- execute "DROP TABLE #{quote_table_name(table_name)}"
468
+ execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
374
469
  end
375
470
 
376
- # Adds a new column to the named table.
377
- # See TableDefinition#column for details of the options you can use.
471
+ # Add a new +type+ column named +column_name+ to +table_name+.
472
+ #
473
+ # The +type+ parameter is normally one of the migrations native types,
474
+ # which is one of the following:
475
+ # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
476
+ # <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
477
+ # <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
478
+ # <tt>:binary</tt>, <tt>:boolean</tt>.
479
+ #
480
+ # You may use a type not in this list as long as it is supported by your
481
+ # database (for example, "polygon" in MySQL), but this will not be database
482
+ # agnostic and should usually be avoided.
483
+ #
484
+ # Available options are (none of these exists by default):
485
+ # * <tt>:limit</tt> -
486
+ # Requests a maximum column length. This is number of characters for a <tt>:string</tt> column
487
+ # and number of bytes for <tt>:text</tt>, <tt>:binary</tt> and <tt>:integer</tt> columns.
488
+ # * <tt>:default</tt> -
489
+ # The column's default value. Use nil for NULL.
490
+ # * <tt>:null</tt> -
491
+ # Allows or disallows +NULL+ values in the column. This option could
492
+ # have been named <tt>:null_allowed</tt>.
493
+ # * <tt>:precision</tt> -
494
+ # Specifies the precision for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
495
+ # * <tt>:scale</tt> -
496
+ # Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
497
+ #
498
+ # Note: The precision is the total number of significant digits
499
+ # and the scale is the number of digits that can be stored following
500
+ # the decimal point. For example, the number 123.45 has a precision of 5
501
+ # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
502
+ # range from -999.99 to 999.99.
503
+ #
504
+ # Please be aware of different RDBMS implementations behavior with
505
+ # <tt>:decimal</tt> columns:
506
+ # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
507
+ # <tt>:precision</tt>, and makes no comments about the requirements of
508
+ # <tt>:precision</tt>.
509
+ # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
510
+ # Default is (10,0).
511
+ # * PostgreSQL: <tt>:precision</tt> [1..infinity],
512
+ # <tt>:scale</tt> [0..infinity]. No default.
513
+ # * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
514
+ # but the maximum supported <tt>:precision</tt> is 16. No default.
515
+ # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
516
+ # Default is (38,0).
517
+ # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
518
+ # Default unknown.
519
+ # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
520
+ # Default (38,0).
521
+ #
522
+ # == Examples
523
+ #
524
+ # add_column(:users, :picture, :binary, limit: 2.megabytes)
525
+ # # ALTER TABLE "users" ADD "picture" blob(2097152)
526
+ #
527
+ # add_column(:articles, :status, :string, limit: 20, default: 'draft', null: false)
528
+ # # ALTER TABLE "articles" ADD "status" varchar(20) DEFAULT 'draft' NOT NULL
529
+ #
530
+ # add_column(:answers, :bill_gates_money, :decimal, precision: 15, scale: 2)
531
+ # # ALTER TABLE "answers" ADD "bill_gates_money" decimal(15,2)
532
+ #
533
+ # add_column(:measurements, :sensor_reading, :decimal, precision: 30, scale: 20)
534
+ # # ALTER TABLE "measurements" ADD "sensor_reading" decimal(30,20)
535
+ #
536
+ # # While :scale defaults to zero on most databases, it
537
+ # # probably wouldn't hurt to include it.
538
+ # add_column(:measurements, :huge_integer, :decimal, precision: 30)
539
+ # # ALTER TABLE "measurements" ADD "huge_integer" decimal(30)
540
+ #
541
+ # # Defines a column with a database-specific type.
542
+ # add_column(:shapes, :triangle, 'polygon')
543
+ # # ALTER TABLE "shapes" ADD "triangle" polygon
378
544
  def add_column(table_name, column_name, type, options = {})
379
545
  at = create_alter_table table_name
380
546
  at.add_column(column_name, type, options)
@@ -422,11 +588,16 @@ module ActiveRecord
422
588
  #
423
589
  # change_column_default(:users, :email, nil)
424
590
  #
425
- def change_column_default(table_name, column_name, default)
591
+ # Passing a hash containing +:from+ and +:to+ will make this change
592
+ # reversible in migration:
593
+ #
594
+ # change_column_default(:posts, :state, from: nil, to: "draft")
595
+ #
596
+ def change_column_default(table_name, column_name, default_or_changes)
426
597
  raise NotImplementedError, "change_column_default is not implemented"
427
598
  end
428
599
 
429
- # Sets or removes a +NOT NULL+ constraint on a column. The +null+ flag
600
+ # Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
430
601
  # indicates whether the value can be +NULL+. For example
431
602
  #
432
603
  # change_column_null(:users, :nickname, false)
@@ -438,7 +609,7 @@ module ActiveRecord
438
609
  # allows them to be +NULL+ (drops the constraint).
439
610
  #
440
611
  # The method accepts an optional fourth argument to replace existing
441
- # +NULL+s with some other value. Use that one when enabling the
612
+ # <tt>NULL</tt>s with some other value. Use that one when enabling the
442
613
  # constraint if needed, since otherwise those rows would not be valid.
443
614
  #
444
615
  # Please note the fourth argument does not set a column's default.
@@ -492,6 +663,8 @@ module ActiveRecord
492
663
  #
493
664
  # CREATE INDEX by_name ON accounts(name(10))
494
665
  #
666
+ # ====== Creating an index with specific key lengths for multiple keys
667
+ #
495
668
  # add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
496
669
  #
497
670
  # generates:
@@ -518,6 +691,8 @@ module ActiveRecord
518
691
  #
519
692
  # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
520
693
  #
694
+ # Note: Partial indexes are only supported for PostgreSQL and SQLite 3.8.0+.
695
+ #
521
696
  # ====== Creating an index with a specific method
522
697
  #
523
698
  # add_index(:developers, :name, using: 'btree')
@@ -537,7 +712,7 @@ module ActiveRecord
537
712
  #
538
713
  # CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
539
714
  #
540
- # Note: only supported by MySQL. Supported: <tt>:fulltext</tt> and <tt>:spatial</tt> on MyISAM tables.
715
+ # Note: only supported by MySQL.
541
716
  def add_index(table_name, column_name, options = {})
542
717
  index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
543
718
  execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
@@ -545,15 +720,15 @@ module ActiveRecord
545
720
 
546
721
  # Removes the given index from the table.
547
722
  #
548
- # Removes the +index_accounts_on_column+ in the +accounts+ table.
723
+ # Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
549
724
  #
550
- # remove_index :accounts, :column
725
+ # remove_index :accounts, :branch_id
551
726
  #
552
- # Removes the index named +index_accounts_on_branch_id+ in the +accounts+ table.
727
+ # Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
553
728
  #
554
729
  # remove_index :accounts, column: :branch_id
555
730
  #
556
- # Removes the index named +index_accounts_on_branch_id_and_party_id+ in the +accounts+ table.
731
+ # Removes the index on +branch_id+ and +party_id+ in the +accounts+ table if exactly one such index exists.
557
732
  #
558
733
  # remove_index :accounts, column: [:branch_id, :party_id]
559
734
  #
@@ -562,10 +737,7 @@ module ActiveRecord
562
737
  # remove_index :accounts, name: :by_branch_party
563
738
  #
564
739
  def remove_index(table_name, options = {})
565
- remove_index!(table_name, index_name_for_remove(table_name, options))
566
- end
567
-
568
- def remove_index!(table_name, index_name) #:nodoc:
740
+ index_name = index_name_for_remove(table_name, options)
569
741
  execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
570
742
  end
571
743
 
@@ -576,9 +748,8 @@ module ActiveRecord
576
748
  # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
577
749
  #
578
750
  def rename_index(table_name, old_name, new_name)
579
- if new_name.length > allowed_index_name_length
580
- raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
581
- end
751
+ validate_index_length!(table_name, new_name)
752
+
582
753
  # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
583
754
  old_index_def = indexes(table_name).detect { |i| i.name == old_name }
584
755
  return unless old_index_def
@@ -610,10 +781,23 @@ module ActiveRecord
610
781
  indexes(table_name).detect { |i| i.name == index_name }
611
782
  end
612
783
 
613
- # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
614
- # The reference column is an +integer+ by default, the <tt>:type</tt> option can be used to specify
615
- # a different type.
616
- # <tt>add_reference</tt> and <tt>add_belongs_to</tt> are acceptable.
784
+ # Adds a reference. The reference column is an integer by default,
785
+ # the <tt>:type</tt> option can be used to specify a different type.
786
+ # Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
787
+ # #add_reference and #add_belongs_to are acceptable.
788
+ #
789
+ # The +options+ hash can include the following keys:
790
+ # [<tt>:type</tt>]
791
+ # The reference column type. Defaults to +:integer+.
792
+ # [<tt>:index</tt>]
793
+ # Add an appropriate index. Defaults to false.
794
+ # See #add_index for usage of this option.
795
+ # [<tt>:foreign_key</tt>]
796
+ # Add an appropriate foreign key constraint. Defaults to false.
797
+ # [<tt>:polymorphic</tt>]
798
+ # Whether an additional +_type+ column should be added. Defaults to false.
799
+ # [<tt>:null</tt>]
800
+ # Whether the column allows nulls. Defaults to true.
617
801
  #
618
802
  # ====== Create a user_id integer column
619
803
  #
@@ -623,26 +807,33 @@ module ActiveRecord
623
807
  #
624
808
  # add_reference(:products, :user, type: :string)
625
809
  #
626
- # ====== Create a supplier_id and supplier_type columns
810
+ # ====== Create supplier_id, supplier_type columns and appropriate index
811
+ #
812
+ # add_reference(:products, :supplier, polymorphic: true, index: true)
627
813
  #
628
- # add_belongs_to(:products, :supplier, polymorphic: true)
814
+ # ====== Create a supplier_id column with a unique index
629
815
  #
630
- # ====== Create a supplier_id, supplier_type columns and appropriate index
816
+ # add_reference(:products, :supplier, index: { unique: true })
631
817
  #
632
- # add_reference(:products, :supplier, polymorphic: true, index: true)
818
+ # ====== Create a supplier_id column with a named index
633
819
  #
634
- def add_reference(table_name, ref_name, options = {})
635
- polymorphic = options.delete(:polymorphic)
636
- index_options = options.delete(:index)
637
- type = options.delete(:type) || :integer
638
- add_column(table_name, "#{ref_name}_id", type, options)
639
- add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
640
- add_index(table_name, polymorphic ? %w[type id].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
820
+ # add_reference(:products, :supplier, index: { name: "my_supplier_index" })
821
+ #
822
+ # ====== Create a supplier_id column and appropriate foreign key
823
+ #
824
+ # add_reference(:products, :supplier, foreign_key: true)
825
+ #
826
+ # ====== Create a supplier_id column and a foreign key to the firms table
827
+ #
828
+ # add_reference(:products, :supplier, foreign_key: {to_table: :firms})
829
+ #
830
+ def add_reference(table_name, *args)
831
+ ReferenceDefinition.new(*args).add_to(update_table_definition(table_name, self))
641
832
  end
642
833
  alias :add_belongs_to :add_reference
643
834
 
644
835
  # Removes the reference(s). Also removes a +type+ column if one exists.
645
- # <tt>remove_reference</tt>, <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
836
+ # #remove_reference and #remove_belongs_to are acceptable.
646
837
  #
647
838
  # ====== Remove the reference
648
839
  #
@@ -652,14 +843,23 @@ module ActiveRecord
652
843
  #
653
844
  # remove_reference(:products, :supplier, polymorphic: true)
654
845
  #
846
+ # ====== Remove the reference with a foreign key
847
+ #
848
+ # remove_reference(:products, :user, index: true, foreign_key: true)
849
+ #
655
850
  def remove_reference(table_name, ref_name, options = {})
851
+ if options[:foreign_key]
852
+ reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
853
+ remove_foreign_key(table_name, reference_name)
854
+ end
855
+
656
856
  remove_column(table_name, "#{ref_name}_id")
657
857
  remove_column(table_name, "#{ref_name}_type") if options[:polymorphic]
658
858
  end
659
859
  alias :remove_belongs_to :remove_reference
660
860
 
661
861
  # Returns an array of foreign keys for the given table.
662
- # The foreign keys are represented as +ForeignKeyDefinition+ objects.
862
+ # The foreign keys are represented as ForeignKeyDefinition objects.
663
863
  def foreign_keys(table_name)
664
864
  raise NotImplementedError, "foreign_keys is not implemented"
665
865
  end
@@ -668,8 +868,8 @@ module ActiveRecord
668
868
  # +to_table+ contains the referenced primary key.
669
869
  #
670
870
  # The foreign key will be named after the following pattern: <tt>fk_rails_<identifier></tt>.
671
- # +identifier+ is a 10 character long random string. A custom name can be specified with
672
- # the <tt>:name</tt> option.
871
+ # +identifier+ is a 10 character long string which is deterministically generated from the
872
+ # +from_table+ and +column+. A custom name can be specified with the <tt>:name</tt> option.
673
873
  #
674
874
  # ====== Creating a simple foreign key
675
875
  #
@@ -677,7 +877,7 @@ module ActiveRecord
677
877
  #
678
878
  # generates:
679
879
  #
680
- # ALTER TABLE "articles" ADD CONSTRAINT articles_author_id_fk FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
880
+ # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
681
881
  #
682
882
  # ====== Creating a foreign key on a specific column
683
883
  #
@@ -693,7 +893,7 @@ module ActiveRecord
693
893
  #
694
894
  # generates:
695
895
  #
696
- # ALTER TABLE "articles" ADD CONSTRAINT articles_author_id_fk FOREIGN KEY ("author_id") REFERENCES "authors" ("id") ON DELETE CASCADE
896
+ # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id") ON DELETE CASCADE
697
897
  #
698
898
  # The +options+ hash can include the following keys:
699
899
  # [<tt>:column</tt>]
@@ -703,28 +903,23 @@ module ActiveRecord
703
903
  # [<tt>:name</tt>]
704
904
  # The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
705
905
  # [<tt>:on_delete</tt>]
706
- # Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade:+ and +:restrict+
906
+ # Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
707
907
  # [<tt>:on_update</tt>]
708
- # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade:+ and +:restrict+
908
+ # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
709
909
  def add_foreign_key(from_table, to_table, options = {})
710
910
  return unless supports_foreign_keys?
711
911
 
712
- options[:column] ||= foreign_key_column_for(to_table)
713
-
714
- options = {
715
- column: options[:column],
716
- primary_key: options[:primary_key],
717
- name: foreign_key_name(from_table, options),
718
- on_delete: options[:on_delete],
719
- on_update: options[:on_update]
720
- }
912
+ options = foreign_key_options(from_table, to_table, options)
721
913
  at = create_alter_table from_table
722
914
  at.add_foreign_key to_table, options
723
915
 
724
916
  execute schema_creation.accept(at)
725
917
  end
726
918
 
727
- # Removes the given foreign key from the table.
919
+ # Removes the given foreign key from the table. Any option parameters provided
920
+ # will be used to re-add the foreign key in case of a migration rollback.
921
+ # It is recommended that you provide any options used when creating the foreign
922
+ # key so that the migration can be reverted properly.
728
923
  #
729
924
  # Removes the foreign key on +accounts.branch_id+.
730
925
  #
@@ -738,24 +933,11 @@ module ActiveRecord
738
933
  #
739
934
  # remove_foreign_key :accounts, name: :special_fk_name
740
935
  #
936
+ # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
741
937
  def remove_foreign_key(from_table, options_or_to_table = {})
742
938
  return unless supports_foreign_keys?
743
939
 
744
- if options_or_to_table.is_a?(Hash)
745
- options = options_or_to_table
746
- else
747
- options = { column: foreign_key_column_for(options_or_to_table) }
748
- end
749
-
750
- fk_name_to_delete = options.fetch(:name) do
751
- fk_to_delete = foreign_keys(from_table).detect {|fk| fk.column == options[:column].to_s }
752
-
753
- if fk_to_delete
754
- fk_to_delete.name
755
- else
756
- raise ArgumentError, "Table '#{from_table}' has no foreign key on column '#{options[:column]}'"
757
- end
758
- end
940
+ fk_name_to_delete = foreign_key_for!(from_table, options_or_to_table).name
759
941
 
760
942
  at = create_alter_table from_table
761
943
  at.drop_foreign_key fk_name_to_delete
@@ -763,16 +945,63 @@ module ActiveRecord
763
945
  execute schema_creation.accept(at)
764
946
  end
765
947
 
948
+ # Checks to see if a foreign key exists on a table for a given foreign key definition.
949
+ #
950
+ # # Check a foreign key exists
951
+ # foreign_key_exists?(:accounts, :branches)
952
+ #
953
+ # # Check a foreign key on a specified column exists
954
+ # foreign_key_exists?(:accounts, column: :owner_id)
955
+ #
956
+ # # Check a foreign key with a custom name exists
957
+ # foreign_key_exists?(:accounts, name: "special_fk_name")
958
+ #
959
+ def foreign_key_exists?(from_table, options_or_to_table = {})
960
+ foreign_key_for(from_table, options_or_to_table).present?
961
+ end
962
+
963
+ def foreign_key_for(from_table, options_or_to_table = {}) # :nodoc:
964
+ return unless supports_foreign_keys?
965
+ foreign_keys(from_table).detect {|fk| fk.defined_for? options_or_to_table }
966
+ end
967
+
968
+ def foreign_key_for!(from_table, options_or_to_table = {}) # :nodoc:
969
+ foreign_key_for(from_table, options_or_to_table) or \
970
+ raise ArgumentError, "Table '#{from_table}' has no foreign key for #{options_or_to_table}"
971
+ end
972
+
766
973
  def foreign_key_column_for(table_name) # :nodoc:
767
- "#{table_name.to_s.singularize}_id"
974
+ prefix = Base.table_name_prefix
975
+ suffix = Base.table_name_suffix
976
+ name = table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
977
+ "#{name.singularize}_id"
978
+ end
979
+
980
+ def foreign_key_options(from_table, to_table, options) # :nodoc:
981
+ options = options.dup
982
+ options[:column] ||= foreign_key_column_for(to_table)
983
+ options[:name] ||= foreign_key_name(from_table, options)
984
+ options
768
985
  end
769
986
 
770
987
  def dump_schema_information #:nodoc:
988
+ versions = ActiveRecord::SchemaMigration.order('version').pluck(:version)
989
+ insert_versions_sql(versions)
990
+ end
991
+
992
+ def insert_versions_sql(versions) # :nodoc:
771
993
  sm_table = ActiveRecord::Migrator.schema_migrations_table_name
772
994
 
773
- ActiveRecord::SchemaMigration.order('version').map { |sm|
774
- "INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');"
775
- }.join "\n\n"
995
+ if supports_multi_insert?
996
+ sql = "INSERT INTO #{sm_table} (version) VALUES "
997
+ sql << versions.map {|v| "('#{v}')" }.join(', ')
998
+ sql << ";\n\n"
999
+ sql
1000
+ else
1001
+ versions.map { |version|
1002
+ "INSERT INTO #{sm_table} (version) VALUES ('#{version}');"
1003
+ }.join "\n\n"
1004
+ end
776
1005
  end
777
1006
 
778
1007
  # Should not be called normally, but this operation is non-destructive.
@@ -781,12 +1010,20 @@ module ActiveRecord
781
1010
  ActiveRecord::SchemaMigration.create_table
782
1011
  end
783
1012
 
784
- def assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths)
1013
+ def initialize_internal_metadata_table
1014
+ ActiveRecord::InternalMetadata.create_table
1015
+ end
1016
+
1017
+ def internal_string_options_for_primary_key # :nodoc:
1018
+ { primary_key: true }
1019
+ end
1020
+
1021
+ def assume_migrated_upto_version(version, migrations_paths)
785
1022
  migrations_paths = Array(migrations_paths)
786
1023
  version = version.to_i
787
1024
  sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
788
1025
 
789
- migrated = select_values("SELECT version FROM #{sm_table}").map { |v| v.to_i }
1026
+ migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
790
1027
  paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" }
791
1028
  versions = Dir[*paths].map do |filename|
792
1029
  filename.split('/').last.split('_').first.to_i
@@ -796,14 +1033,12 @@ module ActiveRecord
796
1033
  execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
797
1034
  end
798
1035
 
799
- inserted = Set.new
800
- (versions - migrated).each do |v|
801
- if inserted.include?(v)
802
- raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
803
- elsif v < version
804
- execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
805
- inserted << v
1036
+ inserting = (versions - migrated).select {|v| v < version}
1037
+ if inserting.any?
1038
+ if (duplicate = inserting.detect {|v| inserting.count(v) > 1})
1039
+ raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
806
1040
  end
1041
+ execute insert_versions_sql(inserting)
807
1042
  end
808
1043
  end
809
1044
 
@@ -824,6 +1059,12 @@ module ActiveRecord
824
1059
  raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
825
1060
  end
826
1061
 
1062
+ elsif [:datetime, :time].include?(type) && precision ||= native[:precision]
1063
+ if (0..6) === precision
1064
+ column_type_sql << "(#{precision})"
1065
+ else
1066
+ raise(ActiveRecordError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6")
1067
+ end
827
1068
  elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
828
1069
  column_type_sql << "(#{limit})"
829
1070
  end
@@ -835,22 +1076,23 @@ module ActiveRecord
835
1076
  end
836
1077
 
837
1078
  # Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
838
- # Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax - they
1079
+ # PostgreSQL, MySQL, and Oracle overrides this for custom DISTINCT syntax - they
839
1080
  # require the order columns appear in the SELECT.
840
1081
  #
841
1082
  # columns_for_distinct("posts.id", ["posts.created_at desc"])
842
- def columns_for_distinct(columns, orders) #:nodoc:
1083
+ #
1084
+ def columns_for_distinct(columns, orders) # :nodoc:
843
1085
  columns
844
1086
  end
845
1087
 
846
- include TimestampDefaultDeprecation
847
1088
  # Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
848
- # Additional options (like <tt>null: false</tt>) are forwarded to #add_column.
1089
+ # Additional options (like +:null+) are forwarded to #add_column.
849
1090
  #
850
- # add_timestamps(:suppliers, null: false)
1091
+ # add_timestamps(:suppliers, null: true)
851
1092
  #
852
1093
  def add_timestamps(table_name, options = {})
853
- emit_warning_if_null_unspecified(options)
1094
+ options[:null] = false if options[:null].nil?
1095
+
854
1096
  add_column table_name, :created_at, :datetime, options
855
1097
  add_column table_name, :updated_at, :datetime, options
856
1098
  end
@@ -868,15 +1110,19 @@ module ActiveRecord
868
1110
  Table.new(table_name, base)
869
1111
  end
870
1112
 
871
- def add_index_options(table_name, column_name, options = {}) #:nodoc:
872
- column_names = Array(column_name)
873
- index_name = index_name(table_name, column: column_names)
1113
+ def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
1114
+ if column_name.is_a?(String) && /\W/ === column_name
1115
+ column_names = column_name
1116
+ else
1117
+ column_names = Array(column_name)
1118
+ end
874
1119
 
875
1120
  options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
876
1121
 
877
- index_type = options[:unique] ? "UNIQUE" : ""
878
1122
  index_type = options[:type].to_s if options.key?(:type)
1123
+ index_type ||= options[:unique] ? "UNIQUE" : ""
879
1124
  index_name = options[:name].to_s if options.key?(:name)
1125
+ index_name ||= index_name(table_name, index_name_options(column_names))
880
1126
  max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
881
1127
 
882
1128
  if options.key?(:algorithm)
@@ -894,12 +1140,26 @@ module ActiveRecord
894
1140
  if index_name.length > max_index_length
895
1141
  raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
896
1142
  end
897
- if table_exists?(table_name) && index_name_exists?(table_name, index_name, false)
1143
+ if data_source_exists?(table_name) && index_name_exists?(table_name, index_name, false)
898
1144
  raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
899
1145
  end
900
1146
  index_columns = quoted_columns_for_index(column_names, options).join(", ")
901
1147
 
902
- [index_name, index_type, index_columns, index_options, algorithm, using]
1148
+ [index_name, index_type, index_columns, index_options, algorithm, using, comment]
1149
+ end
1150
+
1151
+ def options_include_default?(options)
1152
+ options.include?(:default) && !(options[:null] == false && options[:default].nil?)
1153
+ end
1154
+
1155
+ # Changes the comment for a table or removes it if +nil+.
1156
+ def change_table_comment(table_name, comment)
1157
+ raise NotImplementedError, "#{self.class} does not support changing table comments"
1158
+ end
1159
+
1160
+ # Changes the comment for a column or removes it if +nil+.
1161
+ def change_column_comment(table_name, column_name, comment) #:nodoc:
1162
+ raise NotImplementedError, "#{self.class} does not support changing column comments"
903
1163
  end
904
1164
 
905
1165
  protected
@@ -918,6 +1178,8 @@ module ActiveRecord
918
1178
 
919
1179
  # Overridden by the MySQL adapter for supporting index lengths
920
1180
  def quoted_columns_for_index(column_names, options = {})
1181
+ return [column_names] if column_names.is_a?(String)
1182
+
921
1183
  option_strings = Hash[column_names.map {|name| [name, '']}]
922
1184
 
923
1185
  # add index sort order if supported
@@ -928,26 +1190,38 @@ module ActiveRecord
928
1190
  column_names.map {|name| quote_column_name(name) + option_strings[name]}
929
1191
  end
930
1192
 
931
- def options_include_default?(options)
932
- options.include?(:default) && !(options[:null] == false && options[:default].nil?)
933
- end
934
-
935
1193
  def index_name_for_remove(table_name, options = {})
936
- index_name = index_name(table_name, options)
1194
+ return options[:name] if can_remove_index_by_name?(options)
937
1195
 
938
- unless index_name_exists?(table_name, index_name, true)
939
- if options.is_a?(Hash) && options.has_key?(:name)
940
- options_without_column = options.dup
941
- options_without_column.delete :column
942
- index_name_without_column = index_name(table_name, options_without_column)
1196
+ # if the adapter doesn't support the indexes call the best we can do
1197
+ # is return the default index name for the options provided
1198
+ return index_name(table_name, options) unless respond_to?(:indexes)
943
1199
 
944
- return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
945
- end
1200
+ checks = []
1201
+
1202
+ if options.is_a?(Hash)
1203
+ checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
1204
+ column_names = Array(options[:column]).map(&:to_s)
1205
+ else
1206
+ column_names = Array(options).map(&:to_s)
1207
+ end
946
1208
 
947
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
1209
+ if column_names.any?
1210
+ checks << lambda { |i| i.columns.join('_and_') == column_names.join('_and_') }
948
1211
  end
949
1212
 
950
- index_name
1213
+ raise ArgumentError "No name or columns specified" if checks.none?
1214
+
1215
+ matching_indexes = indexes(table_name).select { |i| checks.all? { |check| check[i] } }
1216
+
1217
+ if matching_indexes.count > 1
1218
+ raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
1219
+ "Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
1220
+ elsif matching_indexes.none?
1221
+ raise ArgumentError, "No indexes found on #{table_name} with the options provided."
1222
+ else
1223
+ matching_indexes.first.name
1224
+ end
951
1225
  end
952
1226
 
953
1227
  def rename_table_indexes(table_name, new_name)
@@ -973,19 +1247,47 @@ module ActiveRecord
973
1247
  end
974
1248
 
975
1249
  private
976
- def create_table_definition(name, temporary, options, as = nil)
977
- TableDefinition.new native_database_types, name, temporary, options, as
1250
+ def create_table_definition(*args)
1251
+ TableDefinition.new(*args)
978
1252
  end
979
1253
 
980
1254
  def create_alter_table(name)
981
- AlterTable.new create_table_definition(name, false, {})
1255
+ AlterTable.new create_table_definition(name)
1256
+ end
1257
+
1258
+ def index_name_options(column_names) # :nodoc:
1259
+ if column_names.is_a?(String)
1260
+ column_names = column_names.scan(/\w+/).join('_')
1261
+ end
1262
+
1263
+ { column: column_names }
982
1264
  end
983
1265
 
984
1266
  def foreign_key_name(table_name, options) # :nodoc:
1267
+ identifier = "#{table_name}_#{options.fetch(:column)}_fk"
1268
+ hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
985
1269
  options.fetch(:name) do
986
- "fk_rails_#{SecureRandom.hex(5)}"
1270
+ "fk_rails_#{hashed_identifier}"
987
1271
  end
988
1272
  end
1273
+
1274
+ def validate_index_length!(table_name, new_name) # :nodoc:
1275
+ if new_name.length > allowed_index_name_length
1276
+ raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
1277
+ end
1278
+ end
1279
+
1280
+ def extract_new_default_value(default_or_changes)
1281
+ if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
1282
+ default_or_changes[:to]
1283
+ else
1284
+ default_or_changes
1285
+ end
1286
+ end
1287
+
1288
+ def can_remove_index_by_name?(options)
1289
+ options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
1290
+ end
989
1291
  end
990
1292
  end
991
1293
  end