activerecord 4.2.11.1 → 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 (246) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1282 -1195
  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.rb +8 -4
  8. data/lib/active_record/aggregations.rb +35 -24
  9. data/lib/active_record/association_relation.rb +3 -3
  10. data/lib/active_record/associations.rb +317 -209
  11. data/lib/active_record/associations/alias_tracker.rb +19 -16
  12. data/lib/active_record/associations/association.rb +11 -9
  13. data/lib/active_record/associations/association_scope.rb +73 -102
  14. data/lib/active_record/associations/belongs_to_association.rb +21 -32
  15. data/lib/active_record/associations/builder/association.rb +28 -34
  16. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  17. data/lib/active_record/associations/builder/collection_association.rb +7 -19
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +14 -11
  19. data/lib/active_record/associations/builder/has_many.rb +4 -4
  20. data/lib/active_record/associations/builder/has_one.rb +11 -6
  21. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  22. data/lib/active_record/associations/collection_association.rb +49 -41
  23. data/lib/active_record/associations/collection_proxy.rb +67 -27
  24. data/lib/active_record/associations/foreign_association.rb +1 -1
  25. data/lib/active_record/associations/has_many_association.rb +20 -71
  26. data/lib/active_record/associations/has_many_through_association.rb +8 -47
  27. data/lib/active_record/associations/has_one_association.rb +12 -5
  28. data/lib/active_record/associations/join_dependency.rb +29 -19
  29. data/lib/active_record/associations/join_dependency/join_association.rb +16 -10
  30. data/lib/active_record/associations/preloader.rb +14 -4
  31. data/lib/active_record/associations/preloader/association.rb +46 -52
  32. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  33. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  35. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  36. data/lib/active_record/associations/singular_association.rb +7 -1
  37. data/lib/active_record/associations/through_association.rb +11 -3
  38. data/lib/active_record/attribute.rb +68 -18
  39. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  40. data/lib/active_record/attribute_assignment.rb +19 -140
  41. data/lib/active_record/attribute_decorators.rb +6 -5
  42. data/lib/active_record/attribute_methods.rb +76 -47
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  44. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  45. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  46. data/lib/active_record/attribute_methods/query.rb +2 -2
  47. data/lib/active_record/attribute_methods/read.rb +31 -59
  48. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
  50. data/lib/active_record/attribute_methods/write.rb +13 -37
  51. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  52. data/lib/active_record/attribute_set.rb +30 -3
  53. data/lib/active_record/attribute_set/builder.rb +6 -4
  54. data/lib/active_record/attributes.rb +199 -81
  55. data/lib/active_record/autosave_association.rb +49 -16
  56. data/lib/active_record/base.rb +32 -23
  57. data/lib/active_record/callbacks.rb +39 -43
  58. data/lib/active_record/coders/json.rb +1 -1
  59. data/lib/active_record/coders/yaml_column.rb +20 -8
  60. data/lib/active_record/collection_cache_key.rb +40 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -182
  62. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  63. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
  64. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
  65. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -10
  66. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  67. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  68. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -185
  69. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  70. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +380 -141
  71. data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
  72. data/lib/active_record/connection_adapters/abstract_adapter.rb +141 -59
  73. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +401 -370
  74. data/lib/active_record/connection_adapters/column.rb +28 -43
  75. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  76. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  77. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  78. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  79. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  80. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  83. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  84. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  85. data/lib/active_record/connection_adapters/mysql2_adapter.rb +29 -166
  86. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  87. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
  88. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  90. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -57
  91. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  92. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  96. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  100. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  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 +234 -148
  111. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  112. data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
  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 +149 -192
  119. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  120. data/lib/active_record/connection_handling.rb +37 -14
  121. data/lib/active_record/core.rb +89 -107
  122. data/lib/active_record/counter_cache.rb +13 -24
  123. data/lib/active_record/dynamic_matchers.rb +1 -20
  124. data/lib/active_record/enum.rb +113 -76
  125. data/lib/active_record/errors.rb +87 -48
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +1 -1
  128. data/lib/active_record/fixture_set/file.rb +26 -5
  129. data/lib/active_record/fixtures.rb +76 -40
  130. data/lib/active_record/gem_version.rb +4 -4
  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 +18 -2
  135. data/lib/active_record/locale/en.yml +3 -2
  136. data/lib/active_record/locking/optimistic.rb +15 -15
  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.rb +363 -133
  140. data/lib/active_record/migration/command_recorder.rb +59 -18
  141. data/lib/active_record/migration/compatibility.rb +126 -0
  142. data/lib/active_record/model_schema.rb +129 -41
  143. data/lib/active_record/nested_attributes.rb +58 -29
  144. data/lib/active_record/null_relation.rb +16 -8
  145. data/lib/active_record/persistence.rb +121 -80
  146. data/lib/active_record/query_cache.rb +15 -18
  147. data/lib/active_record/querying.rb +10 -9
  148. data/lib/active_record/railtie.rb +23 -16
  149. data/lib/active_record/railties/controller_runtime.rb +1 -1
  150. data/lib/active_record/railties/databases.rake +69 -46
  151. data/lib/active_record/readonly_attributes.rb +1 -1
  152. data/lib/active_record/reflection.rb +282 -115
  153. data/lib/active_record/relation.rb +176 -116
  154. data/lib/active_record/relation/batches.rb +139 -34
  155. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  156. data/lib/active_record/relation/calculations.rb +79 -108
  157. data/lib/active_record/relation/delegation.rb +7 -20
  158. data/lib/active_record/relation/finder_methods.rb +163 -81
  159. data/lib/active_record/relation/from_clause.rb +32 -0
  160. data/lib/active_record/relation/merger.rb +16 -42
  161. data/lib/active_record/relation/predicate_builder.rb +120 -107
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  163. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  164. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  165. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  166. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  167. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  168. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  169. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  170. data/lib/active_record/relation/query_attribute.rb +19 -0
  171. data/lib/active_record/relation/query_methods.rb +308 -244
  172. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  173. data/lib/active_record/relation/spawn_methods.rb +4 -7
  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/result.rb +4 -3
  177. data/lib/active_record/runtime_registry.rb +1 -1
  178. data/lib/active_record/sanitization.rb +95 -66
  179. data/lib/active_record/schema.rb +26 -22
  180. data/lib/active_record/schema_dumper.rb +62 -38
  181. data/lib/active_record/schema_migration.rb +11 -14
  182. data/lib/active_record/scoping.rb +32 -15
  183. data/lib/active_record/scoping/default.rb +23 -9
  184. data/lib/active_record/scoping/named.rb +49 -28
  185. data/lib/active_record/secure_token.rb +38 -0
  186. data/lib/active_record/serialization.rb +2 -4
  187. data/lib/active_record/statement_cache.rb +16 -14
  188. data/lib/active_record/store.rb +8 -3
  189. data/lib/active_record/suppressor.rb +58 -0
  190. data/lib/active_record/table_metadata.rb +68 -0
  191. data/lib/active_record/tasks/database_tasks.rb +57 -43
  192. data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
  193. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  194. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  195. data/lib/active_record/timestamp.rb +20 -9
  196. data/lib/active_record/touch_later.rb +58 -0
  197. data/lib/active_record/transactions.rb +138 -56
  198. data/lib/active_record/type.rb +66 -17
  199. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  200. data/lib/active_record/type/date.rb +2 -45
  201. data/lib/active_record/type/date_time.rb +2 -49
  202. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  203. data/lib/active_record/type/internal/timezone.rb +15 -0
  204. data/lib/active_record/type/serialized.rb +15 -14
  205. data/lib/active_record/type/time.rb +10 -16
  206. data/lib/active_record/type/type_map.rb +4 -4
  207. data/lib/active_record/type_caster.rb +7 -0
  208. data/lib/active_record/type_caster/connection.rb +29 -0
  209. data/lib/active_record/type_caster/map.rb +19 -0
  210. data/lib/active_record/validations.rb +33 -32
  211. data/lib/active_record/validations/absence.rb +23 -0
  212. data/lib/active_record/validations/associated.rb +10 -3
  213. data/lib/active_record/validations/length.rb +24 -0
  214. data/lib/active_record/validations/presence.rb +11 -12
  215. data/lib/active_record/validations/uniqueness.rb +30 -29
  216. data/lib/rails/generators/active_record/migration.rb +7 -0
  217. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  218. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  219. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
  220. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  221. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  222. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  223. metadata +59 -34
  224. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  225. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  226. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  227. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  228. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  229. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  231. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  232. data/lib/active_record/type/big_integer.rb +0 -13
  233. data/lib/active_record/type/binary.rb +0 -50
  234. data/lib/active_record/type/boolean.rb +0 -31
  235. data/lib/active_record/type/decimal.rb +0 -64
  236. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  237. data/lib/active_record/type/decorator.rb +0 -14
  238. data/lib/active_record/type/float.rb +0 -19
  239. data/lib/active_record/type/integer.rb +0 -59
  240. data/lib/active_record/type/mutable.rb +0 -16
  241. data/lib/active_record/type/numeric.rb +0 -36
  242. data/lib/active_record/type/string.rb +0 -40
  243. data/lib/active_record/type/text.rb +0 -11
  244. data/lib/active_record/type/time_value.rb +0 -38
  245. data/lib/active_record/type/unsigned_integer.rb +0 -15
  246. data/lib/active_record/type/value.rb +0 -110
