activerecord 4.2.11.3 → 5.0.0.beta1

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 (229) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1029 -1349
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -7
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record.rb +7 -3
  7. data/lib/active_record/aggregations.rb +35 -25
  8. data/lib/active_record/association_relation.rb +2 -2
  9. data/lib/active_record/associations.rb +305 -204
  10. data/lib/active_record/associations/alias_tracker.rb +19 -16
  11. data/lib/active_record/associations/association.rb +10 -8
  12. data/lib/active_record/associations/association_scope.rb +73 -102
  13. data/lib/active_record/associations/belongs_to_association.rb +20 -32
  14. data/lib/active_record/associations/builder/association.rb +28 -34
  15. data/lib/active_record/associations/builder/belongs_to.rb +41 -18
  16. data/lib/active_record/associations/builder/collection_association.rb +8 -24
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +11 -11
  18. data/lib/active_record/associations/builder/has_many.rb +4 -4
  19. data/lib/active_record/associations/builder/has_one.rb +10 -5
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -9
  21. data/lib/active_record/associations/collection_association.rb +40 -43
  22. data/lib/active_record/associations/collection_proxy.rb +55 -29
  23. data/lib/active_record/associations/foreign_association.rb +1 -1
  24. data/lib/active_record/associations/has_many_association.rb +20 -71
  25. data/lib/active_record/associations/has_many_through_association.rb +8 -52
  26. data/lib/active_record/associations/has_one_association.rb +12 -5
  27. data/lib/active_record/associations/join_dependency.rb +28 -18
  28. data/lib/active_record/associations/join_dependency/join_association.rb +13 -12
  29. data/lib/active_record/associations/preloader.rb +13 -4
  30. data/lib/active_record/associations/preloader/association.rb +45 -51
  31. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  32. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  33. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  34. data/lib/active_record/associations/preloader/through_association.rb +5 -4
  35. data/lib/active_record/associations/singular_association.rb +6 -0
  36. data/lib/active_record/associations/through_association.rb +11 -3
  37. data/lib/active_record/attribute.rb +61 -17
  38. data/lib/active_record/attribute/user_provided_default.rb +23 -0
  39. data/lib/active_record/attribute_assignment.rb +27 -140
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods.rb +79 -26
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  43. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  44. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  45. data/lib/active_record/attribute_methods/query.rb +2 -2
  46. data/lib/active_record/attribute_methods/read.rb +26 -42
  47. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +42 -9
  49. data/lib/active_record/attribute_methods/write.rb +13 -24
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set.rb +30 -3
  52. data/lib/active_record/attribute_set/builder.rb +6 -4
  53. data/lib/active_record/attributes.rb +194 -81
  54. data/lib/active_record/autosave_association.rb +33 -15
  55. data/lib/active_record/base.rb +30 -18
  56. data/lib/active_record/callbacks.rb +36 -40
  57. data/lib/active_record/coders/yaml_column.rb +20 -8
  58. data/lib/active_record/collection_cache_key.rb +31 -0
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +431 -122
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +40 -22
  62. data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -8
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -38
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +229 -185
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +52 -13
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +275 -115
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +32 -33
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -32
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +384 -221
  70. data/lib/active_record/connection_adapters/column.rb +27 -41
  71. data/lib/active_record/connection_adapters/connection_specification.rb +2 -21
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +57 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +69 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +59 -0
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +22 -101
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +6 -10
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +3 -3
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  80. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +23 -57
  81. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  85. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  86. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  87. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +23 -16
  92. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  96. data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -11
  97. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -0
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +174 -128
  101. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +184 -112
  103. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  104. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  105. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +15 -0
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +134 -110
  107. data/lib/active_record/connection_adapters/statement_pool.rb +28 -11
  108. data/lib/active_record/connection_handling.rb +5 -5
  109. data/lib/active_record/core.rb +72 -104
  110. data/lib/active_record/counter_cache.rb +9 -20
  111. data/lib/active_record/dynamic_matchers.rb +1 -20
  112. data/lib/active_record/enum.rb +110 -76
  113. data/lib/active_record/errors.rb +72 -47
  114. data/lib/active_record/explain_registry.rb +1 -1
  115. data/lib/active_record/explain_subscriber.rb +1 -1
  116. data/lib/active_record/fixture_set/file.rb +19 -4
  117. data/lib/active_record/fixtures.rb +76 -40
  118. data/lib/active_record/gem_version.rb +4 -4
  119. data/lib/active_record/inheritance.rb +27 -40
  120. data/lib/active_record/integration.rb +4 -4
  121. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  122. data/lib/active_record/locale/en.yml +3 -2
  123. data/lib/active_record/locking/optimistic.rb +10 -14
  124. data/lib/active_record/locking/pessimistic.rb +1 -1
  125. data/lib/active_record/log_subscriber.rb +40 -22
  126. data/lib/active_record/migration.rb +304 -133
  127. data/lib/active_record/migration/command_recorder.rb +59 -18
  128. data/lib/active_record/migration/compatibility.rb +90 -0
  129. data/lib/active_record/model_schema.rb +92 -40
  130. data/lib/active_record/nested_attributes.rb +45 -34
  131. data/lib/active_record/null_relation.rb +15 -7
  132. data/lib/active_record/persistence.rb +112 -72
  133. data/lib/active_record/querying.rb +6 -5
  134. data/lib/active_record/railtie.rb +20 -13
  135. data/lib/active_record/railties/controller_runtime.rb +1 -1
  136. data/lib/active_record/railties/databases.rake +47 -38
  137. data/lib/active_record/readonly_attributes.rb +1 -1
  138. data/lib/active_record/reflection.rb +182 -57
  139. data/lib/active_record/relation.rb +152 -100
  140. data/lib/active_record/relation/batches.rb +133 -33
  141. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  142. data/lib/active_record/relation/calculations.rb +80 -101
  143. data/lib/active_record/relation/delegation.rb +6 -19
  144. data/lib/active_record/relation/finder_methods.rb +58 -46
  145. data/lib/active_record/relation/from_clause.rb +32 -0
  146. data/lib/active_record/relation/merger.rb +13 -42
  147. data/lib/active_record/relation/predicate_builder.rb +99 -105
  148. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  149. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +78 -0
  150. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  151. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  152. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  153. data/lib/active_record/relation/predicate_builder/range_handler.rb +17 -0
  154. data/lib/active_record/relation/query_attribute.rb +19 -0
  155. data/lib/active_record/relation/query_methods.rb +274 -238
  156. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  157. data/lib/active_record/relation/spawn_methods.rb +3 -6
  158. data/lib/active_record/relation/where_clause.rb +173 -0
  159. data/lib/active_record/relation/where_clause_factory.rb +37 -0
  160. data/lib/active_record/result.rb +4 -3
  161. data/lib/active_record/runtime_registry.rb +1 -1
  162. data/lib/active_record/sanitization.rb +94 -65
  163. data/lib/active_record/schema.rb +23 -22
  164. data/lib/active_record/schema_dumper.rb +33 -22
  165. data/lib/active_record/schema_migration.rb +10 -4
  166. data/lib/active_record/scoping.rb +17 -6
  167. data/lib/active_record/scoping/default.rb +19 -6
  168. data/lib/active_record/scoping/named.rb +39 -28
  169. data/lib/active_record/secure_token.rb +38 -0
  170. data/lib/active_record/serialization.rb +2 -4
  171. data/lib/active_record/statement_cache.rb +15 -13
  172. data/lib/active_record/store.rb +8 -3
  173. data/lib/active_record/suppressor.rb +54 -0
  174. data/lib/active_record/table_metadata.rb +64 -0
  175. data/lib/active_record/tasks/database_tasks.rb +30 -40
  176. data/lib/active_record/tasks/mysql_database_tasks.rb +7 -15
  177. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  178. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  179. data/lib/active_record/timestamp.rb +16 -9
  180. data/lib/active_record/touch_later.rb +58 -0
  181. data/lib/active_record/transactions.rb +138 -56
  182. data/lib/active_record/type.rb +66 -17
  183. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  184. data/lib/active_record/type/date.rb +2 -45
  185. data/lib/active_record/type/date_time.rb +2 -49
  186. data/lib/active_record/type/internal/abstract_json.rb +33 -0
  187. data/lib/active_record/type/internal/timezone.rb +15 -0
  188. data/lib/active_record/type/serialized.rb +9 -14
  189. data/lib/active_record/type/time.rb +3 -21
  190. data/lib/active_record/type/type_map.rb +4 -4
  191. data/lib/active_record/type_caster.rb +7 -0
  192. data/lib/active_record/type_caster/connection.rb +29 -0
  193. data/lib/active_record/type_caster/map.rb +19 -0
  194. data/lib/active_record/validations.rb +33 -32
  195. data/lib/active_record/validations/absence.rb +24 -0
  196. data/lib/active_record/validations/associated.rb +10 -3
  197. data/lib/active_record/validations/length.rb +36 -0
  198. data/lib/active_record/validations/presence.rb +12 -12
  199. data/lib/active_record/validations/uniqueness.rb +24 -21
  200. data/lib/rails/generators/active_record/migration.rb +7 -0
  201. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  202. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  203. data/lib/rails/generators/active_record/migration/templates/migration.rb +4 -1
  204. data/lib/rails/generators/active_record/model/model_generator.rb +21 -15
  205. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  206. metadata +50 -35
  207. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  208. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  209. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  210. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  211. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  212. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  213. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  214. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  215. data/lib/active_record/type/big_integer.rb +0 -13
  216. data/lib/active_record/type/binary.rb +0 -50
  217. data/lib/active_record/type/boolean.rb +0 -31
  218. data/lib/active_record/type/decimal.rb +0 -64
  219. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  220. data/lib/active_record/type/decorator.rb +0 -14
  221. data/lib/active_record/type/float.rb +0 -19
  222. data/lib/active_record/type/integer.rb +0 -59
  223. data/lib/active_record/type/mutable.rb +0 -16
  224. data/lib/active_record/type/numeric.rb +0 -36
  225. data/lib/active_record/type/string.rb +0 -40
  226. data/lib/active_record/type/text.rb +0 -11
  227. data/lib/active_record/type/time_value.rb +0 -38
  228. data/lib/active_record/type/unsigned_integer.rb +0 -15
  229. data/lib/active_record/type/value.rb +0 -110
