activerecord 3.2.22.4 → 4.0.13

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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2799 -617
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +23 -32
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +40 -34
  7. data/lib/active_record/association_relation.rb +22 -0
  8. data/lib/active_record/associations/alias_tracker.rb +4 -2
  9. data/lib/active_record/associations/association.rb +60 -46
  10. data/lib/active_record/associations/association_scope.rb +46 -40
  11. data/lib/active_record/associations/belongs_to_association.rb +17 -4
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +73 -56
  15. data/lib/active_record/associations/builder/collection_association.rb +54 -40
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -64
  18. data/lib/active_record/associations/builder/has_one.rb +13 -50
  19. data/lib/active_record/associations/builder/singular_association.rb +13 -13
  20. data/lib/active_record/associations/collection_association.rb +130 -96
  21. data/lib/active_record/associations/collection_proxy.rb +916 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -13
  23. data/lib/active_record/associations/has_many_association.rb +35 -8
  24. data/lib/active_record/associations/has_many_through_association.rb +37 -17
  25. data/lib/active_record/associations/has_one_association.rb +42 -19
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency/join_association.rb +39 -22
  28. data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
  29. data/lib/active_record/associations/join_dependency/join_part.rb +21 -8
  30. data/lib/active_record/associations/join_dependency.rb +30 -9
  31. data/lib/active_record/associations/join_helper.rb +1 -11
  32. data/lib/active_record/associations/preloader/association.rb +29 -33
  33. data/lib/active_record/associations/preloader/collection_association.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +2 -2
  35. data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
  36. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +13 -17
  38. data/lib/active_record/associations/preloader.rb +20 -43
  39. data/lib/active_record/associations/singular_association.rb +11 -11
  40. data/lib/active_record/associations/through_association.rb +3 -3
  41. data/lib/active_record/associations.rb +223 -282
  42. data/lib/active_record/attribute_assignment.rb +134 -154
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  44. data/lib/active_record/attribute_methods/dirty.rb +36 -29
  45. data/lib/active_record/attribute_methods/primary_key.rb +45 -31
  46. data/lib/active_record/attribute_methods/query.rb +5 -4
  47. data/lib/active_record/attribute_methods/read.rb +67 -90
  48. data/lib/active_record/attribute_methods/serialization.rb +133 -70
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +51 -45
  50. data/lib/active_record/attribute_methods/write.rb +34 -39
  51. data/lib/active_record/attribute_methods.rb +268 -108
  52. data/lib/active_record/autosave_association.rb +80 -73
  53. data/lib/active_record/base.rb +54 -451
  54. data/lib/active_record/callbacks.rb +60 -22
  55. data/lib/active_record/coders/yaml_column.rb +18 -21
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +347 -197
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +146 -138
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -19
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +19 -3
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +151 -142
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +499 -217
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +208 -0
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +209 -44
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +169 -61
  67. data/lib/active_record/connection_adapters/column.rb +67 -36
  68. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +28 -29
  70. data/lib/active_record/connection_adapters/mysql_adapter.rb +200 -73
  71. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +98 -0
  72. data/lib/active_record/connection_adapters/postgresql/cast.rb +160 -0
  73. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +240 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid.rb +374 -0
  75. data/lib/active_record/connection_adapters/postgresql/quoting.rb +183 -0
  76. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +508 -0
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +544 -899
  79. data/lib/active_record/connection_adapters/schema_cache.rb +76 -16
  80. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +595 -16
  81. data/lib/active_record/connection_handling.rb +98 -0
  82. data/lib/active_record/core.rb +472 -0
  83. data/lib/active_record/counter_cache.rb +107 -108
  84. data/lib/active_record/dynamic_matchers.rb +115 -63
  85. data/lib/active_record/errors.rb +36 -18
  86. data/lib/active_record/explain.rb +15 -63
  87. data/lib/active_record/explain_registry.rb +30 -0
  88. data/lib/active_record/explain_subscriber.rb +8 -4
  89. data/lib/active_record/fixture_set/file.rb +55 -0
  90. data/lib/active_record/fixtures.rb +159 -155
  91. data/lib/active_record/inheritance.rb +93 -59
  92. data/lib/active_record/integration.rb +8 -8
  93. data/lib/active_record/locale/en.yml +8 -1
  94. data/lib/active_record/locking/optimistic.rb +39 -43
  95. data/lib/active_record/locking/pessimistic.rb +4 -4
  96. data/lib/active_record/log_subscriber.rb +19 -9
  97. data/lib/active_record/migration/command_recorder.rb +102 -33
  98. data/lib/active_record/migration/join_table.rb +15 -0
  99. data/lib/active_record/migration.rb +411 -173
  100. data/lib/active_record/model_schema.rb +81 -94
  101. data/lib/active_record/nested_attributes.rb +173 -131
  102. data/lib/active_record/null_relation.rb +67 -0
  103. data/lib/active_record/persistence.rb +254 -106
  104. data/lib/active_record/query_cache.rb +18 -36
  105. data/lib/active_record/querying.rb +19 -15
  106. data/lib/active_record/railtie.rb +113 -38
  107. data/lib/active_record/railties/console_sandbox.rb +3 -4
  108. data/lib/active_record/railties/controller_runtime.rb +4 -3
  109. data/lib/active_record/railties/databases.rake +115 -368
  110. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  111. data/lib/active_record/readonly_attributes.rb +7 -3
  112. data/lib/active_record/reflection.rb +110 -61
  113. data/lib/active_record/relation/batches.rb +29 -29
  114. data/lib/active_record/relation/calculations.rb +155 -125
  115. data/lib/active_record/relation/delegation.rb +94 -18
  116. data/lib/active_record/relation/finder_methods.rb +151 -203
  117. data/lib/active_record/relation/merger.rb +188 -0
  118. data/lib/active_record/relation/predicate_builder.rb +85 -42
  119. data/lib/active_record/relation/query_methods.rb +793 -146
  120. data/lib/active_record/relation/spawn_methods.rb +43 -150
  121. data/lib/active_record/relation.rb +293 -173
  122. data/lib/active_record/result.rb +48 -7
  123. data/lib/active_record/runtime_registry.rb +17 -0
  124. data/lib/active_record/sanitization.rb +41 -54
  125. data/lib/active_record/schema.rb +19 -12
  126. data/lib/active_record/schema_dumper.rb +41 -41
  127. data/lib/active_record/schema_migration.rb +46 -0
  128. data/lib/active_record/scoping/default.rb +56 -52
  129. data/lib/active_record/scoping/named.rb +78 -103
  130. data/lib/active_record/scoping.rb +54 -124
  131. data/lib/active_record/serialization.rb +6 -2
  132. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  133. data/lib/active_record/statement_cache.rb +26 -0
  134. data/lib/active_record/store.rb +131 -15
  135. data/lib/active_record/tasks/database_tasks.rb +204 -0
  136. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  137. data/lib/active_record/tasks/mysql_database_tasks.rb +144 -0
  138. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  139. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  140. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  141. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  142. data/lib/active_record/test_case.rb +67 -38
  143. data/lib/active_record/timestamp.rb +16 -11
  144. data/lib/active_record/transactions.rb +73 -51
  145. data/lib/active_record/validations/associated.rb +19 -13
  146. data/lib/active_record/validations/presence.rb +65 -0
  147. data/lib/active_record/validations/uniqueness.rb +110 -57
  148. data/lib/active_record/validations.rb +18 -17
  149. data/lib/active_record/version.rb +7 -6
  150. data/lib/active_record.rb +63 -45
  151. data/lib/rails/generators/active_record/migration/migration_generator.rb +45 -8
  152. data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
  153. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  154. data/lib/rails/generators/active_record/model/model_generator.rb +5 -4
  155. data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
  156. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  157. data/lib/rails/generators/active_record.rb +3 -5
  158. metadata +43 -29
  159. data/examples/associations.png +0 -0
  160. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  161. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  162. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  163. data/lib/active_record/dynamic_finder_match.rb +0 -68
  164. data/lib/active_record/dynamic_scope_match.rb +0 -23
  165. data/lib/active_record/fixtures/file.rb +0 -65
  166. data/lib/active_record/identity_map.rb +0 -162
  167. data/lib/active_record/observer.rb +0 -121
  168. data/lib/active_record/session_store.rb +0 -360
  169. data/lib/rails/generators/active_record/migration.rb +0 -15
  170. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  171. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  172. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  173. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,10 +1,11 @@