@@ -6,45 +6,100 @@ module ActiveRecord
6
6
  # We can then redefine how certain data types may be handled in the schema dumper on the
7
7
  # Adapter level by over-writing this code inside the database specific adapters
8
8
  module ColumnDumper
9
- def column_spec(column, types)
10
- spec = prepare_column_options(column, types)
11
- (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k}: ")}
9
+ def column_spec(column)
10
+ spec = Hash[prepare_column_options(column).map { |k, v| [k, "#{k}: #{v}"] }]
11
+ spec[:name] = column.name.inspect
12
+ spec[:type] = schema_type(column).to_s
12
13
  spec
13
14
  end
14
15
 
15
- # This can be overridden on a Adapter level basis to support other
16
+ def column_spec_for_primary_key(column)
17
+ return {} if default_primary_key?(column)
18
+ spec = { id: schema_type(column).inspect }
19
+ spec.merge!(prepare_column_options(column).except!(:null))
20
+ end
21
+
22
+ # This can be overridden on an Adapter level basis to support other
16
23
  # extended datatypes (Example: Adding an array option in the
17
- # PostgreSQLAdapter)
18
- def prepare_column_options(column, types)
24
+ # PostgreSQL::ColumnDumper)
25
+ def prepare_column_options(column)
19
26
  spec = {}