@@ -6,45 +6,84 @@ 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)
9
+ def column_spec(column)
10
+ spec = prepare_column_options(column)
11
11
  (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k}: ")}
12
12
  spec
13
13
  end
14
14
 
15
- # This can be overridden on a Adapter level basis to support other
15
+ def column_spec_for_primary_key(column)
16
+ return if column.type == :integer
17
+ spec = { id: column.type.inspect }
18
+ spec.merge!(prepare_column_options(column).delete_if { |key, _| [:name, :type].include?(key) })
19
+ end
20
+
21
+ # This can be overridden on an Adapter level basis to support other
16
22
  # extended datatypes (Example: Adding an array option in the
17
- # PostgreSQLAdapter)
18
- def prepare_column_options(column, types)
23
+ # PostgreSQL::ColumnDumper)
24
+ def prepare_column_options(column)
19
25
  spec = {}
20
26
  spec[:name] = column.name.inspect
21
- spec[:type] = column.type.to_s
27
+ spec[:type] = schema_type(column)
22
28
  spec[:null] = 'false' unless column.null
23
29
 
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
30
+ if limit = schema_limit(column)
31
+ spec[:limit] = limit
32
+ end
33
+
34
+ if precision = schema_precision(column)
35
+ spec[:precision] = precision
36
+ end
37
+
38
+ if scale = schema_scale(column)
39
+ spec[:scale] = scale
40
+ end
28
41
 