1
- require 'active_support/core_ext/array/wrap'
2
- require 'active_support/deprecation/reporting'
1
+ require 'active_record/migration/join_table'
3
2
 
4
3
  module ActiveRecord
5
4
  module ConnectionAdapters # :nodoc:
6
5
  module SchemaStatements
7
- # Returns a Hash of mappings from the abstract data types to the native
6
+ include ActiveRecord::Migration::JoinTable
7
+
8
+ # Returns a hash of mappings from the abstract data types to the native
8
9
  # database types. See TableDefinition#column for details on the recognized
9
10
  # abstract data types.
10
11
  def native_database_types
@@ -13,13 +14,13 @@ module ActiveRecord
13
14
 
14
15
  # Truncates a table alias according to the limits of the current adapter.
15
16
  def table_alias_for(table_name)
16
- table_name[0...table_alias_length].gsub(/\./, '_')
17
+ table_name[0...table_alias_length].tr('.', '_')
17
18
  end
18
19
 
19
20
  # Checks to see if the table +table_name+ exists on the database.
20
21
  #
21
- # === Example
22
22
  # table_exists?(:developers)
23
+ #
23
24
  def table_exists?(table_name)
24
25
  tables.include?(table_name.to_s)
25
26
  end
@@ -29,20 +30,20 @@ module ActiveRecord
29
30
 
30
31
  # Checks to see if an index exists on a table for a given index definition.
31
32
  #
32
- # === Examples
33
- # # Check an index exists
34
- # index_exists?(:suppliers, :company_id)
33
+ # # Check an index exists
34
+ # index_exists?(:suppliers, :company_id)
35
35
  #
36
- # # Check an index on multiple columns exists
37
- # index_exists?(:suppliers, [:company_id, :company_type])
36
+ # # Check an index on multiple columns exists
37
+ # index_exists?(:suppliers, [:company_id, :company_type])
38
38
  #
39
- # # Check a unique index exists
40
- # index_exists?(:suppliers, :company_id, :unique => true)
39
+ # # Check a unique index exists
40
+ # index_exists?(:suppliers, :company_id, unique: true)
41
+ #
42
+ # # Check an index with a custom name exists
43
+ # index_exists?(:suppliers, :company_id, name: "idx_company_id"
41
44
  #
42
- # # Check an index with a custom name exists
43
- # index_exists?(:suppliers, :company_id, :name => "idx_company_id"
44
45
  def index_exists?(table_name, column_name, options = {})