20
- spec[:name] = column.name.inspect
21
- spec[:type] = column.type.to_s
22
- spec[:null] = 'false' unless column.null
23
27
 
24
- limit = column.limit || types[column.type][:limit]
25
- spec[:limit] = limit.inspect if limit
26
- spec[:precision] = column.precision.inspect if column.precision
27
- spec[:scale] = column.scale.inspect if column.scale
28
+ if limit = schema_limit(column)
29
+ spec[:limit] = limit
30
+ end
31
+
32
+ if precision = schema_precision(column)
33
+ spec[:precision] = precision
34
+ end
35
+
36
+ if scale = schema_scale(column)
37
+ spec[:scale] = scale
38
+ end
28
39
 
29
40
  default = schema_default(column) if column.has_default?
30
41
  spec[:default] = default unless default.nil?
31
42
 
43
+ spec[:null] = 'false' unless column.null
44
+
45
+ if collation = schema_collation(column)
46
+ spec[:collation] = collation
47
+ end
48
+
49
+ spec[:comment] = column.comment.inspect if column.comment.present?
50
+
32
51
  spec
33
52
  end
34
53
 
35
54
  # Lists the valid migration options
36
55
  def migration_keys
37
- [:name, :limit, :precision, :scale, :default, :null]
56
+ [:name, :limit, :precision, :scale, :default, :null, :collation, :comment]
38
57
  end
39
58
 
40
59
  private
41
60
 
61
+ def default_primary_key?(column)
62
+ schema_type(column) == :integer
63
+ end
64
+
65
+ def schema_type(column)
66
+ if column.bigint?
67
+ :bigint
68
+ else
69
+ column.type
70
+ end
71
+ end
72
+
73
+ def schema_limit(column)
74
+ limit = column.limit unless column.bigint?
75
+ limit.inspect if limit && limit != native_database_types[column.type][:limit]
76
+ end
77
+
78
+ def schema_precision(column)
79
+ column.precision.inspect if column.precision
80
+ end
81
+
82
+ def schema_scale(column)
83
+ column.scale.inspect if column.scale
84
+ end
85
+
42
86
  def schema_default(column)
43
- default = column.type_cast_from_database(column.default)
44
- unless default.nil?
45
- column.type_cast_for_schema(default)
87
+ type = lookup_cast_type_from_column(column)
88
+ default = type.deserialize(column.default)
89
+ if default.nil?
90
+ schema_expression(column)
91
+ else
92
+ type.type_cast_for_schema(default)
46
93
  end
47
94
  end
95
+
96
+ def schema_expression(column)
97
+ "-> { #{column.default_function.inspect} }" if column.default_function
98
+ end
99
+
100
+ def schema_collation(column)
101
+ column.collation.inspect if column.collation
102
+ end
48
103
  end
49
104
  end
50
105
  end
@@ -14,15 +14,24 @@ module ActiveRecord
14
14
  {}
15
15
  end
16
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
+
17
26
  # Truncates a table alias according to the limits of the current adapter.
18
27
  def table_alias_for(table_name)
19
28
  table_name[0...table_alias_length].tr('.', '_')
20
29
  end
21
30
 
22
31
  # Returns the relation names useable to back Active Record models.