29
42
  default = schema_default(column) if column.has_default?
30
43
  spec[:default] = default unless default.nil?
31
44
 
45
+ if collation = schema_collation(column)
46
+ spec[:collation] = collation
47
+ end
48
+
32
49
  spec
33
50
  end
34
51
 
35
52
  # Lists the valid migration options
36
53
  def migration_keys
37
- [:name, :limit, :precision, :scale, :default, :null]
54
+ [:name, :limit, :precision, :scale, :default, :null, :collation]
38
55
  end
39
56
 
40
57
  private
41
58
 
59
+ def schema_type(column)
60
+ column.type.to_s
61
+ end
62
+
63
+ def schema_limit(column)
64
+ limit = column.limit
65
+ limit.inspect if limit && limit != native_database_types[column.type][:limit]
66
+ end
67
+
68
+ def schema_precision(column)
69
+ column.precision.inspect if column.precision
70
+ end
71
+
72
+ def schema_scale(column)
73
+ column.scale.inspect if column.scale
74
+ end
75
+
42
76
  def schema_default(column)
43
- default = column.type_cast_from_database(column.default)
77
+ type = lookup_cast_type_from_column(column)
78
+ default = type.deserialize(column.default)
44
79
  unless default.nil?
45
- column.type_cast_for_schema(default)
80
+ type.type_cast_for_schema(default)
46
81
  end
47
82
  end
83
+
84
+ def schema_collation(column)
85
+ column.collation.inspect if column.collation
86
+ end
48
87
  end
49
88
  end
50
89
  end
@@ -14,15 +14,19 @@ module ActiveRecord
14
14
  {}
15
15
  end
16
16
 
17
+ def table_options(table_name)
18
+ nil
19
+ end
20
+
17
21
  # Truncates a table alias according to the limits of the current adapter.
18
22
  def table_alias_for(table_name)
19
23
  table_name[0...table_alias_length].tr('.', '_')
20
24
  end
21
25
 
22
26
  # Returns the relation names useable to back Active Record models.
23
- # For most adapters this means all tables and views.
27
+ # For most adapters this means all #tables and #views.
24
28
  def data_sources
25
- tables
29
+ tables | views
26
30
  end
27
31
 
28
32
  # Checks to see if the data source +name+ exists on the database.
@@ -33,6 +37,11 @@ module ActiveRecord
33
37
  data_sources.include?(name.to_s)
34
38
  end
35
39
 
40
+ # Returns an array of table names defined in the database.
41
+ def tables(name = nil)
42
+ raise NotImplementedError, "#tables is not implemented"
43
+ end
44
+
36
45
  # Checks to see if the table +table_name+ exists on the database.
37
46
  #
38
47
  # table_exists?(:developers)
@@ -41,6 +50,19 @@ module ActiveRecord
41
50
  tables.include?(table_name.to_s)
42
51
  end
43
52
 
53
+ # Returns an array of view names defined in the database.
54
+ def views
55
+ raise NotImplementedError, "#views is not implemented"
56
+ end
57
+
58
+ # Checks to see if the view +view_name+ exists on the database.
59
+ #
60
+ # view_exists?(:ebooks)
61
+ #
62
+ def view_exists?(view_name)
63
+ views.include?(view_name.to_s)
64
+ end
65
+
44
66
  # Returns an array of indexes for the given table.
45
67
  # def indexes(table_name, name = nil) end
46
68
 
@@ -60,11 +82,10 @@ module ActiveRecord
60
82
  #
61
83
  def index_exists?(table_name, column_name, options = {})
62
84
  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
85
  checks = []
65
- checks << lambda { |i| i.name == index_name }
66
86
  checks << lambda { |i| i.columns == column_names }
67
87
  checks << lambda { |i| i.unique } if options[:unique]
88
+ checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
68
89
 
69
90
  indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
70
91
  end
@@ -98,10 +119,16 @@ module ActiveRecord
98
119
  (!options.key?(:null) || c.null == options[:null]) }
99
120
  end
100
121
 
122
+ # Returns just a table's primary key
123
+ def primary_key(table_name)
124
+ pks = primary_keys(table_name)
125
+ pks.first if pks.one?
126
+ end
127
+
101
128
  # Creates a new table with the name +table_name+. +table_name+ may either