45
- column_names = Array.wrap(column_name)
46
+ column_names = Array(column_name)
46
47
  index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, :column => column_names)
47
48
  if options[:unique]
48
49
  indexes(table_name).any?{ |i| i.unique && i.name == index_name }
@@ -53,25 +54,30 @@ module ActiveRecord
53
54
 
54
55
  # Returns an array of Column objects for the table specified by +table_name+.
55
56
  # See the concrete implementation for details on the expected parameter values.
56
- def columns(table_name, name = nil) end
57
+ def columns(table_name) end
57
58
 
58
59
  # Checks to see if a column exists in a given table.
59
60
  #
60
- # === Examples
61
- # # Check a column exists
62
- # column_exists?(:suppliers, :name)
61
+ # # Check a column exists
62
+ # column_exists?(:suppliers, :name)
63
+ #
64
+ # # Check a column exists of a particular type
65
+ # column_exists?(:suppliers, :name, :string)
63
66
  #
64
- # # Check a column exists of a particular type
65
- # column_exists?(:suppliers, :name, :string)
67
+ # # Check a column exists with a specific definition
68
+ # column_exists?(:suppliers, :name, :string, limit: 100)
69
+ # column_exists?(:suppliers, :name, :string, default: 'default')
70
+ # column_exists?(:suppliers, :name, :string, null: false)
71
+ # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
66
72
  #
67
- # # Check a column exists with a specific definition
68
- # column_exists?(:suppliers, :name, :string, :limit => 100)
69
73
  def column_exists?(table_name, column_name, type = nil, options = {})
70
74
  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]) }
75
+ (!type || c.type == type) &&
76
+ (!options.key?(:limit) || c.limit == options[:limit]) &&
77
+ (!options.key?(:precision) || c.precision == options[:precision]) &&
78
+ (!options.key?(:scale) || c.scale == options[:scale]) &&
79
+ (!options.key?(:default) || c.default == options[:default]) &&
80
+ (!options.key?(:null) || c.null == options[:null]) }
75
81
  end
76
82
 
77
83
  # Creates a new table with the name +table_name+. +table_name+ may either
@@ -81,27 +87,30 @@ module ActiveRecord
81
87
  # form or the regular form, like this:
82
88
  #
83
89
  # === 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
90
  #
88
- # create_table(:suppliers) do |t|
89
- # t.column :name, :string, :limit => 60
90
- # # Other fields here
91
- # end
91
+ # # create_table() passes a TableDefinition object to the block.
92
+ # # This form will not only create the table, but also columns for the
93
+ # # table.
94
+ #
95
+ # create_table(:suppliers) do |t|
96
+ # t.column :name, :string, limit: 60
97
+ # # Other fields here
98
+ # end
92
99
  #
93
100
  # === 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
101
+ #
102
+ # # You can also use the column types as method calls, rather than calling the column method.
103
+ # create_table(:suppliers) do |t|
104
+ # t.string :name, limit: 60
105
+ # # Other fields here
106
+ # end
99
107
  #
100
108
  # === 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})
109
+ #
110
+ # # Creates a table called 'suppliers' with no columns.
111
+ # create_table(:suppliers)
112
+ # # Add a column to 'suppliers'.
113
+ # add_column(:suppliers, :name, :string, {limit: 60})
105
114
  #
106
115
  # The +options+ hash can include the following keys:
107
116
  # [<tt>:id</tt>]
@@ -111,9 +120,9 @@ module ActiveRecord
111
120
  # The name of the primary key, if one is to be added automatically.
112
121
  # Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
113
122
  #
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.
123
+ # Note that Active Record models will automatically detect their
124
+ # primary key. This can be avoided by using +self.primary_key=+ on the model
125
+ # to define the key explicitly.
117
126
  #
118
127
  # [<tt>:options</tt>]
119
128
  # Any extra options you want appended to the table definition.
@@ -123,39 +132,54 @@ module ActiveRecord
123
132
  # Set to true to drop the table before creating it.
124
133
  # Defaults to false.
125
134
  #
126
- # ===== Examples
127
135
  # ====== Add a backend specific option to the generated SQL (MySQL)
128
- # create_table(:suppliers, :options => 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
136
+ #
137
+ # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
138
+ #
129
139
  # generates:
130
- # CREATE TABLE suppliers (
131
- # id int(11) DEFAULT NULL auto_increment PRIMARY KEY
132
- # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
140
+ #
141
+ # CREATE TABLE suppliers (
142
+ # id int(11) DEFAULT NULL auto_increment PRIMARY KEY
143
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
133
144
  #
134
145
  # ====== Rename the primary key column
135
- # create_table(:objects, :primary_key => 'guid') do |t|
136
- # t.column :name, :string, :limit => 80
137
- # end
146
+ #
147
+ # create_table(:objects, primary_key: 'guid') do |t|
148
+ # t.column :name, :string, limit: 80
149
+ # end
150
+ #
138
151
  # generates:
139
- # CREATE TABLE objects (
140
- # guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
141
- # name varchar(80)
142
- # )
152
+ #
153
+ # CREATE TABLE objects (
154
+ # guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
155
+ # name varchar(80)
156
+ # )
143
157
  #
144
158
  # ====== 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
159
+ #
160
+ # create_table(:categories_suppliers, id: false) do |t|
161
+ # t.column :category_id, :integer
162
+ # t.column :supplier_id, :integer
163
+ # end
164
+ #
149
165
  # generates:
150
- # CREATE TABLE categories_suppliers (
151
- # category_id int,
152
- # supplier_id int
153
- # )
166
+ #
167
+ # CREATE TABLE categories_suppliers (
168
+ # category_id int,
169
+ # supplier_id int
170
+ # )
154
171
  #
155
172
  # See also TableDefinition#column for details on how to create columns.
156
173
  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
174
+ td = create_table_definition table_name, options[:temporary], options[:options]
175
+
176
+ unless options[:id] == false
177
+ pk = options.fetch(:primary_key) {
178
+ Base.get_primary_key table_name.to_s.singularize
179
+ }
180
+
181
+ td.primary_key pk, options.fetch(:id, :primary_key), options
182
+ end
159
183
 
160
184
  yield td if block_given?
161
185
 
@@ -163,95 +187,167 @@ module ActiveRecord
163
187
  drop_table(table_name, options)
164
188
  end
165
189
 
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
190
+ execute schema_creation.accept td
191
+ td.indexes.each_pair { |c,o| add_index table_name, c, o }
192
+ end
193
+
194
+ # Creates a new join table with the name created using the lexical order of the first two
195
+ # arguments. These arguments can be a String or a Symbol.
196
+ #
197
+ # # Creates a table called 'assemblies_parts' with no id.
198
+ # create_join_table(:assemblies, :parts)
199
+ #
200
+ # You can pass a +options+ hash can include the following keys:
201
+ # [<tt>:table_name</tt>]
202
+ # Sets the table name overriding the default
203
+ # [<tt>:column_options</tt>]
204
+ # Any extra options you want appended to the columns definition.
205
+ # [<tt>:options</tt>]
206
+ # Any extra options you want appended to the table definition.
207
+ # [<tt>:temporary</tt>]
208
+ # Make a temporary table.
209
+ # [<tt>:force</tt>]
210
+ # Set to true to drop the table before creating it.
211
+ # Defaults to false.
212
+ #
213
+ # Note that +create_join_table+ does not create any indices by default; you can use
214
+ # its block form to do so yourself:
215
+ #
216
+ # create_join_table :products, :categories do |t|
217
+ # t.index :product_id
218
+ # t.index :category_id
219
+ # end
220
+ #
221
+ # ====== Add a backend specific option to the generated SQL (MySQL)
222
+ #
223
+ # create_join_table(:assemblies, :parts, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
224
+ #
225
+ # generates:
226
+ #
227
+ # CREATE TABLE assemblies_parts (
228
+ # assembly_id int NOT NULL,
229
+ # part_id int NOT NULL,
230
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
231
+ #
232
+ def create_join_table(table_1, table_2, options = {})
233
+ join_table_name = find_join_table_name(table_1, table_2, options)
234
+
235
+ column_options = options.delete(:column_options) || {}
236
+ column_options.reverse_merge!(null: false)
237
+
238
+ t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
239
+
240
+ create_table(join_table_name, options.merge!(id: false)) do |td|
241
+ td.integer t1_column, column_options
242
+ td.integer t2_column, column_options
243
+ yield td if block_given?
244
+ end
245
+ end
246
+
247
+ # Drops the join table specified by the given arguments.
248
+ # See +create_join_table+ for details.
249
+ #
250
+ # Although this command ignores the block if one is given, it can be helpful
251
+ # to provide one in a migration's +change+ method so it can be reverted.
252
+ # In that case, the block will be used by create_join_table.
253
+ def drop_join_table(table_1, table_2, options = {})
254
+ join_table_name = find_join_table_name(table_1, table_2, options)
255
+ drop_table(join_table_name)
171
256
  end
172
257
 
173
258
  # A block for changing columns in +table+.
174
259
  #
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
260
+ # # change_table() yields a Table instance
261
+ # change_table(:suppliers) do |t|
262
+ # t.column :name, :string, limit: 60
263
+ # # Other column alterations here
264
+ # end
181
265
  #
182
266
  # The +options+ hash can include the following keys:
183
267
  # [<tt>:bulk</tt>]
184
268
  # 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 ...
269
+ #
270
+ # ALTER TABLE `users` ADD COLUMN age INT(11), ADD COLUMN birthdate DATETIME ...
186
271
  #
187
272
  # Defaults to false.
188
273
  #
189
- # ===== Examples
190
274
  # ====== Add a column
191
- # change_table(:suppliers) do |t|
192
- # t.column :name, :string, :limit => 60
193
- # end
275
+ #
276
+ # change_table(:suppliers) do |t|
277
+ # t.column :name, :string, limit: 60
278
+ # end
194
279
  #
195
280
  # ====== Add 2 integer columns
196
- # change_table(:suppliers) do |t|
197
- # t.integer :width, :height, :null => false, :default => 0
198
- # end
281
+ #
282
+ # change_table(:suppliers) do |t|
283
+ # t.integer :width, :height, null: false, default: 0
284
+ # end
199
285
  #
200
286
  # ====== Add created_at/updated_at columns
201
- # change_table(:suppliers) do |t|
202
- # t.timestamps
203
- # end
287
+ #
288
+ # change_table(:suppliers) do |t|
289
+ # t.timestamps
290
+ # end
204
291
  #
205
292
  # ====== Add a foreign key column
206
- # change_table(:suppliers) do |t|
207
- # t.references :company
208
- # end
209
293
  #