23
- # For most adapters this means all tables and views.
32
+ # For most adapters this means all #tables and #views.
24
33
  def data_sources
25
- tables
34
+ tables | views
26
35
  end
27
36
 
28
37
  # Checks to see if the data source +name+ exists on the database.
@@ -33,6 +42,11 @@ module ActiveRecord
33
42
  data_sources.include?(name.to_s)
34
43
  end
35
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
+
36
50
  # Checks to see if the table +table_name+ exists on the database.
37
51
  #
38
52
  # table_exists?(:developers)
@@ -41,6 +55,19 @@ module ActiveRecord
41
55
  tables.include?(table_name.to_s)
42
56
  end
43
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
+
44
71
  # Returns an array of indexes for the given table.
45
72
  # def indexes(table_name, name = nil) end
46
73
 
@@ -60,18 +87,19 @@ module ActiveRecord
60
87
  #
61
88
  def index_exists?(table_name, column_name, options = {})
62
89
  column_names = Array(column_name).map(&:to_s)
63
- index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, column: column_names)
64
90
  checks = []
65
- checks << lambda { |i| i.name == index_name }
66
91
  checks << lambda { |i| i.columns == column_names }
67
92
  checks << lambda { |i| i.unique } if options[:unique]
93
+ checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
68
94
 
69
95
  indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
70
96
  end
71
97
 
72
98
  # Returns an array of Column objects for the table specified by +table_name+.
73
99
  # See the concrete implementation for details on the expected parameter values.
74
- def columns(table_name) end
100
+ def columns(table_name)
101
+ raise NotImplementedError, "#columns is not implemented"
102
+ end
75
103
 
76
104
  # Checks to see if a column exists in a given table.
77
105
  #
@@ -89,19 +117,32 @@ module ActiveRecord
89
117
  #
90
118
  def column_exists?(table_name, column_name, type = nil, options = {})
91
119
  column_name = column_name.to_s