102
129
  # be a String or a Symbol.
103
130
  #
104
- # There are two ways to work with +create_table+. You can use the block
131
+ # There are two ways to work with #create_table. You can use the block
105
132
  # form or the regular form, like this:
106
133
  #
107
134
  # === Block form
@@ -133,13 +160,16 @@ module ActiveRecord
133
160
  # The +options+ hash can include the following keys:
134
161
  # [<tt>:id</tt>]
135
162
  # 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.
163
+ # Join tables for {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many] should set it to false.
164
+ #
165
+ # A Symbol can be used to specify the type of the generated primary key column.
137
166
  # [<tt>:primary_key</tt>]
138
167
  # The name of the primary key, if one is to be added automatically.
139
168
  # Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
140
169
  #
141
170
  # Note that Active Record models will automatically detect their
142
- # primary key. This can be avoided by using +self.primary_key=+ on the model
171
+ # primary key. This can be avoided by using
172
+ # {self.primary_key=}[rdoc-ref:AttributeMethods::PrimaryKey::ClassMethods#primary_key=] on the model
143
173
  # to define the key explicitly.
144
174
  #
145
175
  # [<tt>:options</tt>]
@@ -161,7 +191,7 @@ module ActiveRecord
161
191
  # generates:
162
192
  #
163
193
  # CREATE TABLE suppliers (
164
- # id int(11) DEFAULT NULL auto_increment PRIMARY KEY
194
+ # id int auto_increment PRIMARY KEY
165
195
  # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
166
196
  #
167
197
  # ====== Rename the primary key column
@@ -173,10 +203,23 @@ module ActiveRecord
173
203
  # generates:
174
204
  #
175
205
  # CREATE TABLE objects (
176
- # guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
206
+ # guid int auto_increment PRIMARY KEY,
177
207
  # name varchar(80)
178
208
  # )
179
209
  #
210
+ # ====== Change the primary key column type
211
+ #
212
+ # create_table(:tags, id: :string) do |t|
213
+ # t.column :label, :string
214
+ # end
215
+ #
216
+ # generates:
217
+ #
218
+ # CREATE TABLE tags (
219
+ # id varchar PRIMARY KEY,
220
+ # label varchar
221
+ # )
222
+ #
180
223
  # ====== Do not add a primary key column
181
224
  #
182
225
  # create_table(:categories_suppliers, id: false) do |t|
@@ -210,12 +253,16 @@ module ActiveRecord
210
253
  Base.get_primary_key table_name.to_s.singularize
211
254
  end
212
255
 
213
- td.primary_key pk, options.fetch(:id, :primary_key), options
256
+ if pk.is_a?(Array)
257
+ td.primary_keys pk
258
+ else
259
+ td.primary_key pk, options.fetch(:id, :primary_key), options
260
+ end
214
261
  end
215
262
 
216
263
  yield td if block_given?
217
264
 
218
- if options[:force] && table_exists?(table_name)
265
+ if options[:force] && data_source_exists?(table_name)
219
266
  drop_table(table_name, options)
220
267
  end
221
268
 
@@ -227,10 +274,6 @@ module ActiveRecord
227
274
  end
228
275
  end
229
276
 
230
- td.foreign_keys.each do |other_table_name, foreign_key_options|
231
- add_foreign_key(table_name, other_table_name, foreign_key_options)
232
- end
233
-
234
277
  result
235
278
  end
236
279
 
@@ -253,7 +296,7 @@ module ActiveRecord
253
296
  # Set to true to drop the table before creating it.
254
297
  # Defaults to false.
255
298
  #
256
- # Note that +create_join_table+ does not create any indices by default; you can use
299
+ # Note that #create_join_table does not create any indices by default; you can use
257
300
  # its block form to do so yourself:
258
301
  #
259
302
  # create_join_table :products, :categories do |t|
@@ -288,11 +331,11 @@ module ActiveRecord
288
331
  end
289
332
 
290
333
  # Drops the join table specified by the given arguments.
291
- # See +create_join_table+ for details.
334
+ # See #create_join_table for details.
292
335
  #
293
336
  # Although this command ignores the block if one is given, it can be helpful
294
337
  # 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.
338
+ # In that case, the block will be used by #create_join_table.
296
339
  def drop_join_table(table_1, table_2, options = {})
297
340
  join_table_name = find_join_table_name(table_1, table_2, options)
298
341
  drop_table(join_table_name)
@@ -310,7 +353,7 @@ module ActiveRecord
310
353
  # [<tt>:bulk</tt>]
311
354
  # Set this to true to make this a bulk alter query, such as
312
355
  #
313
- # ALTER TABLE `users` ADD COLUMN age INT(11), ADD COLUMN birthdate DATETIME ...
356
+ # ALTER TABLE `users` ADD COLUMN age INT, ADD COLUMN birthdate DATETIME ...
314
357
  #
315
358
  # Defaults to false.
316
359
  #
@@ -391,16 +434,92 @@ module ActiveRecord
391
434
  # [<tt>:force</tt>]
392
435
  # Set to +:cascade+ to drop dependent objects as well.
393
436
  # Defaults to false.
437
+ # [<tt>:if_exists</tt>]
438
+ # Set to +true+ to only drop the table if it exists.
439
+ # Defaults to false.
394
440
  #
395
441
  # Although this command ignores most +options+ and the block if one is given,
396
442
  # 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.
