activerecord 3.2.19 → 5.0.0

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

Potentially problematic release.


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

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