92
- columns(table_name).any?{ |c| c.name == column_name &&
93
- (!type || c.type == type) &&
94
- (!options.key?(:limit) || c.limit == options[:limit]) &&
95
- (!options.key?(:precision) || c.precision == options[:precision]) &&
96
- (!options.key?(:scale) || c.scale == options[:scale]) &&
97
- (!options.key?(:default) || c.default == options[:default]) &&
98
- (!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?
99
140
  end
100
141
 
101
142
  # Creates a new table with the name +table_name+. +table_name+ may either
102
143
  # be a String or a Symbol.
103
144
  #
104
- # 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
105
146
  # form or the regular form, like this:
106
147
  #
107
148
  # === Block form
@@ -133,13 +174,16 @@ module ActiveRecord
133
174
  # The +options+ hash can include the following keys:
134
175
  # [<tt>:id</tt>]
135
176
  # Whether to automatically add a primary key column. Defaults to true.
136
- # 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.
137
180
  # [<tt>:primary_key</tt>]
138
181
  # The name of the primary key, if one is to be added automatically.
139
182
  # Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
140
183
  #
141
184
  # Note that Active Record models will automatically detect their
142
- # 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
143
187
  # to define the key explicitly.
144
188
  #
145
189
  # [<tt>:options</tt>]
@@ -161,7 +205,7 @@ module ActiveRecord
161
205
  # generates:
162
206
  #
163
207
  # CREATE TABLE suppliers (
164
- # id int(11) DEFAULT NULL auto_increment PRIMARY KEY
208
+ # id int auto_increment PRIMARY KEY
165
209
  # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
166
210
  #
167
211
  # ====== Rename the primary key column
@@ -173,10 +217,23 @@ module ActiveRecord
173
217
  # generates:
174
218
  #
175
219
  # CREATE TABLE objects (
176
- # guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
220
+ # guid int auto_increment PRIMARY KEY,
177
221
  # name varchar(80)
178
222
  # )
179
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
+ #
180
237
  # ====== Do not add a primary key column
181
238
  #
182
239
  # create_table(:categories_suppliers, id: false) do |t|
@@ -202,20 +259,24 @@ module ActiveRecord
202
259
  # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
203
260
  #
204
261
  # See also TableDefinition#column for details on how to create columns.
205
- def create_table(table_name, options = {})
206
- 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
207
264
 
208
265
  if options[:id] != false && !options[:as]
209
266
  pk = options.fetch(:primary_key) do
210
267
  Base.get_primary_key table_name.to_s.singularize
211
268
  end
212
269
 
213
- 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
214
275
  end
215
276
 
216
277
  yield td if block_given?
217
278
 
218
- if options[:force] && table_exists?(table_name)
279
+ if options[:force] && data_source_exists?(table_name)
219
280
  drop_table(table_name, options)
220
281
  end
221
282
 
@@ -227,8 +288,12 @@ module ActiveRecord
227
288
  end
228
289
  end
229
290
 
230
- td.foreign_keys.each do |other_table_name, foreign_key_options|
231
- add_foreign_key(table_name, other_table_name, foreign_key_options)
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
232
297
  end
233
298
 
234
299
  result
@@ -253,7 +318,7 @@ module ActiveRecord
253
318
  # Set to true to drop the table before creating it.
254
319
  # Defaults to false.
255
320
  #
256
- # 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
257
322
  # its block form to do so yourself:
258
323
  #
259
324
  # create_join_table :products, :categories do |t|
@@ -277,22 +342,23 @@ module ActiveRecord
277
342
 
278
343
  column_options = options.delete(:column_options) || {}
279
344
  column_options.reverse_merge!(null: false)
345
+ type = column_options.delete(:type) || :integer
280
346
 
281
347
  t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
282
348
 
283
349
  create_table(join_table_name, options.merge!(id: false)) do |td|
284
- td.integer t1_column, column_options
285
- td.integer t2_column, column_options
350
+ td.send type, t1_column, column_options
351
+ td.send type, t2_column, column_options
286
352
  yield td if block_given?
287
353
  end
288
354
  end
289
355
 
290
356
  # Drops the join table specified by the given arguments.
291
- # See +create_join_table+ for details.
357
+ # See #create_join_table for details.
292
358
  #
293
359
  # Although this command ignores the block if one is given, it can be helpful
294
360
  # to provide one in a migration's +change+ method so it can be reverted.
295
- # 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.
296
362
  def drop_join_table(table_1, table_2, options = {})
297
363
  join_table_name = find_join_table_name(table_1, table_2, options)
298
364
  drop_table(join_table_name)
@@ -310,7 +376,7 @@ module ActiveRecord
310
376
  # [<tt>:bulk</tt>]
311
377
  # Set this to true to make this a bulk alter query, such as
312
378
  #
313
- # ALTER TABLE `users` ADD COLUMN age INT(11), ADD COLUMN birthdate DATETIME ...
379
+ # ALTER TABLE `users` ADD COLUMN age INT, ADD COLUMN birthdate DATETIME ...
314
380
  #
315
381
  # Defaults to false.
316
382
  #
@@ -391,16 +457,90 @@ module ActiveRecord
391
457
  # [<tt>:force</tt>]
392
458
  # Set to +:cascade+ to drop dependent objects as well.
393
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.
394
463
  #
395
464
  # Although this command ignores most +options+ and the block if one is given,
396
465
  # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
397
- # 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.
398
467
  def drop_table(table_name, options = {})
399
- execute "DROP TABLE #{quote_table_name(table_name)}"
468
+ execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
400
469
  end
401
470
 
402
- # Adds a new column to the named table.
403
- # 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
404
544
  def add_column(table_name, column_name, type, options = {})
405
545
  at = create_alter_table table_name
406
546
  at.add_column(column_name, type, options)
@@ -448,11 +588,16 @@ module ActiveRecord
448
588
  #
449
589
  # change_column_default(:users, :email, nil)
450
590
  #
451
- 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)
452
597
  raise NotImplementedError, "change_column_default is not implemented"
453
598
  end
454
599
 
455
- # 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
456
601
  # indicates whether the value can be +NULL+. For example
457
602
  #
458
603
  # change_column_null(:users, :nickname, false)
@@ -464,7 +609,7 @@ module ActiveRecord
464
609
  # allows them to be +NULL+ (drops the constraint).
465
610
  #
466
611
  # The method accepts an optional fourth argument to replace existing
467
- # +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
468
613
  # constraint if needed, since otherwise those rows would not be valid.
469
614
  #
470
615
  # Please note the fourth argument does not set a column's default.
@@ -518,6 +663,8 @@ module ActiveRecord
518
663
  #
519
664
  # CREATE INDEX by_name ON accounts(name(10))
520
665
  #
666
+ # ====== Creating an index with specific key lengths for multiple keys
667
+ #
521
668
  # add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
522
669
  #
523
670
  # generates:
@@ -565,7 +712,7 @@ module ActiveRecord
565
712
  #
566
713
  # CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
567
714
  #
568
- # Note: only supported by MySQL. Supported: <tt>:fulltext</tt> and <tt>:spatial</tt> on MyISAM tables.
715
+ # Note: only supported by MySQL.
569
716
  def add_index(table_name, column_name, options = {})
570
717
  index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
571
718
  execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
@@ -573,15 +720,15 @@ module ActiveRecord
573
720
 
574
721
  # Removes the given index from the table.
575
722
  #
576
- # 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.
577
724
  #
578
- # remove_index :accounts, :column
725
+ # remove_index :accounts, :branch_id
579
726
  #
580
- # 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.
581
728
  #
582
729
  # remove_index :accounts, column: :branch_id
583
730
  #
584
- # 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.
585
732
  #
586
733
  # remove_index :accounts, column: [:branch_id, :party_id]
587
734
  #
@@ -590,10 +737,7 @@ module ActiveRecord
590
737
  # remove_index :accounts, name: :by_branch_party
591
738
  #
592
739
  def remove_index(table_name, options = {})
593
- remove_index!(table_name, index_name_for_remove(table_name, options))
594
- end
595
-
596
- def remove_index!(table_name, index_name) #:nodoc:
740
+ index_name = index_name_for_remove(table_name, options)
597
741
  execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
598
742
  end
599
743
 
@@ -640,17 +784,20 @@ module ActiveRecord
640
784
  # Adds a reference. The reference column is an integer by default,
641
785
  # the <tt>:type</tt> option can be used to specify a different type.
642
786
  # Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
643
- # <tt>add_reference</tt> and <tt>add_belongs_to</tt> are acceptable.
787
+ # #add_reference and #add_belongs_to are acceptable.
644
788
  #
645
789
  # The +options+ hash can include the following keys:
646
790
  # [<tt>:type</tt>]
647
791
  # The reference column type. Defaults to +:integer+.
648
792
  # [<tt>:index</tt>]
649
- # Add an appropriate index. Defaults to false.
793
+ # Add an appropriate index. Defaults to false.
794
+ # See #add_index for usage of this option.
650
795
  # [<tt>:foreign_key</tt>]
651
- # Add an appropriate foreign key. Defaults to false.
796
+ # Add an appropriate foreign key constraint. Defaults to false.
652
797
  # [<tt>:polymorphic</tt>]
653
- # Wether an additional +_type+ column should be added. Defaults to false.
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.
654
801
  #
655
802
  # ====== Create a user_id integer column
656
803
  #
@@ -664,28 +811,29 @@ module ActiveRecord
664
811
  #
665
812
  # add_reference(:products, :supplier, polymorphic: true, index: true)
666
813
  #
667
- def add_reference(table_name, ref_name, options = {})
668
- polymorphic = options.delete(:polymorphic)
669
- index_options = options.delete(:index)
670
- type = options.delete(:type) || :integer
671
- foreign_key_options = options.delete(:foreign_key)
672
-
673
- if polymorphic && foreign_key_options
674
- raise ArgumentError, "Cannot add a foreign key to a polymorphic relation"
675
- end
676
-
677
- add_column(table_name, "#{ref_name}_id", type, options)
678
- add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
679
- 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
680
- if foreign_key_options
681
- to_table = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
682
- add_foreign_key(table_name, to_table, foreign_key_options.is_a?(Hash) ? foreign_key_options : {})
683
- end
814
+ # ====== Create a supplier_id column with a unique index
815
+ #
816
+ # add_reference(:products, :supplier, index: { unique: true })
817
+ #
818
+ # ====== Create a supplier_id column with a named index
819
+ #
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))
684
832
  end
