activerecord 4.2.11.3 → 5.0.7.2

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

Potentially problematic release.


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

Files changed (251) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1638 -1132
  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 +7 -2
  8. data/lib/active_record/aggregations.rb +34 -21
  9. data/lib/active_record/association_relation.rb +7 -4
  10. data/lib/active_record/associations.rb +347 -218
  11. data/lib/active_record/associations/alias_tracker.rb +19 -16
  12. data/lib/active_record/associations/association.rb +22 -10
  13. data/lib/active_record/associations/association_scope.rb +75 -104
  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 +16 -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 +13 -11
  22. data/lib/active_record/associations/collection_association.rb +85 -69
  23. data/lib/active_record/associations/collection_proxy.rb +104 -46
  24. data/lib/active_record/associations/foreign_association.rb +1 -1
  25. data/lib/active_record/associations/has_many_association.rb +21 -78
  26. data/lib/active_record/associations/has_many_through_association.rb +6 -47
  27. data/lib/active_record/associations/has_one_association.rb +12 -5
  28. data/lib/active_record/associations/join_dependency.rb +38 -22
  29. data/lib/active_record/associations/join_dependency/join_association.rb +15 -14
  30. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  31. data/lib/active_record/associations/preloader.rb +14 -4
  32. data/lib/active_record/associations/preloader/association.rb +52 -71
  33. data/lib/active_record/associations/preloader/collection_association.rb +0 -7
  34. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  35. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  36. data/lib/active_record/associations/preloader/singular_association.rb +0 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +36 -17
  38. data/lib/active_record/associations/singular_association.rb +13 -1
  39. data/lib/active_record/associations/through_association.rb +12 -4
  40. data/lib/active_record/attribute.rb +69 -19
  41. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  42. data/lib/active_record/attribute_assignment.rb +19 -140
  43. data/lib/active_record/attribute_decorators.rb +6 -5
  44. data/lib/active_record/attribute_methods.rb +69 -44
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  46. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  47. data/lib/active_record/attribute_methods/primary_key.rb +16 -3
  48. data/lib/active_record/attribute_methods/query.rb +2 -2
  49. data/lib/active_record/attribute_methods/read.rb +31 -59
  50. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  51. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
  52. data/lib/active_record/attribute_methods/write.rb +13 -37
  53. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  54. data/lib/active_record/attribute_set.rb +32 -3
  55. data/lib/active_record/attribute_set/builder.rb +42 -16
  56. data/lib/active_record/attributes.rb +199 -81
  57. data/lib/active_record/autosave_association.rb +54 -17
  58. data/lib/active_record/base.rb +32 -23
  59. data/lib/active_record/callbacks.rb +39 -43
  60. data/lib/active_record/coders/json.rb +1 -1
  61. data/lib/active_record/coders/yaml_column.rb +20 -8
  62. data/lib/active_record/collection_cache_key.rb +50 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +467 -189
  64. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  65. data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -62
  66. data/lib/active_record/connection_adapters/abstract/query_cache.rb +39 -4
  67. data/lib/active_record/connection_adapters/abstract/quoting.rb +86 -13
  68. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  69. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  70. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -188
  71. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  72. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +407 -156
  73. data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
  74. data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -71
  75. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +433 -399
  76. data/lib/active_record/connection_adapters/column.rb +28 -43
  77. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  78. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  79. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  80. data/lib/active_record/connection_adapters/mysql/database_statements.rb +108 -0
  81. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  82. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  83. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  84. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  86. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  87. data/lib/active_record/connection_adapters/mysql2_adapter.rb +25 -166
  88. data/lib/active_record/connection_adapters/postgresql/column.rb +33 -11
  89. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -72
  90. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  92. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +37 -57
  93. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +3 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -2
  95. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  96. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  97. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +13 -3
  98. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  99. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  102. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +56 -19
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +250 -154
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -2
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +264 -170
  116. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  118. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  119. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  120. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  121. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +151 -194
  122. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  123. data/lib/active_record/connection_handling.rb +37 -14
  124. data/lib/active_record/core.rb +92 -108
  125. data/lib/active_record/counter_cache.rb +13 -24
  126. data/lib/active_record/dynamic_matchers.rb +1 -20
  127. data/lib/active_record/enum.rb +116 -76
  128. data/lib/active_record/errors.rb +87 -48
  129. data/lib/active_record/explain.rb +20 -9
  130. data/lib/active_record/explain_registry.rb +1 -1
  131. data/lib/active_record/explain_subscriber.rb +1 -1
  132. data/lib/active_record/fixture_set/file.rb +26 -5
  133. data/lib/active_record/fixtures.rb +77 -41
  134. data/lib/active_record/gem_version.rb +4 -4
  135. data/lib/active_record/inheritance.rb +32 -40
  136. data/lib/active_record/integration.rb +17 -14
  137. data/lib/active_record/internal_metadata.rb +56 -0
  138. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  139. data/lib/active_record/locale/en.yml +3 -2
  140. data/lib/active_record/locking/optimistic.rb +15 -15
  141. data/lib/active_record/locking/pessimistic.rb +1 -1
  142. data/lib/active_record/log_subscriber.rb +48 -24
  143. data/lib/active_record/migration.rb +362 -111
  144. data/lib/active_record/migration/command_recorder.rb +59 -18
  145. data/lib/active_record/migration/compatibility.rb +126 -0
  146. data/lib/active_record/model_schema.rb +270 -73
  147. data/lib/active_record/nested_attributes.rb +58 -29
  148. data/lib/active_record/no_touching.rb +4 -0
  149. data/lib/active_record/null_relation.rb +16 -8
  150. data/lib/active_record/persistence.rb +152 -90
  151. data/lib/active_record/query_cache.rb +18 -23
  152. data/lib/active_record/querying.rb +12 -11
  153. data/lib/active_record/railtie.rb +23 -16
  154. data/lib/active_record/railties/controller_runtime.rb +1 -1
  155. data/lib/active_record/railties/databases.rake +52 -41
  156. data/lib/active_record/readonly_attributes.rb +1 -1
  157. data/lib/active_record/reflection.rb +302 -115
  158. data/lib/active_record/relation.rb +187 -120
  159. data/lib/active_record/relation/batches.rb +141 -36
  160. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  161. data/lib/active_record/relation/calculations.rb +92 -117
  162. data/lib/active_record/relation/delegation.rb +8 -20
  163. data/lib/active_record/relation/finder_methods.rb +173 -89
  164. data/lib/active_record/relation/from_clause.rb +32 -0
  165. data/lib/active_record/relation/merger.rb +16 -42
  166. data/lib/active_record/relation/predicate_builder.rb +120 -107
  167. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  168. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  169. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  170. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  171. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  172. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  173. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  174. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  175. data/lib/active_record/relation/query_attribute.rb +19 -0
  176. data/lib/active_record/relation/query_methods.rb +308 -244
  177. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  178. data/lib/active_record/relation/spawn_methods.rb +4 -7
  179. data/lib/active_record/relation/where_clause.rb +174 -0
  180. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  181. data/lib/active_record/result.rb +11 -4
  182. data/lib/active_record/runtime_registry.rb +1 -1
  183. data/lib/active_record/sanitization.rb +105 -66
  184. data/lib/active_record/schema.rb +26 -22
  185. data/lib/active_record/schema_dumper.rb +54 -37
  186. data/lib/active_record/schema_migration.rb +11 -14
  187. data/lib/active_record/scoping.rb +34 -16
  188. data/lib/active_record/scoping/default.rb +28 -10
  189. data/lib/active_record/scoping/named.rb +59 -26
  190. data/lib/active_record/secure_token.rb +38 -0
  191. data/lib/active_record/serialization.rb +3 -5
  192. data/lib/active_record/statement_cache.rb +17 -15
  193. data/lib/active_record/store.rb +8 -3
  194. data/lib/active_record/suppressor.rb +58 -0
  195. data/lib/active_record/table_metadata.rb +69 -0
  196. data/lib/active_record/tasks/database_tasks.rb +66 -49
  197. data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
  198. data/lib/active_record/tasks/postgresql_database_tasks.rb +12 -3
  199. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  200. data/lib/active_record/timestamp.rb +20 -9
  201. data/lib/active_record/touch_later.rb +63 -0
  202. data/lib/active_record/transactions.rb +139 -57
  203. data/lib/active_record/type.rb +66 -17
  204. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  205. data/lib/active_record/type/date.rb +2 -45
  206. data/lib/active_record/type/date_time.rb +2 -49
  207. data/lib/active_record/type/internal/abstract_json.rb +33 -0
  208. data/lib/active_record/type/internal/timezone.rb +15 -0
  209. data/lib/active_record/type/serialized.rb +15 -14
  210. data/lib/active_record/type/time.rb +10 -16
  211. data/lib/active_record/type/type_map.rb +4 -4
  212. data/lib/active_record/type_caster.rb +7 -0
  213. data/lib/active_record/type_caster/connection.rb +29 -0
  214. data/lib/active_record/type_caster/map.rb +19 -0
  215. data/lib/active_record/validations.rb +33 -32
  216. data/lib/active_record/validations/absence.rb +23 -0
  217. data/lib/active_record/validations/associated.rb +10 -3
  218. data/lib/active_record/validations/length.rb +24 -0
  219. data/lib/active_record/validations/presence.rb +11 -12
  220. data/lib/active_record/validations/uniqueness.rb +33 -33
  221. data/lib/rails/generators/active_record/migration.rb +15 -0
  222. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -5
  223. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  224. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
  225. data/lib/rails/generators/active_record/model/model_generator.rb +33 -16
  226. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  227. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  228. metadata +58 -34
  229. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  230. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  231. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  232. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  233. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  234. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  235. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  236. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  237. data/lib/active_record/type/big_integer.rb +0 -13
  238. data/lib/active_record/type/binary.rb +0 -50
  239. data/lib/active_record/type/boolean.rb +0 -31
  240. data/lib/active_record/type/decimal.rb +0 -64
  241. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  242. data/lib/active_record/type/decorator.rb +0 -14
  243. data/lib/active_record/type/float.rb +0 -19
  244. data/lib/active_record/type/integer.rb +0 -59
  245. data/lib/active_record/type/mutable.rb +0 -16
  246. data/lib/active_record/type/numeric.rb +0 -36
  247. data/lib/active_record/type/string.rb +0 -40
  248. data/lib/active_record/type/text.rb +0 -11
  249. data/lib/active_record/type/time_value.rb +0 -38
  250. data/lib/active_record/type/unsigned_integer.rb +0 -15
  251. 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,27 @@ 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