443
+ # In that case, +options+ and the block will be used by #create_table.
398
444
  def drop_table(table_name, options = {})
399
- execute "DROP TABLE #{quote_table_name(table_name)}"
445
+ execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
400
446
  end
401
447
 
402
- # Adds a new column to the named table.
403
- # See TableDefinition#column for details of the options you can use.
448
+ # Add a new +type+ column named +column_name+ to +table_name+.
449
+ #
450
+ # The +type+ parameter is normally one of the migrations native types,
451
+ # which is one of the following:
452
+ # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
453
+ # <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>,
454
+ # <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
455
+ # <tt>:binary</tt>, <tt>:boolean</tt>.
456
+ #
457
+ # You may use a type not in this list as long as it is supported by your
458
+ # database (for example, "polygon" in MySQL), but this will not be database
459
+ # agnostic and should usually be avoided.
460
+ #
461
+ # Available options are (none of these exists by default):
462
+ # * <tt>:limit</tt> -
463
+ # Requests a maximum column length. This is number of characters for a <tt>:string</tt> column
464
+ # and number of bytes for <tt>:text</tt>, <tt>:binary</tt> and <tt>:integer</tt> columns.
465
+ # * <tt>:default</tt> -
466
+ # The column's default value. Use nil for NULL.
467
+ # * <tt>:null</tt> -
468
+ # Allows or disallows +NULL+ values in the column. This option could
469
+ # have been named <tt>:null_allowed</tt>.
470
+ # * <tt>:precision</tt> -
471
+ # Specifies the precision for a <tt>:decimal</tt> column.
472
+ # * <tt>:scale</tt> -
473
+ # Specifies the scale for a <tt>:decimal</tt> column.
474
+ #
475
+ # Note: The precision is the total number of significant digits
476
+ # and the scale is the number of digits that can be stored following
477
+ # the decimal point. For example, the number 123.45 has a precision of 5
478
+ # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
479
+ # range from -999.99 to 999.99.
480
+ #
481
+ # Please be aware of different RDBMS implementations behavior with
482
+ # <tt>:decimal</tt> columns:
483
+ # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
484
+ # <tt>:precision</tt>, and makes no comments about the requirements of
485
+ # <tt>:precision</tt>.
486
+ # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
487
+ # Default is (10,0).
488
+ # * PostgreSQL: <tt>:precision</tt> [1..infinity],
489
+ # <tt>:scale</tt> [0..infinity]. No default.
490
+ # * SQLite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used.
491
+ # Internal storage as strings. No default.
492
+ # * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
493
+ # but the maximum supported <tt>:precision</tt> is 16. No default.
494
+ # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
495
+ # Default is (38,0).
496
+ # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
497
+ # Default unknown.
498
+ # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
499
+ # Default (38,0).
500
+ #
501
+ # == Examples
502
+ #
503
+ # add_column(:users, :picture, :binary, limit: 2.megabytes)
504
+ # # ALTER TABLE "users" ADD "picture" blob(2097152)
505
+ #
506
+ # add_column(:articles, :status, :string, limit: 20, default: 'draft', null: false)
507
+ # # ALTER TABLE "articles" ADD "status" varchar(20) DEFAULT 'draft' NOT NULL
508
+ #
509
+ # add_column(:answers, :bill_gates_money, :decimal, precision: 15, scale: 2)
510
+ # # ALTER TABLE "answers" ADD "bill_gates_money" decimal(15,2)
511
+ #
512
+ # add_column(:measurements, :sensor_reading, :decimal, precision: 30, scale: 20)
513
+ # # ALTER TABLE "measurements" ADD "sensor_reading" decimal(30,20)
514
+ #
515
+ # # While :scale defaults to zero on most databases, it
516
+ # # probably wouldn't hurt to include it.
517
+ # add_column(:measurements, :huge_integer, :decimal, precision: 30)
518
+ # # ALTER TABLE "measurements" ADD "huge_integer" decimal(30)
519
+ #
520
+ # # Defines a column with a database-specific type.
521
+ # add_column(:shapes, :triangle, 'polygon')
522
+ # # ALTER TABLE "shapes" ADD "triangle" polygon
404
523
  def add_column(table_name, column_name, type, options = {})
405
524
  at = create_alter_table table_name
406
525
  at.add_column(column_name, type, options)
@@ -448,11 +567,16 @@ module ActiveRecord
448
567
  #
449
568
  # change_column_default(:users, :email, nil)
450
569
  #
451
- def change_column_default(table_name, column_name, default)
570
+ # Passing a hash containing +:from+ and +:to+ will make this change
571
+ # reversible in migration:
572
+ #
573
+ # change_column_default(:posts, :state, from: nil, to: "draft")
574
+ #
575
+ def change_column_default(table_name, column_name, default_or_changes)
452
576
  raise NotImplementedError, "change_column_default is not implemented"
453
577
  end
454
578
 
455
- # Sets or removes a +NOT NULL+ constraint on a column. The +null+ flag
579
+ # Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
456
580
  # indicates whether the value can be +NULL+. For example
457
581
  #
458
582
  # change_column_null(:users, :nickname, false)
@@ -464,7 +588,7 @@ module ActiveRecord
464
588
  # allows them to be +NULL+ (drops the constraint).
465
589
  #
466
590
  # The method accepts an optional fourth argument to replace existing