685
833
  alias :add_belongs_to :add_reference
686
834
 
687
835
  # Removes the reference(s). Also removes a +type+ column if one exists.
688
- # <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.
689
837
  #
690
838
  # ====== Remove the reference
691
839
  #
@@ -701,8 +849,8 @@ module ActiveRecord
701
849
  #
702
850
  def remove_reference(table_name, ref_name, options = {})
703
851
  if options[:foreign_key]
704
- to_table = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
705
- remove_foreign_key(table_name, to_table)
852
+ reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
853
+ remove_foreign_key(table_name, reference_name)
706
854
  end
707
855
 
708
856
  remove_column(table_name, "#{ref_name}_id")
@@ -711,7 +859,7 @@ module ActiveRecord
711
859
  alias :remove_belongs_to :remove_reference
712
860
 
713
861
  # Returns an array of foreign keys for the given table.
714
- # The foreign keys are represented as +ForeignKeyDefinition+ objects.
862
+ # The foreign keys are represented as ForeignKeyDefinition objects.
715
863
  def foreign_keys(table_name)
716
864
  raise NotImplementedError, "foreign_keys is not implemented"
717
865
  end
@@ -729,11 +877,11 @@ module ActiveRecord
729
877
  #
730
878
  # generates:
731
879
  #
732
- # 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")
733
881
  #
734
882
  # ====== Creating a foreign key on a specific column
735
883
  #