+ pk = primary_keys(table_name)
133
+ pk = pk.first unless pk.size > 1
134
+ pk
99
135
  end
100
136
 
101
137
  # Creates a new table with the name +table_name+. +table_name+ may either
102
138
  # be a String or a Symbol.
103
139
  #
104
- # There are two ways to work with +create_table+. You can use the block
140
+ # There are two ways to work with #create_table. You can use the block
105
141
  # form or the regular form, like this:
106
142
  #
107
143
  # === Block form
@@ -133,13 +169,16 @@ module ActiveRecord
133
169
  # The +options+ hash can include the following keys:
134
170
  # [<tt>:id</tt>]
135
171
  # 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.
172
+ # Join tables for {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many] should set it to false.
173
+ #
174
+ # A Symbol can be used to specify the type of the generated primary key column.
137
175
  # [<tt>:primary_key</tt>]
138
176
  # The name of the primary key, if one is to be added automatically.
139
177
  # Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
140
178
  #
141
179
  # Note that Active Record models will automatically detect their
142
- # primary key. This can be avoided by using +self.primary_key=+ on the model
180
+ # primary key. This can be avoided by using
181
+ # {self.primary_key=}[rdoc-ref:AttributeMethods::PrimaryKey::ClassMethods#primary_key=] on the model
143
182
  # to define the key explicitly.