210
- # Creates a <tt>company_id(integer)</tt> column
294
+ # change_table(:suppliers) do |t|
295
+ # t.references :company
296
+ # end
297
+ #
298
+ # Creates a <tt>company_id(integer)</tt> column.
211
299
  #
212
300
  # ====== Add a polymorphic foreign key column
301
+ #
213
302
  # change_table(:suppliers) do |t|
214
- # t.belongs_to :company, :polymorphic => true
303
+ # t.belongs_to :company, polymorphic: true
215
304
  # end
216
305
  #
217
- # Creates <tt>company_type(varchar)</tt> and <tt>company_id(integer)</tt> columns
306
+ # Creates <tt>company_type(varchar)</tt> and <tt>company_id(integer)</tt> columns.
218
307
  #
219
308
  # ====== Remove a column
309
+ #
220
310
  # change_table(:suppliers) do |t|
221
311
  # t.remove :company
222
312
  # end
223
313
  #
224
314
  # ====== Remove several columns
315
+ #
225
316
  # change_table(:suppliers) do |t|
226
317
  # t.remove :company_id
227
318
  # t.remove :width, :height
228
319
  # end
229
320
  #
230
321
  # ====== Remove an index
322
+ #
231
323
  # change_table(:suppliers) do |t|
232
324
  # t.remove_index :company_id
233
325
  # end
234
326
  #
235
- # See also Table for details on
236
- # all of the various column transformation
327
+ # See also Table for details on all of the various column transformation.
237
328
  def change_table(table_name, options = {})
238
329
  if supports_bulk_alter? && options[:bulk]
239
330
  recorder = ActiveRecord::Migration::CommandRecorder.new(self)
240
- yield Table.new(table_name, recorder)
331
+ yield update_table_definition(table_name, recorder)
241
332
  bulk_change_table(table_name, recorder.commands)
242
333
  else
243
- yield Table.new(table_name, self)
334
+ yield update_table_definition(table_name, self)
244
335
  end
245
336
  end
246
337
 
247
338
  # Renames a table.
248
- # ===== Example
249
- # rename_table('octopuses', 'octopi')
339
+ #
340
+ # rename_table('octopuses', 'octopi')
341
+ #
250
342
  def rename_table(table_name, new_name)
251
343
  raise NotImplementedError, "rename_table is not implemented"
252
344
  end
253
345
 
254
346
  # Drops a table from the database.
347
+ #
348
+ # Although this command ignores +options+ and the block if one is given, it can be helpful
349
+ # to provide these in a migration's +change+ method so it can be reverted.
350
+ # In that case, +options+ and the block will be used by create_table.
255
351
  def drop_table(table_name, options = {})
256
352
  execute "DROP TABLE #{quote_table_name(table_name)}"
257
353
  end
@@ -259,49 +355,80 @@ module ActiveRecord
259
355
  # Adds a new column to the named table.
260
356
  # See TableDefinition#column for details of the options you can use.
261
357
  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
358
+ at = create_alter_table table_name
359
+ at.add_column(column_name, type, options)
360
+ execute schema_creation.accept at
361
+ end
277
362
 
278
- columns_for_remove(table_name, *column_names).each do |column_name|
279
- execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{column_name}"
363
+ # Removes the given columns from the table definition.
364
+ #
365
+ # remove_columns(:suppliers, :qualification, :experience)
366
+ #
367
+ def remove_columns(table_name, *column_names)
368
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.empty?
369
+ column_names.each do |column_name|
370
+ remove_column(table_name, column_name)
280
371
  end
281
372
  end
282
- alias :remove_columns :remove_column
373
+
374
+ # Removes the column from the table definition.
375
+ #
376
+ # remove_column(:suppliers, :qualification)
377
+ #
378
+ # The +type+ and +options+ parameters will be ignored if present. It can be helpful
379
+ # to provide these in a migration's +change+ method so it can be reverted.
380
+ # In that case, +type+ and +options+ will be used by add_column.
381
+ def remove_column(table_name, column_name, type = nil, options = {})
382
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
383
+ end
283
384
 
284
385
  # Changes the column's definition according to the new options.
285
386
  # 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)
387
+ #
388
+ # change_column(:suppliers, :name, :string, limit: 80)
389
+ # change_column(:accounts, :description, :text)
390
+ #
289
391
  def change_column(table_name, column_name, type, options = {})
290
392
  raise NotImplementedError, "change_column is not implemented"
291
393
  end
292
394
 
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)
395
+ # Sets a new default value for a column:
396
+ #
397
+ # change_column_default(:suppliers, :qualification, 'new')
398
+ # change_column_default(:accounts, :authorized, 1)
399
+ #
400
+ # Setting the default to +nil+ effectively drops the default:
401
+ #
402
+ # change_column_default(:users, :email, nil)
403
+ #
298
404
  def change_column_default(table_name, column_name, default)
299
405
  raise NotImplementedError, "change_column_default is not implemented"
300
406
  end
301
407
 
408
+ # Sets or removes a +NOT NULL+ constraint on a column. The +null+ flag
409
+ # indicates whether the value can be +NULL+. For example
410
+ #
411
+ # change_column_null(:users, :nickname, false)
412
+ #
413
+ # says nicknames cannot be +NULL+ (adds the constraint), whereas
414
+ #
415
+ # change_column_null(:users, :nickname, true)
416
+ #
417
+ # allows them to be +NULL+ (drops the constraint).
418
+ #
419
+ # The method accepts an optional fourth argument to replace existing
420
+ # +NULL+s with some other value. Use that one when enabling the
421
+ # constraint if needed, since otherwise those rows would not be valid.
422
+ #
423
+ # Please note the fourth argument does not set a column's default.
424
+ def change_column_null(table_name, column_name, null, default = nil)
425
+ raise NotImplementedError, "change_column_null is not implemented"
426
+ end
427
+
302
428
  # Renames a column.