736
- # add_foreign_key :articles, :users, column: :author_id, primary_key: :lng_id
884
+ # add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id"
737
885
  #
738
886
  # generates:
739
887
  #
@@ -745,7 +893,7 @@ module ActiveRecord
745
893
  #
746
894
  # generates:
747
895
  #
748
- # 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
749
897
  #
750
898
  # The +options+ hash can include the following keys:
751
899
  # [<tt>:column</tt>]
@@ -755,28 +903,23 @@ module ActiveRecord
755
903
  # [<tt>:name</tt>]
756
904
  # The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
757
905
  # [<tt>:on_delete</tt>]
758
- # 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+
759
907
  # [<tt>:on_update</tt>]
760
- # 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+
761
909
  def add_foreign_key(from_table, to_table, options = {})
762
910
  return unless supports_foreign_keys?
763
911
 
764
- options[:column] ||= foreign_key_column_for(to_table)
765
-
766
- options = {
767
- column: options[:column],
768
- primary_key: options[:primary_key],
769
- name: foreign_key_name(from_table, options),
770
- on_delete: options[:on_delete],
771
- on_update: options[:on_update]
772
- }
912
+ options = foreign_key_options(from_table, to_table, options)
773
913
  at = create_alter_table from_table
774
914
  at.add_foreign_key to_table, options
775
915
 
776
916
  execute schema_creation.accept(at)
777
917
  end
778
918
 
779
- # 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.
780
923
  #
781
924
  # Removes the foreign key on +accounts.branch_id+.
782
925
  #
@@ -790,24 +933,11 @@ module ActiveRecord
790
933
  #
791
934
  # remove_foreign_key :accounts, name: :special_fk_name
792
935
  #
936
+ # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
793
937
  def remove_foreign_key(from_table, options_or_to_table = {})
794
938
  return unless supports_foreign_keys?
795
939
 
796
- if options_or_to_table.is_a?(Hash)
797
- options = options_or_to_table
798
- else
799
- options = { column: foreign_key_column_for(options_or_to_table) }
800
- end
801
-
802
- fk_name_to_delete = options.fetch(:name) do
803
- fk_to_delete = foreign_keys(from_table).detect {|fk| fk.column == options[:column].to_s }
804
-
805
- if fk_to_delete
806
- fk_to_delete.name
807
- else
808
- raise ArgumentError, "Table '#{from_table}' has no foreign key on column '#{options[:column]}'"
809
- end
810
- end
940
+ fk_name_to_delete = foreign_key_for!(from_table, options_or_to_table).name
811
941
 
812
942
  at = create_alter_table from_table
813
943
  at.drop_foreign_key fk_name_to_delete
@@ -815,6 +945,31 @@ module ActiveRecord
815
945
  execute schema_creation.accept(at)
816
946
  end
817
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
+
818
973
  def foreign_key_column_for(table_name) # :nodoc:
819
974
  prefix = Base.table_name_prefix
820
975
  suffix = Base.table_name_suffix
@@ -822,12 +977,31 @@ module ActiveRecord
822
977
  "#{name.singularize}_id"
823
978
  end
824
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
985
+ end
986
+
825
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:
826
993
  sm_table = ActiveRecord::Migrator.schema_migrations_table_name
827
994
 
828
- ActiveRecord::SchemaMigration.order('version').map { |sm|
829
- "INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');"
830
- }.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
831
1005
  end
832
1006
 
833
1007
  # Should not be called normally, but this operation is non-destructive.
@@ -836,28 +1010,35 @@ module ActiveRecord
836
1010
  ActiveRecord::SchemaMigration.create_table
837
1011
  end
838
1012
 
839
- 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)
840
1022
  migrations_paths = Array(migrations_paths)
841
1023
  version = version.to_i
842
1024
  sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
843
1025
 
844
1026
  migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
845
- versions = ActiveRecord::Migrator.migration_files(migrations_paths).map do |file|
846
- ActiveRecord::Migrator.parse_migration_filename(file).first.to_i
1027
+ paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" }
1028
+ versions = Dir[*paths].map do |filename|
1029
+ filename.split('/').last.split('_').first.to_i
847
1030
  end
848
1031
 
849
1032
  unless migrated.include?(version)
850
1033
  execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
851
1034
  end
852
1035
 
853
- inserted = Set.new
854
- (versions - migrated).each do |v|
855
- if inserted.include?(v)
856
- raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
857
- elsif v < version
858
- execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
859
- 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."
860
1040
  end
1041
+ execute insert_versions_sql(inserting)
861
1042
  end
862
1043
  end
863
1044
 
@@ -878,6 +1059,12 @@ module ActiveRecord
878
1059
  raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