144
183
  #
145
184
  # [<tt>:options</tt>]
@@ -161,7 +200,7 @@ module ActiveRecord
161
200
  # generates:
162
201
  #
163
202
  # CREATE TABLE suppliers (
164
- # id int(11) DEFAULT NULL auto_increment PRIMARY KEY
203
+ # id int auto_increment PRIMARY KEY
165
204
  # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
166
205
  #
167
206
  # ====== Rename the primary key column
@@ -173,10 +212,23 @@ module ActiveRecord
173
212
  # generates:
174
213
  #
175
214
  # CREATE TABLE objects (
176
- # guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
215
+ # guid int auto_increment PRIMARY KEY,
177
216
  # name varchar(80)
178
217
  # )
179
218
  #
219
+ # ====== Change the primary key column type
220
+ #
221
+ # create_table(:tags, id: :string) do |t|
222
+ # t.column :label, :string
223
+ # end
224
+ #
225
+ # generates:
226
+ #
227
+ # CREATE TABLE tags (
228
+ # id varchar PRIMARY KEY,
229
+ # label varchar
230
+ # )
231
+ #
180
232
  # ====== Do not add a primary key column
181
233
  #
182
234
  # create_table(:categories_suppliers, id: false) do |t|
@@ -202,33 +254,41 @@ module ActiveRecord
202
254
  # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
203
255
  #
204
256
  # 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]
257
+ def create_table(table_name, comment: nil, **options)
258
+ td = create_table_definition table_name, options[:temporary], options[:options], options[:as], comment: comment
207
259
 
208
260
  if options[:id] != false && !options[:as]
209
261
  pk = options.fetch(:primary_key) do
210
262
  Base.get_primary_key table_name.to_s.singularize
211
263
  end
212
264
 
213
- td.primary_key pk, options.fetch(:id, :primary_key), options
265
+ if pk.is_a?(Array)
266
+ td.primary_keys pk
267
+ else
268
+ td.primary_key pk, options.fetch(:id, :primary_key), options
269
+ end
214
270
  end
215
271
 
216
272
  yield td if block_given?
217
273
 
218
- if options[:force] && table_exists?(table_name)
274
+ if options[:force] && data_source_exists?(table_name)
219
275
  drop_table(table_name, options)