303
- # ===== Example
304
- # rename_column(:suppliers, :description, :name)
429
+ #
430
+ # rename_column(:suppliers, :description, :name)
431
+ #
305
432
  def rename_column(table_name, column_name, new_column_name)
306
433
  raise NotImplementedError, "rename_column is not implemented"
307
434
  end
@@ -312,56 +439,107 @@ module ActiveRecord
312
439
  # The index will be named after the table and the column name(s), unless
313
440
  # you pass <tt>:name</tt> as an option.
314
441
  #
315
- # ===== Examples
316
- #
317
442
  # ====== Creating a simple index
318
- # add_index(:suppliers, :name)
319
- # generates
320
- # CREATE INDEX suppliers_name_index ON suppliers(name)
443
+ #
444
+ # add_index(:suppliers, :name)
445
+ #
446
+ # generates:
447
+ #
448
+ # CREATE INDEX suppliers_name_index ON suppliers(name)
321
449
  #
322
450
  # ====== 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)
451
+ #
452
+ # add_index(:accounts, [:branch_id, :party_id], unique: true)
453
+ #
454
+ # generates:
455
+ #
456
+ # CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
326
457
  #
327
458
  # ====== Creating a named index
328
- # add_index(:accounts, [:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
329
- # generates
459
+ #
460
+ # add_index(:accounts, [:branch_id, :party_id], unique: true, name: 'by_branch_party')
461
+ #
462
+ # generates:
463
+ #
330
464
  # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
331
465
  #
332
466
  # ====== 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
467
  #
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))
468
+ # add_index(:accounts, :name, name: 'by_name', length: 10)
469
+ #
470
+ # generates:
471
+ #
472
+ # CREATE INDEX by_name ON accounts(name(10))
473
+ #
474
+ # add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
475
+ #
476
+ # generates:
477
+ #
478
+ # CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
340
479
  #
341
- # Note: SQLite doesn't support index length
480
+ # Note: SQLite doesn't support index length.
342
481
  #
343
482
  # ====== 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
483
  #
348
- # Note: mysql doesn't yet support index order (it accepts the syntax but ignores it)
484
+ # add_index(:accounts, [:branch_id, :party_id, :surname], order: {branch_id: :desc, party_id: :asc})
485
+ #
486
+ # generates:
487
+ #
488
+ # CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
489
+ #
490
+ # Note: MySQL doesn't yet support index order (it accepts the syntax but ignores it).
491
+ #
492
+ # ====== Creating a partial index
493
+ #
494
+ # add_index(:accounts, [:branch_id, :party_id], unique: true, where: "active")
495
+ #
496
+ # generates:
497
+ #
498
+ # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
499
+ #
500
+ # ====== Creating an index with a specific method
501
+ #
502
+ # add_index(:developers, :name, using: 'btree')
503
+ #
504
+ # generates:
505
+ #
506
+ # CREATE INDEX index_developers_on_name ON developers USING btree (name) -- PostgreSQL
507
+ # CREATE INDEX index_developers_on_name USING btree ON developers (name) -- MySQL
508
+ #
509
+ # Note: only supported by PostgreSQL and MySQL
510
+ #
511
+ # ====== Creating an index with a specific type
512
+ #
513
+ # add_index(:developers, :name, type: :fulltext)
514
+ #
515
+ # generates:
516
+ #
517
+ # CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
349
518
  #
519
+ # Note: only supported by MySQL. Supported: <tt>:fulltext</tt> and <tt>:spatial</tt> on MyISAM tables.
350
520
  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})"
521
+ index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
522
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
353
523
  end
354
524
 
355
- # Remove the given index from the table.
525
+ # Removes the given index from the table.
526
+ #
527
+ # Removes the +index_accounts_on_column+ in the +accounts+ table.
356
528
  #
357
- # Remove the index_accounts_on_column in the accounts table.
358
529
  # 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
530
+ #
531
+ # Removes the index named +index_accounts_on_branch_id+ in the +accounts+ table.
532
+ #
533
+ # remove_index :accounts, column: :branch_id
534
+ #
535
+ # Removes the index named +index_accounts_on_branch_id_and_party_id+ in the +accounts+ table.
536
+ #
537
+ # remove_index :accounts, column: [:branch_id, :party_id]
538
+ #
539
+ # Removes the index named +by_branch_party+ in the +accounts+ table.
540
+ #
541
+ # remove_index :accounts, name: :by_branch_party
542
+ #
365
543
  def remove_index(table_name, options = {})
366
544
  remove_index!(table_name, index_name_for_remove(table_name, options))
367
545
  end
@@ -370,10 +548,12 @@ module ActiveRecord
370
548
  execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
371
549
  end
372
550
 
373
- # Rename an index.
551
+ # Renames an index.
552
+ #
553
+ # Rename the +index_people_on_last_name+ index to +index_users_on_last_name+:
374
554
  #
375
- # Rename the index_people_on_last_name index to index_users_on_last_name
376
555
  # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
556
+ #
377
557
  def rename_index(table_name, old_name, new_name)
378
558
  # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