879
1060
  end
880
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
881
1068
  elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
882
1069
  column_type_sql << "(#{limit})"
883
1070
  end
@@ -898,14 +1085,14 @@ module ActiveRecord
898
1085
  columns
899
1086
  end
900
1087
 
901
- include TimestampDefaultDeprecation
902
1088
  # Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
903
- # Additional options (like <tt>null: false</tt>) are forwarded to #add_column.
1089
+ # Additional options (like +:null+) are forwarded to #add_column.
904
1090
  #
905
- # add_timestamps(:suppliers, null: false)
1091
+ # add_timestamps(:suppliers, null: true)
906
1092
  #
907
1093
  def add_timestamps(table_name, options = {})
908
- emit_warning_if_null_unspecified(:add_timestamps, options)
1094
+ options[:null] = false if options[:null].nil?
1095
+
909
1096
  add_column table_name, :created_at, :datetime, options
910
1097
  add_column table_name, :updated_at, :datetime, options
911
1098
  end
@@ -923,15 +1110,19 @@ module ActiveRecord
923
1110
  Table.new(table_name, base)
924
1111
  end
925
1112
 
926
- def add_index_options(table_name, column_name, options = {}) #:nodoc:
927
- column_names = Array(column_name)
928
- 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
929
1119
 
930
1120
  options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
931
1121
 
932
- index_type = options[:unique] ? "UNIQUE" : ""
933
1122
  index_type = options[:type].to_s if options.key?(:type)
1123
+ index_type ||= options[:unique] ? "UNIQUE" : ""
934
1124
  index_name = options[:name].to_s if options.key?(:name)
1125
+ index_name ||= index_name(table_name, index_name_options(column_names))
935
1126
  max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
936
1127
 
937
1128
  if options.key?(:algorithm)
@@ -949,12 +1140,26 @@ module ActiveRecord
949
1140
  if index_name.length > max_index_length
950
1141
  raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
951
1142
  end
952
- 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)
953
1144
  raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
954
1145
  end
955
1146
  index_columns = quoted_columns_for_index(column_names, options).join(", ")
956
1147
 
957
- [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"
958
1163
  end
959
1164
 
960
1165
  protected
@@ -973,6 +1178,8 @@ module ActiveRecord
973
1178
 
974
1179
  # Overridden by the MySQL adapter for supporting index lengths
975
1180
  def quoted_columns_for_index(column_names, options = {})
1181
+ return [column_names] if column_names.is_a?(String)
1182
+
976
1183
  option_strings = Hash[column_names.map {|name| [name, '']}]
977
1184
 
978
1185
  # add index sort order if supported
@@ -983,26 +1190,38 @@ module ActiveRecord
983
1190
  column_names.map {|name| quote_column_name(name) + option_strings[name]}
984
1191
  end
985
1192
 
986
- def options_include_default?(options)
987
- options.include?(:default) && !(options[:null] == false && options[:default].nil?)
988
- end
989
-
990
1193
  def index_name_for_remove(table_name, options = {})
991
- index_name = index_name(table_name, options)
1194
+ return options[:name] if can_remove_index_by_name?(options)
992
1195
 
993
- unless index_name_exists?(table_name, index_name, true)
994
- if options.is_a?(Hash) && options.has_key?(:name)
995
- options_without_column = options.dup
996
- options_without_column.delete :column
997
- 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)
998
1199
 
999
- return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
1000
- 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
1001
1208
 
1002
- 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_') }
1003
1211
  end
1004
1212
 
1005
- 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
1006
1225
  end
1007
1226
 
1008
1227
  def rename_table_indexes(table_name, new_name)
@@ -1028,12 +1247,20 @@ module ActiveRecord
1028
1247
  end
1029
1248
 
1030
1249
  private
1031
- def create_table_definition(name, temporary, options, as = nil)
1032
- TableDefinition.new native_database_types, name, temporary, options, as
1250
+ def create_table_definition(*args)
1251
+ TableDefinition.new(*args)
1033
1252
  end
1034
1253
 
1035
1254
  def create_alter_table(name)
1036
- 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 }
1037
1264
  end
1038
1265
 
1039
1266
  def foreign_key_name(table_name, options) # :nodoc:
@@ -1044,11 +1271,23 @@ module ActiveRecord
1044
1271
  end
1045
1272
  end
1046
1273
 
1047
- def validate_index_length!(table_name, new_name)
1274
+ def validate_index_length!(table_name, new_name) # :nodoc:
1048
1275
  if new_name.length > allowed_index_name_length
1049
1276
  raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
1050
1277
  end
1051
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
1052
1291
  end
1053
1292
  end
1054
1293
  end