220
276
  end
221
277
 
222
278
  result = execute schema_creation.accept td
223
279
 
224
280
  unless supports_indexes_in_create?
225
- td.indexes.each_pair do |column_name, index_options|
281
+ td.indexes.each do |column_name, index_options|
226
282
  add_index(table_name, column_name, index_options)
227
283
  end
228
284
  end
229
285
 
230
- td.foreign_keys.each do |other_table_name, foreign_key_options|
231
- add_foreign_key(table_name, other_table_name, foreign_key_options)
286
+ if supports_comments? && !supports_comments_in_create?
287
+ change_table_comment(table_name, comment) if comment.present?
288
+
289
+ td.columns.each do |column|
290
+ change_column_comment(table_name, column.name, column.comment) if column.comment.present?
291
+ end
232
292
  end
233
293
 
234
294
  result
@@ -253,7 +313,7 @@ module ActiveRecord
253
313
  # Set to true to drop the table before creating it.
254
314
  # Defaults to false.
255
315
  #
256
- # Note that +create_join_table+ does not create any indices by default; you can use
316
+ # Note that #create_join_table does not create any indices by default; you can use
257
317
  # its block form to do so yourself:
258
318
  #
259
319
  # create_join_table :products, :categories do |t|
@@ -277,22 +337,23 @@ module ActiveRecord
277
337
 
278
338
  column_options = options.delete(:column_options) || {}
279
339
  column_options.reverse_merge!(null: false)
340
+ type = column_options.delete(:type) || :integer
280
341
 
281
342
  t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
282
343
 
283
344
  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
345
+ td.send type, t1_column, column_options
346
+ td.send type, t2_column, column_options
286
347
  yield td if block_given?
287
348
  end
288
349
  end
289
350
 
290
351
  # Drops the join table specified by the given arguments.
291
- # See +create_join_table+ for details.
352
+ # See #create_join_table for details.
292
353
  #
293
354
  # Although this command ignores the block if one is given, it can be helpful
294
355
  # 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.
356
+ # In that case, the block will be used by #create_join_table.
296
357
  def drop_join_table(table_1, table_2, options = {})
297
358
  join_table_name = find_join_table_name(table_1, table_2, options)
298
359
  drop_table(join_table_name)
@@ -310,7 +371,7 @@ module ActiveRecord
310
371
  # [<tt>:bulk</tt>]
311
372
  # Set this to true to make this a bulk alter query, such as
312
373
  #
313
- # ALTER TABLE `users` ADD COLUMN age INT(11), ADD COLUMN birthdate DATETIME ...
374
+ # ALTER TABLE `users` ADD COLUMN age INT, ADD COLUMN birthdate DATETIME ...
314
375
  #
315
376
  # Defaults to false.
316
377
  #
@@ -391,16 +452,90 @@ module ActiveRecord
391
452
  # [<tt>:force</tt>]
392
453
  # Set to +:cascade+ to drop dependent objects as well.
393
454
  # Defaults to false.
455
+ # [<tt>:if_exists</tt>]
456
+ # Set to +true+ to only drop the table if it exists.
457
+ # Defaults to false.
394
458
  #
395
459
  # Although this command ignores most +options+ and the block if one is given,
396
460
  # 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.
461
+ # In that case, +options+ and the block will be used by #create_table.
398
462
  def drop_table(table_name, options = {})
399
- execute "DROP TABLE #{quote_table_name(table_name)}"
463
+ execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
400
464
  end
401
465
 