467
- # +NULL+s with some other value. Use that one when enabling the
591
+ # <tt>NULL</tt>s with some other value. Use that one when enabling the
468
592
  # constraint if needed, since otherwise those rows would not be valid.
469
593
  #
470
594
  # Please note the fourth argument does not set a column's default.
@@ -518,6 +642,8 @@ module ActiveRecord
518
642
  #
519
643
  # CREATE INDEX by_name ON accounts(name(10))
520
644
  #
645
+ # ====== Creating an index with specific key lengths for multiple keys
646
+ #
521
647
  # add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
522
648
  #
523
649
  # generates:
@@ -573,15 +699,15 @@ module ActiveRecord
573
699
 
574
700
  # Removes the given index from the table.
575
701
  #
576
- # Removes the +index_accounts_on_column+ in the +accounts+ table.
702
+ # Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
577
703
  #
578
- # remove_index :accounts, :column
704
+ # remove_index :accounts, :branch_id
579
705
  #
580
- # Removes the index named +index_accounts_on_branch_id+ in the +accounts+ table.
706
+ # Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
581
707
  #
582
708
  # remove_index :accounts, column: :branch_id
583
709
  #
584
- # Removes the index named +index_accounts_on_branch_id_and_party_id+ in the +accounts+ table.
710
+ # Removes the index on +branch_id+ and +party_id+ in the +accounts+ table if exactly one such index exists.
585
711
  #
586
712
  # remove_index :accounts, column: [:branch_id, :party_id]
587
713
  #
@@ -590,10 +716,7 @@ module ActiveRecord
590
716
  # remove_index :accounts, name: :by_branch_party
591
717
  #
592
718
  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:
719
+ index_name = index_name_for_remove(table_name, options)
597
720
  execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
598
721
  end
599
722
 
@@ -640,7 +763,7 @@ module ActiveRecord
640
763
  # Adds a reference. The reference column is an integer by default,
641
764
  # the <tt>:type</tt> option can be used to specify a different type.
642
765
  # 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.
766
+ # #add_reference and #add_belongs_to are acceptable.
644
767
  #
645
768
  # The +options+ hash can include the following keys:
646
769
  # [<tt>:type</tt>]
@@ -648,9 +771,11 @@ module ActiveRecord
648
771
  # [<tt>:index</tt>]
649
772
  # Add an appropriate index. Defaults to false.
650
773
  # [<tt>:foreign_key</tt>]
651
- # Add an appropriate foreign key. Defaults to false.
774
+ # Add an appropriate foreign key constraint. Defaults to false.
652
775
  # [<tt>:polymorphic</tt>]
653
- # Wether an additional +_type+ column should be added. Defaults to false.
776
+ # Whether an additional +_type+ column should be added. Defaults to false.
777
+ # [<tt>:null</tt>]
778
+ # Whether the column allows nulls. Defaults to true.
654
779
  #
655
780
  # ====== Create a user_id integer column
656
781
  #
@@ -664,28 +789,21 @@ module ActiveRecord
664
789
  #
665
790
  # add_reference(:products, :supplier, polymorphic: true, index: true)
666
791
  #
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
792
+ # ====== Create a supplier_id column and appropriate foreign key
793
+ #
794
+ # add_reference(:products, :supplier, foreign_key: true)
795
+ #
796
+ # ====== Create a supplier_id column and a foreign key to the firms table
797
+ #
798
+ # add_reference(:products, :supplier, foreign_key: {to_table: :firms})
799
+ #
800
+ def add_reference(table_name, *args)
801
+ ReferenceDefinition.new(*args).add_to(update_table_definition(table_name, self))
684
802
  end
685
803
  alias :add_belongs_to :add_reference
686
804
 
687
805
  # 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.
806
+ # #remove_reference and #remove_belongs_to are acceptable.
689
807
  #
690
808
  # ====== Remove the reference
691
809
  #
@@ -701,8 +819,8 @@ module ActiveRecord
701
819
  #
702
820
  def remove_reference(table_name, ref_name, options = {})
703
821
  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)
822
+ reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
823
+ remove_foreign_key(table_name, reference_name)
706
824
  end
707
825
 
708
826
  remove_column(table_name, "#{ref_name}_id")
@@ -711,7 +829,7 @@ module ActiveRecord
711
829
  alias :remove_belongs_to :remove_reference
712
830
 
713
831
  # Returns an array of foreign keys for the given table.
714
- # The foreign keys are represented as +ForeignKeyDefinition+ objects.
832
+ # The foreign keys are represented as ForeignKeyDefinition objects.
715
833
  def foreign_keys(table_name)
716
834
  raise NotImplementedError, "foreign_keys is not implemented"
717
835
  end
@@ -733,7 +851,7 @@ module ActiveRecord
733
851
  #
734
852
  # ====== Creating a foreign key on a specific column
735
853
  #
736
- # add_foreign_key :articles, :users, column: :author_id, primary_key: :lng_id
854
+ # add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id"
737
855
  #
738
856
  # generates:
739
857
  #
@@ -755,28 +873,23 @@ module ActiveRecord
755
873
  # [<tt>:name</tt>]
756
874
  # The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
757
875
  # [<tt>:on_delete</tt>]
758
- # Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade:+ and +:restrict+
876
+ # Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
759
877
  # [<tt>:on_update</tt>]
760
- # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade:+ and +:restrict+
878
+ # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
761
879
  def add_foreign_key(from_table, to_table, options = {})