379
559
  old_index_def = indexes(table_name).detect { |i| i.name == old_name }
@@ -383,9 +563,9 @@ module ActiveRecord
383
563
  end
384
564
 
385
565
  def index_name(table_name, options) #:nodoc:
386
- if Hash === options # legacy support
566
+ if Hash === options
387
567
  if options[:column]
388
- "index_#{table_name}_on_#{Array.wrap(options[:column]) * '_and_'}"
568
+ "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
389
569
  elsif options[:name]
390
570
  options[:name]
391
571
  else
@@ -396,7 +576,7 @@ module ActiveRecord
396
576
  end
397
577
  end
398
578
 
399
- # Verify the existence of an index with a given name.
579
+ # Verifies the existence of an index with a given name.
400
580
  #
401
581
  # The default argument is returned if the underlying implementation does not define the indexes method,
402
582
  # as there's no way to determine the correct answer in that case.
@@ -406,45 +586,63 @@ module ActiveRecord
406
586
  indexes(table_name).detect { |i| i.name == index_name }
407
587
  end
408
588
 
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
589
+ # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
590
+ # <tt>add_reference</tt> and <tt>add_belongs_to</tt> are acceptable.
591
+ #
592
+ # ====== Create a user_id column
593
+ #
594
+ # add_reference(:products, :user)
595
+ #
596
+ # ====== Create a supplier_id and supplier_type columns
597
+ #
598
+ # add_belongs_to(:products, :supplier, polymorphic: true)
599
+ #
600
+ # ====== Create a supplier_id, supplier_type columns and appropriate index
601
+ #
602
+ # add_reference(:products, :supplier, polymorphic: true, index: true)
603
+ #
604
+ def add_reference(table_name, ref_name, options = {})
605
+ polymorphic = options.delete(:polymorphic)
606
+ index_options = options.delete(:index)
607
+ add_column(table_name, "#{ref_name}_id", :integer, options)
608
+ add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
609
+ add_index(table_name, polymorphic ? %w[id type].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : nil) if index_options
412
610
  end
611
+ alias :add_belongs_to :add_reference
612
+
613
+ # Removes the reference(s). Also removes a +type+ column if one exists.
614
+ # <tt>remove_reference</tt>, <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
615
+ #
616
+ # ====== Remove the reference
617
+ #
618
+ # remove_reference(:products, :user, index: true)
619
+ #
620
+ # ====== Remove polymorphic reference
621
+ #
622
+ # remove_reference(:products, :supplier, polymorphic: true)
623
+ #
624
+ def remove_reference(table_name, ref_name, options = {})
625
+ remove_column(table_name, "#{ref_name}_id")
626
+ remove_column(table_name, "#{ref_name}_type") if options[:polymorphic]
627
+ end
628
+ alias :remove_belongs_to :remove_reference
413
629
 
414
630
  def dump_schema_information #:nodoc:
415
631
  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")
632
+
633
+ ActiveRecord::SchemaMigration.order('version').map { |sm|
634
+ "INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');"
635
+ }.join "\n\n"
418
636
  end
419
637
 
420
638
  # Should not be called normally, but this operation is non-destructive.
421
639
  # The migrations module handles this automatically.
422
640
  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
435
-
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"
438
-
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
641
+ ActiveRecord::SchemaMigration.create_table
444
642
  end
445
643
 
446
644
  def assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths)
447
- migrations_paths = Array.wrap(migrations_paths)
645
+ migrations_paths = Array(migrations_paths)
448
646
  version = version.to_i
449
647
  sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
450
648
 
@@ -483,7 +681,7 @@ module ActiveRecord
483
681
  column_type_sql << "(#{precision})"
484
682
  end
485
683
  elsif scale
486
- raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified"
684
+ raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
487
685
  end
488
686
 
489
687
  elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
@@ -492,7 +690,7 @@ module ActiveRecord
492
690
 
493
691
  column_type_sql
494
692
  else
495
- type
693
+ type.to_s
496
694
  end
497
695
  end
498
696
 
@@ -502,38 +700,57 @@ module ActiveRecord
502
700
  if options[:null] == false
503
701
  sql << " NOT NULL"
504
702
  end
703
+ if options[:auto_increment] == true
704
+ sql << " AUTO_INCREMENT"
705
+ end
505
706
  end
506
707
 
507
708
  # 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.
509
709
  #
510
- # distinct("posts.id", "posts.created_at desc")
710
+ # distinct("posts.id", ["posts.created_at desc"])
711
+ #
511
712
  def distinct(columns, order_by)
512
- "DISTINCT #{columns}"
713
+ ActiveSupport::Deprecation.warn("#distinct is deprecated and shall be removed from future releases.")
714
+ "DISTINCT #{columns_for_distinct(columns, order_by)}"
513
715
  end
514
716
 
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
717
+ # Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
718
+ # Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax - they
719
+ # require the order columns appear in the SELECT.
720
+ #
721
+ # columns_for_distinct("posts.id", ["posts.created_at desc"])
722
+ def columns_for_distinct(columns, orders) #:nodoc:
723
+ columns
521
724
  end
522
725
 
523
- # Removes the timestamp columns (created_at and updated_at) from the table definition.
524
- # ===== Examples
726
+ # Adds timestamps (+created_at+ and +updated_at+) columns to the named table.
727
+ #
728
+ # add_timestamps(:suppliers)
729
+ #
730
+ def add_timestamps(table_name, options = {})
731
+ add_column table_name, :created_at, :datetime, options
732
+ add_column table_name, :updated_at, :datetime, options
733
+ end
734
+
735
+ # Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
736
+ #
525
737
  # remove_timestamps(:suppliers)