402
- # Adds a new column to the named table.
403
- # See TableDefinition#column for details of the options you can use.
466
+ # Add a new +type+ column named +column_name+ to +table_name+.
467
+ #
468
+ # The +type+ parameter is normally one of the migrations native types,
469
+ # which is one of the following:
470
+ # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
471
+ # <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
472
+ # <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
473
+ # <tt>:binary</tt>, <tt>:boolean</tt>.
474
+ #
475
+ # You may use a type not in this list as long as it is supported by your
476
+ # database (for example, "polygon" in MySQL), but this will not be database
477
+ # agnostic and should usually be avoided.
478
+ #
479
+ # Available options are (none of these exists by default):
480
+ # * <tt>:limit</tt> -
481
+ # Requests a maximum column length. This is number of characters for a <tt>:string</tt> column
482
+ # and number of bytes for <tt>:text</tt>, <tt>:binary</tt> and <tt>:integer</tt> columns.
483
+ # * <tt>:default</tt> -
484
+ # The column's default value. Use nil for NULL.
485
+ # * <tt>:null</tt> -
486
+ # Allows or disallows +NULL+ values in the column. This option could
487
+ # have been named <tt>:null_allowed</tt>.
488
+ # * <tt>:precision</tt> -
489
+ # Specifies the precision for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
490
+ # * <tt>:scale</tt> -
491
+ # Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
492
+ #
493
+ # Note: The precision is the total number of significant digits
494
+ # and the scale is the number of digits that can be stored following
495
+ # the decimal point. For example, the number 123.45 has a precision of 5
496
+ # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
497
+ # range from -999.99 to 999.99.
498
+ #
499
+ # Please be aware of different RDBMS implementations behavior with
500
+ # <tt>:decimal</tt> columns:
501
+ # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
502
+ # <tt>:precision</tt>, and makes no comments about the requirements of
503
+ # <tt>:precision</tt>.
504
+ # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
505
+ # Default is (10,0).
506
+ # * PostgreSQL: <tt>:precision</tt> [1..infinity],
507
+ # <tt>:scale</tt> [0..infinity]. No default.
508
+ # * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
509
+ # but the maximum supported <tt>:precision</tt> is 16. No default.
510
+ # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
511
+ # Default is (38,0).
512
+ # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
513
+ # Default unknown.
514
+ # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
515
+ # Default (38,0).
516
+ #
517
+ # == Examples
518
+ #
519
+ # add_column(:users, :picture, :binary, limit: 2.megabytes)
520
+ # # ALTER TABLE "users" ADD "picture" blob(2097152)
521
+ #
522
+ # add_column(:articles, :status, :string, limit: 20, default: 'draft', null: false)
523
+ # # ALTER TABLE "articles" ADD "status" varchar(20) DEFAULT 'draft' NOT NULL
524
+ #
525
+ # add_column(:answers, :bill_gates_money, :decimal, precision: 15, scale: 2)
526
+ # # ALTER TABLE "answers" ADD "bill_gates_money" decimal(15,2)
527
+ #
528
+ # add_column(:measurements, :sensor_reading, :decimal, precision: 30, scale: 20)
529
+ # # ALTER TABLE "measurements" ADD "sensor_reading" decimal(30,20)
530
+ #
531
+ # # While :scale defaults to zero on most databases, it
532
+ # # probably wouldn't hurt to include it.
533
+ # add_column(:measurements, :huge_integer, :decimal, precision: 30)
534
+ # # ALTER TABLE "measurements" ADD "huge_integer" decimal(30)
535
+ #
536
+ # # Defines a column with a database-specific type.
537
+ # add_column(:shapes, :triangle, 'polygon')
538
+ # # ALTER TABLE "shapes" ADD "triangle" polygon
404
539
  def add_column(table_name, column_name, type, options = {})
405
540
  at = create_alter_table table_name
406
541
  at.add_column(column_name, type, options)
@@ -448,11 +583,16 @@ module ActiveRecord
448
583
  #
449
584
  # change_column_default(:users, :email, nil)
450
585
  #
451
- def change_column_default(table_name, column_name, default)
586
+ # Passing a hash containing +:from+ and +:to+ will make this change
587
+ # reversible in migration:
588
+ #
589
+ # change_column_default(:posts, :state, from: nil, to: "draft")
590
+ #
591
+ def change_column_default(table_name, column_name, default_or_changes)
452
592
  raise NotImplementedError, "change_column_default is not implemented"
453
593
  end
454
594
 
455
- # Sets or removes a +NOT NULL+ constraint on a column. The +null+ flag
595
+ # Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
456
596
  # indicates whether the value can be +NULL+. For example
457
597
  #
458
598
  # change_column_null(:users, :nickname, false)
@@ -464,7 +604,7 @@ module ActiveRecord
464
604
  # allows them to be +NULL+ (drops the constraint).
465
605
  #
466
606
  # The method accepts an optional fourth argument to replace existing
467
- # +NULL+s with some other value. Use that one when enabling the
607
+ # <tt>NULL</tt>s with some other value. Use that one when enabling the
468
608
  # constraint if needed, since otherwise those rows would not be valid.
469
609
  #
470
610
  # Please note the fourth argument does not set a column's default.
@@ -518,6 +658,8 @@ module ActiveRecord
518
658
  #
519
659
  # CREATE INDEX by_name ON accounts(name(10))
520
660
  #
661
+ # ====== Creating an index with specific key lengths for multiple keys
662
+ #
521
663
  # add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
522
664
  #
523
665
  # generates:
@@ -565,7 +707,7 @@ module ActiveRecord
565
707
  #
566
708
  # CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
567
709
  #
568
- # Note: only supported by MySQL. Supported: <tt>:fulltext</tt> and <tt>:spatial</tt> on MyISAM tables.
710
+ # Note: only supported by MySQL.
569
711
  def add_index(table_name, column_name, options = {})
570
712
  index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
571
713
  execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
@@ -573,15 +715,15 @@ module ActiveRecord
573
715
 
574
716
  # Removes the given index from the table.