762
880
  return unless supports_foreign_keys?
763
881
 
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
- }
882
+ options = foreign_key_options(from_table, to_table, options)
773
883
  at = create_alter_table from_table
774
884
  at.add_foreign_key to_table, options
775
885
 
776
886
  execute schema_creation.accept(at)
777
887
  end
778
888
 
779
- # Removes the given foreign key from the table.
889
+ # Removes the given foreign key from the table. Any option parameters provided
890
+ # will be used to re-add the foreign key in case of a migration rollback.
891
+ # It is recommended that you provide any options used when creating the foreign
892
+ # key so that the migration can be reverted properly.
780
893
  #
781
894
  # Removes the foreign key on +accounts.branch_id+.
782
895
  #
@@ -790,24 +903,11 @@ module ActiveRecord
790
903
  #
791
904
  # remove_foreign_key :accounts, name: :special_fk_name
792
905
  #
906
+ # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
793
907
  def remove_foreign_key(from_table, options_or_to_table = {})
794
908
  return unless supports_foreign_keys?
795
909
 
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
910
+ fk_name_to_delete = foreign_key_for!(from_table, options_or_to_table).name
811
911
 
812
912
  at = create_alter_table from_table
813
913
  at.drop_foreign_key fk_name_to_delete
@@ -815,6 +915,31 @@ module ActiveRecord
815
915
  execute schema_creation.accept(at)
816
916
  end
817
917
 
918
+ # Checks to see if a foreign key exists on a table for a given foreign key definition.
919
+ #
920
+ # # Check a foreign key exists
921
+ # foreign_key_exists?(:accounts, :branches)
922
+ #
923
+ # # Check a foreign key on a specified column exists
924
+ # foreign_key_exists?(:accounts, column: :owner_id)
925
+ #
926
+ # # Check a foreign key with a custom name exists
927
+ # foreign_key_exists?(:accounts, name: "special_fk_name")
928
+ #
929
+ def foreign_key_exists?(from_table, options_or_to_table = {})
930
+ foreign_key_for(from_table, options_or_to_table).present?
931
+ end
932
+
933
+ def foreign_key_for(from_table, options_or_to_table = {}) # :nodoc:
934
+ return unless supports_foreign_keys?
935
+ foreign_keys(from_table).detect {|fk| fk.defined_for? options_or_to_table }
936
+ end
937
+
938
+ def foreign_key_for!(from_table, options_or_to_table = {}) # :nodoc:
939
+ foreign_key_for(from_table, options_or_to_table) or \
940
+ raise ArgumentError, "Table '#{from_table}' has no foreign key for #{options_or_to_table}"
941
+ end
942
+
818
943
  def foreign_key_column_for(table_name) # :nodoc:
819
944
  prefix = Base.table_name_prefix
820
945
  suffix = Base.table_name_suffix
@@ -822,6 +947,13 @@ module ActiveRecord
822
947
  "#{name.singularize}_id"
823
948
  end
824
949
 
950
+ def foreign_key_options(from_table, to_table, options) # :nodoc:
951
+ options = options.dup
952
+ options[:column] ||= foreign_key_column_for(to_table)
953
+ options[:name] ||= foreign_key_name(from_table, options)
954
+ options
955
+ end
956
+
825
957
  def dump_schema_information #:nodoc:
826
958
  sm_table = ActiveRecord::Migrator.schema_migrations_table_name
827
959
 
@@ -836,14 +968,15 @@ module ActiveRecord
836
968
  ActiveRecord::SchemaMigration.create_table
837
969
  end
838
970
 
839
- def assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths)
971
+ def assume_migrated_upto_version(version, migrations_paths)
840
972
  migrations_paths = Array(migrations_paths)
841
973
  version = version.to_i
842
974
  sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
843
975
 
844
976
  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
977
+ paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" }
978
+ versions = Dir[*paths].map do |filename|
979
+ filename.split('/').last.split('_').first.to_i
847
980
  end
848
981
 
849
982
  unless migrated.include?(version)
@@ -878,6 +1011,12 @@ module ActiveRecord
878
1011
  raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
879
1012
  end
880
1013
 