738
+ #
526
739
  def remove_timestamps(table_name)
527
740
  remove_column table_name, :updated_at
528
741
  remove_column table_name, :created_at
529
742
  end
530
743
 
744
+ def update_table_definition(table_name, base) #:nodoc:
745
+ Table.new(table_name, base)
746
+ end
747
+
531
748
  protected
532
749
  def add_index_sort_order(option_strings, column_names, options = {})
533
750
  if options.is_a?(Hash) && order = options[:order]
534
751
  case order
535
752
  when Hash
536
- column_names.each {|name| option_strings[name] += " #{order[name].to_s.upcase}" if order.has_key?(name)}
753
+ column_names.each {|name| option_strings[name] += " #{order[name].upcase}" if order.has_key?(name)}
537
754
  when String
538
755
  column_names.each {|name| option_strings[name] += " #{order.upcase}"}
539
756
  end
@@ -559,31 +776,65 @@ module ActiveRecord
559
776
  end
560
777
 
561
778
  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)
779
+ column_names = Array(column_name)
780
+ index_name = index_name(table_name, column: column_names)
564
781
 
565
782
  if Hash === options # legacy support, since this param was a string
783
+ options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
784
+
566
785
  index_type = options[:unique] ? "UNIQUE" : ""
786
+ index_type = options[:type].to_s if options.key?(:type)
567
787
  index_name = options[:name].to_s if options.key?(:name)
788
+ max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
789
+
790
+ if options.key?(:algorithm)
791
+ algorithm = index_algorithms.fetch(options[:algorithm]) {
792
+ raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
793
+ }
794
+ end
795
+
796
+ using = "USING #{options[:using]}" if options[:using].present?
797
+
798
+ if supports_partial_index?
799
+ index_options = options[:where] ? " WHERE #{options[:where]}" : ""
800
+ end
568
801
  else
802
+ if options
803
+ message = "Passing a string as third argument of `add_index` is deprecated and will" +
804
+ " be removed in Rails 4.1." +
805
+ " Use add_index(#{table_name.inspect}, #{column_name.inspect}, unique: true) instead"
806
+
807
+ ActiveSupport::Deprecation.warn message
808
+ end
809
+
569
810
  index_type = options
811
+ max_index_length = allowed_index_name_length
812
+ algorithm = using = nil
570
813
  end
571
814
 
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"
815
+ if index_name.length > max_index_length
816
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
574
817
  end
575
818
  if index_name_exists?(table_name, index_name, false)
576
819
  raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
577
820
  end
578
821
  index_columns = quoted_columns_for_index(column_names, options).join(", ")
579
822
 
580
- [index_name, index_type, index_columns]
823
+ [index_name, index_type, index_columns, index_options, algorithm, using]
581
824
  end
582
825
 
583
826
  def index_name_for_remove(table_name, options = {})
584
827
  index_name = index_name(table_name, options)
585
828
 
586
829
  unless index_name_exists?(table_name, index_name, true)
830
+ if options.is_a?(Hash) && options.has_key?(:name)
831
+ options_without_column = options.dup
832
+ options_without_column.delete :column
833
+ index_name_without_column = index_name(table_name, options_without_column)
834
+
835
+ return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
836
+ end
837
+
587
838
  raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
588
839
  end
589
840
 
@@ -591,15 +842,46 @@ module ActiveRecord
591
842
  end
592
843
 
593
844
  def columns_for_remove(table_name, *column_names)
594
- column_names = column_names.flatten
595
-
596
- raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.blank?
845
+ ActiveSupport::Deprecation.warn("columns_for_remove is deprecated and will be removed in the future")
846
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.blank?
597
847
  column_names.map {|column_name| quote_column_name(column_name) }
598
848
  end
599
849
 
850
+ def rename_table_indexes(table_name, new_name)
851
+ indexes(new_name).each do |index|
852
+ generated_index_name = index_name(table_name, column: index.columns)
853
+ if generated_index_name == index.name
854
+ rename_index new_name, generated_index_name, index_name(new_name, column: index.columns)
855
+ end
856
+ end
857
+ end
858
+
859
+ def rename_column_indexes(table_name, column_name, new_column_name)
860
+ column_name, new_column_name = column_name.to_s, new_column_name.to_s
861
+ indexes(table_name).each do |index|
862
+ next unless index.columns.include?(new_column_name)
863
+ old_columns = index.columns.dup
864
+ old_columns[old_columns.index(new_column_name)] = column_name
865
+ generated_index_name = index_name(table_name, column: old_columns)
866
+ if generated_index_name == index.name
867
+ rename_index table_name, generated_index_name, index_name(table_name, column: index.columns)
868
+ end
869
+ end
870
+ end
871
+
872
+ def quote_value(value, column)
873
+ column.sql_type ||= type_to_sql(column.type, column.limit, column.precision, column.scale)
874
+
875
+ quote(value, column)
876
+ end
877
+
600
878
  private
601
- def table_definition
602
- TableDefinition.new(self)
879
+ def create_table_definition(name, temporary, options)
880
+ TableDefinition.new native_database_types, name, temporary, options
881
+ end
882
+
883
+ def create_alter_table(name)
884
+ AlterTable.new create_table_definition(name, false, {})
603
885
  end
604
886
  end
605
887
  end