575
717
  #
576
- # Removes the +index_accounts_on_column+ in the +accounts+ table.
718
+ # Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
577
719
  #
578
- # remove_index :accounts, :column
720
+ # remove_index :accounts, :branch_id
579
721
  #
580
- # Removes the index named +index_accounts_on_branch_id+ in the +accounts+ table.
722
+ # Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
581
723
  #
582
724
  # remove_index :accounts, column: :branch_id
583
725
  #
584
- # Removes the index named +index_accounts_on_branch_id_and_party_id+ in the +accounts+ table.
726
+ # Removes the index on +branch_id+ and +party_id+ in the +accounts+ table if exactly one such index exists.
585
727
  #
586
728
  # remove_index :accounts, column: [:branch_id, :party_id]
587
729
  #
@@ -590,10 +732,7 @@ module ActiveRecord
590
732
  # remove_index :accounts, name: :by_branch_party
591
733
  #
592
734
  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:
735
+ index_name = index_name_for_remove(table_name, options)
597
736
  execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
598
737
  end
599
738
 
@@ -623,7 +762,7 @@ module ActiveRecord
623
762
  raise ArgumentError, "You must specify the index name"
624
763
  end
625
764
  else
626
- index_name(table_name, :column => options)
765
+ index_name(table_name, index_name_options(options))
627
766
  end
628
767
  end
629
768
 
@@ -640,17 +779,20 @@ module ActiveRecord
640
779
  # Adds a reference. The reference column is an integer by default,
641
780
  # the <tt>:type</tt> option can be used to specify a different type.
642
781
  # 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.
782
+ # #add_reference and #add_belongs_to are acceptable.
644
783
  #
645
784
  # The +options+ hash can include the following keys:
646
785
  # [<tt>:type</tt>]
647
786
  # The reference column type. Defaults to +:integer+.
648
787
  # [<tt>:index</tt>]
649
- # Add an appropriate index. Defaults to false.
788
+ # Add an appropriate index. Defaults to true.
789
+ # See #add_index for usage of this option.
650
790
  # [<tt>:foreign_key</tt>]
651
- # Add an appropriate foreign key. Defaults to false.
791
+ # Add an appropriate foreign key constraint. Defaults to false.
652
792
  # [<tt>:polymorphic</tt>]
653
- # Wether an additional +_type+ column should be added. Defaults to false.
793
+ # Whether an additional +_type+ column should be added. Defaults to false.
794
+ # [<tt>:null</tt>]
795
+ # Whether the column allows nulls. Defaults to true.
654
796
  #
655
797
  # ====== Create a user_id integer column
656
798
  #
@@ -664,28 +806,29 @@ module ActiveRecord
664
806
  #
665
807
  # add_reference(:products, :supplier, polymorphic: true, index: true)
666
808
  #
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
809
+ # ====== Create a supplier_id column with a unique index
810
+ #
811
+ # add_reference(:products, :supplier, index: { unique: true })
812
+ #
813
+ # ====== Create a supplier_id column with a named index
814
+ #
815
+ # add_reference(:products, :supplier, index: { name: "my_supplier_index" })
816
+ #
817
+ # ====== Create a supplier_id column and appropriate foreign key
818
+ #
819
+ # add_reference(:products, :supplier, foreign_key: true)
820
+ #
821
+ # ====== Create a supplier_id column and a foreign key to the firms table
822
+ #
823
+ # add_reference(:products, :supplier, foreign_key: {to_table: :firms})
824
+ #
825
+ def add_reference(table_name, *args)
826
+ ReferenceDefinition.new(*args).add_to(update_table_definition(table_name, self))
684
827
  end
685
828
  alias :add_belongs_to :add_reference
686
829
 
687
830
  # 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.
831
+ # #remove_reference and #remove_belongs_to are acceptable.
689
832
  #
690
833
  # ====== Remove the reference
691
834
  #
@@ -699,19 +842,24 @@ module ActiveRecord
699
842
  #
700
843
  # remove_reference(:products, :user, index: true, foreign_key: true)
701
844
  #
702
- def remove_reference(table_name, ref_name, options = {})
703
- 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)
845
+ def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
846
+ if foreign_key
847
+ reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
848
+ if foreign_key.is_a?(Hash)
849
+ foreign_key_options = foreign_key
850
+ else
851
+ foreign_key_options = { to_table: reference_name }
852
+ end
853
+ remove_foreign_key(table_name, **foreign_key_options)
706
854
  end
707
855
 
708
856
  remove_column(table_name, "#{ref_name}_id")