1014
+ elsif [:datetime, :time].include?(type) && precision ||= native[:precision]
1015
+ if (0..6) === precision
1016
+ column_type_sql << "(#{precision})"
1017
+ else
1018
+ raise(ActiveRecordError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6")
1019
+ end
881
1020
  elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
882
1021
  column_type_sql << "(#{limit})"
883
1022
  end
@@ -889,23 +1028,22 @@ module ActiveRecord
889
1028
  end
890
1029
 
891
1030
  # Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
892
- # PostgreSQL, MySQL, and Oracle overrides this for custom DISTINCT syntax - they
1031
+ # Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax - they
893
1032
  # require the order columns appear in the SELECT.
894
1033
  #
895
1034
  # columns_for_distinct("posts.id", ["posts.created_at desc"])
896
- #
897
- def columns_for_distinct(columns, orders) # :nodoc:
1035
+ def columns_for_distinct(columns, orders) #:nodoc:
898
1036
  columns
899
1037
  end
900
1038
 
901
- include TimestampDefaultDeprecation
902
1039
  # Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
903
1040
  # Additional options (like <tt>null: false</tt>) are forwarded to #add_column.
904
1041
  #
905
1042
  # add_timestamps(:suppliers, null: false)
906
1043
  #
907
1044
  def add_timestamps(table_name, options = {})
908
- emit_warning_if_null_unspecified(:add_timestamps, options)
1045
+ options[:null] = false if options[:null].nil?
1046
+
909
1047
  add_column table_name, :created_at, :datetime, options
910
1048
  add_column table_name, :updated_at, :datetime, options
911
1049
  end
@@ -925,13 +1063,13 @@ module ActiveRecord
925
1063
 
926
1064
  def add_index_options(table_name, column_name, options = {}) #:nodoc:
927
1065
  column_names = Array(column_name)
928
- index_name = index_name(table_name, column: column_names)
929
1066
 
930
1067
  options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
931
1068
 
932
- index_type = options[:unique] ? "UNIQUE" : ""
933
1069
  index_type = options[:type].to_s if options.key?(:type)
1070
+ index_type ||= options[:unique] ? "UNIQUE" : ""
934
1071
  index_name = options[:name].to_s if options.key?(:name)
1072
+ index_name ||= index_name(table_name, column: column_names)
935
1073
  max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
936
1074
 
937
1075
  if options.key?(:algorithm)
@@ -949,7 +1087,7 @@ module ActiveRecord
949
1087
  if index_name.length > max_index_length
950
1088
  raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
951
1089
  end
952
- if table_exists?(table_name) && index_name_exists?(table_name, index_name, false)
1090
+ if data_source_exists?(table_name) && index_name_exists?(table_name, index_name, false)
953
1091
  raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
954
1092
  end
955
1093
  index_columns = quoted_columns_for_index(column_names, options).join(", ")
@@ -957,6 +1095,10 @@ module ActiveRecord
957
1095
  [index_name, index_type, index_columns, index_options, algorithm, using]
958
1096
  end
959
1097
 
1098
+ def options_include_default?(options)
1099
+ options.include?(:default) && !(options[:null] == false && options[:default].nil?)
1100
+ end
1101
+
960
1102
  protected
961
1103
  def add_index_sort_order(option_strings, column_names, options = {})
962
1104
  if options.is_a?(Hash) && order = options[:order]
@@ -983,26 +1125,36 @@ module ActiveRecord
983
1125
  column_names.map {|name| quote_column_name(name) + option_strings[name]}
984
1126
  end
985
1127
 
986
- def options_include_default?(options)
987
- options.include?(:default) && !(options[:null] == false && options[:default].nil?)
988
- end
989
-
990
1128
  def index_name_for_remove(table_name, options = {})
991
- index_name = index_name(table_name, options)
1129
+ # if the adapter doesn't support the indexes call the best we can do
1130
+ # is return the default index name for the options provided
1131
+ return index_name(table_name, options) unless respond_to?(:indexes)
992
1132
 
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)
1133
+ checks = []
998
1134
 
999
- return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
1000
- end
1135
+ if options.is_a?(Hash)
1136
+ checks << lambda { |i| i.name == options[:name].to_s } if options.has_key?(:name)
1137
+ column_names = Array(options[:column]).map(&:to_s)
1138
+ else
1139
+ column_names = Array(options).map(&:to_s)
1140
+ end
1001
1141
 
1002
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
1142
+ if column_names.any?
1143
+ checks << lambda { |i| i.columns.join('_and_') == column_names.join('_and_') }
1003
1144
  end
1004
1145
 
1005
- index_name
1146
+ raise ArgumentError "No name or columns specified" if checks.none?
1147
+
1148
+ matching_indexes = indexes(table_name).select { |i| checks.all? { |check| check[i] } }
1149
+
1150
+ if matching_indexes.count > 1
1151
+ raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
1152
+ "Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
1153
+ elsif matching_indexes.none?
1154
+ raise ArgumentError, "No indexes found on #{table_name} with the options provided."
1155
+ else
1156
+ matching_indexes.first.name
1157
+ end
1006
1158
  end
1007
1159
 
1008
1160
  def rename_table_indexes(table_name, new_name)
@@ -1028,12 +1180,12 @@ module ActiveRecord
1028
1180
  end
1029
1181
 
1030
1182
  private
1031
- def create_table_definition(name, temporary, options, as = nil)
1032
- TableDefinition.new native_database_types, name, temporary, options, as
1183
+ def create_table_definition(name, temporary = false, options = nil, as = nil)
1184
+ TableDefinition.new(name, temporary, options, as)
1033
1185
  end
1034
1186
 
1035
1187
  def create_alter_table(name)
1036
- AlterTable.new create_table_definition(name, false, {})
1188
+ AlterTable.new create_table_definition(name)
1037
1189
  end
1038
1190
 
1039
1191
  def foreign_key_name(table_name, options) # :nodoc:
@@ -1044,11 +1196,19 @@ module ActiveRecord
1044
1196
  end
1045
1197
  end
1046
1198
 
1047
- def validate_index_length!(table_name, new_name)
1199
+ def validate_index_length!(table_name, new_name) # :nodoc:
1048
1200
  if new_name.length > allowed_index_name_length
1049
1201
  raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
1050
1202
  end
1051
1203
  end
1204
+
1205
+ def extract_new_default_value(default_or_changes)
1206
+ if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
1207
+ default_or_changes[:to]
1208
+ else
1209
+ default_or_changes
1210
+ end
1211
+ end
1052
1212
  end
1053
1213
  end
1054
1214
  end