709
- remove_column(table_name, "#{ref_name}_type") if options[:polymorphic]
857
+ remove_column(table_name, "#{ref_name}_type") if polymorphic
710
858
  end
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,29 @@ 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:
826
- sm_table = ActiveRecord::Migrator.schema_migrations_table_name
988
+ versions = ActiveRecord::SchemaMigration.order('version').pluck(:version)
989
+ insert_versions_sql(versions) if versions.any?
990
+ end
827
991
 
828
- ActiveRecord::SchemaMigration.order('version').map { |sm|
829
- "INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');"
830
- }.join "\n\n"
992
+ def insert_versions_sql(versions) # :nodoc:
993
+ sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
994
+
995
+ if versions.is_a?(Array)
996
+ sql = "INSERT INTO #{sm_table} (version) VALUES\n"
997
+ sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
998
+ sql << ";\n\n"
999
+ sql
1000
+ else
1001
+ "INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
1002
+ end
831
1003
  end
832
1004
 
833
1005
  # Should not be called normally, but this operation is non-destructive.
@@ -836,7 +1008,15 @@ module ActiveRecord
836
1008
  ActiveRecord::SchemaMigration.create_table
837
1009
  end
838
1010
 
839
- def assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths)
1011
+ def initialize_internal_metadata_table
1012
+ ActiveRecord::InternalMetadata.create_table
1013
+ end
1014
+
1015
+ def internal_string_options_for_primary_key # :nodoc:
1016
+ { primary_key: true }
1017
+ end
1018
+
1019
+ def assume_migrated_upto_version(version, migrations_paths)
840
1020
  migrations_paths = Array(migrations_paths)
841
1021
  version = version.to_i
842
1022
  sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
@@ -847,22 +1027,27 @@ module ActiveRecord
847
1027
  end
848
1028
 
849
1029
  unless migrated.include?(version)
850
- execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
1030
+ execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
851
1031
  end
852
1032
 
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
1033
+ inserting = (versions - migrated).select {|v| v < version}
1034
+ if inserting.any?
1035
+ if (duplicate = inserting.detect {|v| inserting.count(v) > 1})
1036
+ raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
1037
+ end
1038
+ if supports_multi_insert?
1039
+ execute insert_versions_sql(inserting)
1040
+ else
1041
+ inserting.each do |v|
1042
+ execute insert_versions_sql(v)
1043
+ end
860
1044
  end
861
1045
  end
862
1046
  end
863
1047
 
864
1048
  def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
865
- if native = native_database_types[type.to_sym]
1049
+ type = type.to_sym if type
1050
+ if native = native_database_types[type]
866
1051
  column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
867
1052
 
868
1053
  if type == :decimal # ignore limit, use precision and scale
@@ -878,6 +1063,12 @@ module ActiveRecord
878
1063
  raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
879
1064
  end
880
1065
 
1066
+ elsif [:datetime, :time].include?(type) && precision ||= native[:precision]
1067
+ if (0..6) === precision
1068
+ column_type_sql << "(#{precision})"
1069
+ else
1070
+ raise(ActiveRecordError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6")
1071
+ end
881
1072
  elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
882
1073
  column_type_sql << "(#{limit})"
883
1074
  end
@@ -898,14 +1089,14 @@ module ActiveRecord
898
1089
  columns
899
1090
  end
900
1091
 
901
- include TimestampDefaultDeprecation
902
1092
  # Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
903
- # Additional options (like <tt>null: false</tt>) are forwarded to #add_column.
1093
+ # Additional options (like +:null+) are forwarded to #add_column.
904
1094
  #
905
- # add_timestamps(:suppliers, null: false)
1095
+ # add_timestamps(:suppliers, null: true)
906
1096
  #
907
1097
  def add_timestamps(table_name, options = {})
908
- emit_warning_if_null_unspecified(:add_timestamps, options)
1098
+ options[:null] = false if options[:null].nil?
1099
+
909
1100
  add_column table_name, :created_at, :datetime, options
910
1101
  add_column table_name, :updated_at, :datetime, options
911
1102
  end
@@ -923,15 +1114,15 @@ module ActiveRecord
923
1114
  Table.new(table_name, base)
924
1115
  end
925
1116
 
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)
1117
+ def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
1118
+ column_names = index_column_names(column_name)
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, 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,60 +1140,92 @@ 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
961
- def add_index_sort_order(option_strings, column_names, options = {})
962
- if options.is_a?(Hash) && order = options[:order]
1166
+
1167
+ def add_index_sort_order(quoted_columns, **options)
1168
+ if order = options[:order]
963
1169
  case order
964
1170
  when Hash
965
- column_names.each {|name| option_strings[name] += " #{order[name].upcase}" if order.has_key?(name)}
1171
+ order = order.symbolize_keys
1172
+ quoted_columns.each { |name, column| column << " #{order[name].upcase}" if order[name].present? }
966
1173
  when String
967
- column_names.each {|name| option_strings[name] += " #{order.upcase}"}
1174
+ quoted_columns.each { |name, column| column << " #{order.upcase}" if order.present? }
968
1175
  end
969
1176
  end
970
1177
 
971
- return option_strings
1178
+ quoted_columns
972
1179
  end
973
1180
 
974
1181
  # Overridden by the MySQL adapter for supporting index lengths
975
- def quoted_columns_for_index(column_names, options = {})
976
- option_strings = Hash[column_names.map {|name| [name, '']}]
977
-
978
- # add index sort order if supported
1182
+ def add_options_for_index_columns(quoted_columns, **options)
979
1183
  if supports_index_sort_order?
980
- option_strings = add_index_sort_order(option_strings, column_names, options)
1184
+ quoted_columns = add_index_sort_order(quoted_columns, options)
981
1185
  end
982
1186
 
983
- column_names.map {|name| quote_column_name(name) + option_strings[name]}
1187
+ quoted_columns
984
1188
  end
985
1189
 
986
- def options_include_default?(options)
987
- options.include?(:default) && !(options[:null] == false && options[:default].nil?)
1190
+ def quoted_columns_for_index(column_names, **options)
1191
+ return [column_names] if column_names.is_a?(String)
1192
+
1193
+ quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }]
1194
+ add_options_for_index_columns(quoted_columns, options).values
988
1195
  end
989
1196
 
990
1197
  def index_name_for_remove(table_name, options = {})
991
- index_name = index_name(table_name, options)
1198
+ return options[:name] if can_remove_index_by_name?(options)
992
1199
 
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)
1200
+ # if the adapter doesn't support the indexes call the best we can do
1201
+ # is return the default index name for the options provided
1202
+ return index_name(table_name, options) unless respond_to?(:indexes)
998
1203
 
999
- return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
1000
- end
1204
+ checks = []
1001
1205
 
1002
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
1206
+ if options.is_a?(Hash)
1207
+ checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
1208
+ column_names = index_column_names(options[:column])
1209
+ else
1210
+ column_names = index_column_names(options)
1211
+ end
1212
+
1213
+ if column_names.present?
1214
+ checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
1003
1215
  end
1004
1216
 
1005
- index_name
1217
+ raise ArgumentError, "No name or columns specified" if checks.none?
1218
+
1219
+ matching_indexes = indexes(table_name).select { |i| checks.all? { |check| check[i] } }
1220
+
1221
+ if matching_indexes.count > 1
1222
+ raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
1223
+ "Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
1224
+ elsif matching_indexes.none?
1225
+ raise ArgumentError, "No indexes found on #{table_name} with the options provided."
1226
+ else
1227
+ matching_indexes.first.name
1228
+ end
1006
1229
  end
1007
1230
 
1008
1231
  def rename_table_indexes(table_name, new_name)
@@ -1028,12 +1251,28 @@ module ActiveRecord
1028
1251
  end
1029
1252
 
1030
1253
  private
1031
- def create_table_definition(name, temporary, options, as = nil)
1032
- TableDefinition.new native_database_types, name, temporary, options, as
1254
+ def create_table_definition(*args)
1255
+ TableDefinition.new(*args)
1033
1256
  end
1034
1257
 
1035
1258
  def create_alter_table(name)
1036
- AlterTable.new create_table_definition(name, false, {})
1259
+ AlterTable.new create_table_definition(name)
1260
+ end
1261
+
1262
+ def index_column_names(column_names)
1263
+ if column_names.is_a?(String) && /\W/ === column_names
1264
+ column_names
1265
+ else
1266
+ Array(column_names)
1267
+ end
1268
+ end
1269
+
1270
+ def index_name_options(column_names) # :nodoc:
1271
+ if column_names.is_a?(String) && /\W/ === column_names
1272
+ column_names = column_names.scan(/\w+/).join('_')
1273
+ end
1274
+
1275
+ { column: column_names }
1037
1276
  end
1038
1277
 
1039
1278
  def foreign_key_name(table_name, options) # :nodoc:
@@ -1044,11 +1283,23 @@ module ActiveRecord
1044
1283
  end
1045
1284
  end
1046
1285
 
1047
- def validate_index_length!(table_name, new_name)
1286
+ def validate_index_length!(table_name, new_name) # :nodoc:
1048
1287
  if new_name.length > allowed_index_name_length
1049
1288
  raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
1050
1289
  end
1051
1290
  end
1291
+
1292
+ def extract_new_default_value(default_or_changes)
1293
+ if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
1294
+ default_or_changes[:to]
1295
+ else
1296
+ default_or_changes
1297
+ end
1298
+ end
1299
+
1300
+ def can_remove_index_by_name?(options)
1301
+ options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
1302
+ end
1052
1303
  end
1053
1304
  end
1054
1305
  end