activerecord 4.0.0.beta1 → 4.0.0.rc1

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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +573 -30
  3. data/README.rdoc +3 -3
  4. data/lib/active_record.rb +8 -2
  5. data/lib/active_record/associations.rb +16 -9
  6. data/lib/active_record/associations/association.rb +8 -6
  7. data/lib/active_record/associations/association_scope.rb +2 -1
  8. data/lib/active_record/associations/belongs_to_association.rb +2 -2
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  10. data/lib/active_record/associations/builder/belongs_to.rb +37 -5
  11. data/lib/active_record/associations/collection_association.rb +38 -14
  12. data/lib/active_record/associations/collection_proxy.rb +18 -15
  13. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +3 -3
  14. data/lib/active_record/associations/has_many_association.rb +4 -3
  15. data/lib/active_record/associations/has_many_through_association.rb +1 -1
  16. data/lib/active_record/associations/has_one_association.rb +1 -1
  17. data/lib/active_record/associations/join_dependency.rb +29 -8
  18. data/lib/active_record/associations/join_dependency/join_association.rb +26 -6
  19. data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
  20. data/lib/active_record/associations/join_dependency/join_part.rb +6 -6
  21. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
  22. data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
  23. data/lib/active_record/associations/through_association.rb +1 -1
  24. data/lib/active_record/attribute_assignment.rb +5 -5
  25. data/lib/active_record/attribute_methods.rb +20 -5
  26. data/lib/active_record/attribute_methods/dirty.rb +5 -1
  27. data/lib/active_record/attribute_methods/primary_key.rb +1 -1
  28. data/lib/active_record/attribute_methods/serialization.rb +9 -2
  29. data/lib/active_record/autosave_association.rb +19 -5
  30. data/lib/active_record/base.rb +3 -3
  31. data/lib/active_record/callbacks.rb +1 -1
  32. data/lib/active_record/coders/yaml_column.rb +8 -13
  33. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +3 -9
  34. data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -1
  35. data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -1
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +2 -8
  37. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +60 -61
  38. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +13 -2
  39. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +291 -153
  40. data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract_adapter.rb +92 -1
  42. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +55 -29
  43. data/lib/active_record/connection_adapters/column.rb +4 -4
  44. data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
  45. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -3
  46. data/lib/active_record/connection_adapters/mysql_adapter.rb +5 -5
  47. data/lib/active_record/connection_adapters/postgresql/cast.rb +22 -2
  48. data/lib/active_record/connection_adapters/postgresql/oid.rb +25 -6
  49. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -13
  50. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +50 -9
  51. data/lib/active_record/connection_adapters/postgresql_adapter.rb +53 -24
  52. data/lib/active_record/connection_adapters/schema_cache.rb +35 -7
  53. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +13 -5
  54. data/lib/active_record/connection_handling.rb +7 -7
  55. data/lib/active_record/core.rb +43 -8
  56. data/lib/active_record/counter_cache.rb +2 -1
  57. data/lib/active_record/errors.rb +11 -10
  58. data/lib/active_record/explain.rb +9 -7
  59. data/lib/active_record/explain_registry.rb +30 -0
  60. data/lib/active_record/explain_subscriber.rb +3 -2
  61. data/lib/active_record/fixture_set/file.rb +1 -2
  62. data/lib/active_record/fixtures.rb +13 -7
  63. data/lib/active_record/inheritance.rb +12 -4
  64. data/lib/active_record/integration.rb +3 -3
  65. data/lib/active_record/locking/optimistic.rb +2 -2
  66. data/lib/active_record/log_subscriber.rb +2 -2
  67. data/lib/active_record/migration.rb +69 -21
  68. data/lib/active_record/model_schema.rb +1 -1
  69. data/lib/active_record/nested_attributes.rb +98 -46
  70. data/lib/active_record/persistence.rb +3 -3
  71. data/lib/active_record/querying.rb +1 -1
  72. data/lib/active_record/railtie.rb +18 -4
  73. data/lib/active_record/railties/console_sandbox.rb +3 -2
  74. data/lib/active_record/railties/controller_runtime.rb +2 -1
  75. data/lib/active_record/railties/databases.rake +38 -80
  76. data/lib/active_record/reflection.rb +36 -3
  77. data/lib/active_record/relation.rb +18 -8
  78. data/lib/active_record/relation/calculations.rb +10 -5
  79. data/lib/active_record/relation/delegation.rb +3 -5
  80. data/lib/active_record/relation/finder_methods.rb +27 -14
  81. data/lib/active_record/relation/merger.rb +30 -2
  82. data/lib/active_record/relation/predicate_builder.rb +1 -6
  83. data/lib/active_record/relation/query_methods.rb +113 -16
  84. data/lib/active_record/runtime_registry.rb +17 -0
  85. data/lib/active_record/schema_dumper.rb +5 -1
  86. data/lib/active_record/schema_migration.rb +8 -5
  87. data/lib/active_record/scoping.rb +56 -2
  88. data/lib/active_record/scoping/default.rb +12 -11
  89. data/lib/active_record/scoping/named.rb +7 -3
  90. data/lib/active_record/serialization.rb +1 -1
  91. data/lib/active_record/statement_cache.rb +26 -0
  92. data/lib/active_record/tasks/database_tasks.rb +55 -10
  93. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  94. data/lib/active_record/tasks/mysql_database_tasks.rb +7 -2
  95. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  96. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  97. data/lib/active_record/timestamp.rb +6 -0
  98. data/lib/active_record/transactions.rb +7 -3
  99. data/lib/active_record/validations.rb +1 -2
  100. data/lib/active_record/validations/uniqueness.rb +7 -3
  101. data/lib/active_record/version.rb +7 -6
  102. data/lib/rails/generators/active_record/migration/migration_generator.rb +9 -2
  103. data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
  104. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  105. data/lib/rails/generators/active_record/model/templates/model.rb +4 -1
  106. metadata +17 -12
  107. data/examples/associations.png +0 -0
@@ -1,10 +1,12 @@
1
+ require 'ipaddr'
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters # :nodoc:
3
5
  # The goal of this module is to move Adapter specific column
4
6
  # definitions to the Adapter instead of having it in the schema
5
7
  # dumper itself. This code represents the normal case.
6
- # We can then redefine how certain data types may be handled in the schema dumper on the
7
- # Adapter level by over-writing this code inside the database spececific adapters
8
+ # We can then redefine how certain data types may be handled in the schema dumper on the
9
+ # Adapter level by over-writing this code inside the database specific adapters
8
10
  module ColumnDumper
9
11
  def column_spec(column, types)
10
12
  spec = prepare_column_options(column, types)
@@ -50,6 +52,15 @@ module ActiveRecord
50
52
  when Range
51
53
  # infinity dumps as Infinity, which causes uninitialized constant error
52
54
  value.inspect.gsub('Infinity', '::Float::INFINITY')
55
+ when IPAddr
56
+ subnet_mask = value.instance_variable_get(:@mask_addr)
57
+
58
+ # If the subnet mask is equal to /32, don't output it
59
+ if subnet_mask == (2**32 - 1)
60
+ "\"#{value.to_s}\""
61
+ else
62
+ "\"#{value.to_s}/#{subnet_mask.to_s(2).count('1')}\""
63
+ end
53
64
  else
54
65
  value.inspect
55
66
  end
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  module SchemaStatements
6
6
  include ActiveRecord::Migration::JoinTable
7
7
 
8
- # Returns a Hash of mappings from the abstract data types to the native
8
+ # Returns a hash of mappings from the abstract data types to the native
9
9
  # database types. See TableDefinition#column for details on the recognized
10
10
  # abstract data types.
11
11
  def native_database_types
@@ -20,6 +20,7 @@ module ActiveRecord
20
20
  # Checks to see if the table +table_name+ exists on the database.
21
21
  #
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,17 +30,18 @@ module ActiveRecord
29
30
 
30
31
  # Checks to see if an index exists on a table for a given index definition.
31
32
  #
32
- # # Check an index exists
33
- # index_exists?(:suppliers, :company_id)
33
+ # # Check an index exists
34
+ # index_exists?(:suppliers, :company_id)
35
+ #
36
+ # # Check an index on multiple columns exists
37
+ # index_exists?(:suppliers, [:company_id, :company_type])
34
38
  #
35
- # # Check an index on multiple columns exists
36
- # index_exists?(:suppliers, [:company_id, :company_type])
39
+ # # Check a unique index exists
40
+ # index_exists?(:suppliers, :company_id, unique: true)
37
41
  #
38
- # # Check a unique index exists
39
- # index_exists?(:suppliers, :company_id, unique: true)
42
+ # # Check an index with a custom name exists
43
+ # index_exists?(:suppliers, :company_id, name: "idx_company_id"
40
44
  #
41
- # # Check an index with a custom name exists
42
- # index_exists?(:suppliers, :company_id, name: "idx_company_id"
43
45
  def index_exists?(table_name, column_name, options = {})
44
46
  column_names = Array(column_name)
45
47
  index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, :column => column_names)
@@ -56,17 +58,18 @@ module ActiveRecord
56
58
 
57
59
  # Checks to see if a column exists in a given table.
58
60
  #
59
- # # Check a column exists
60
- # 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)
61
66
  #
62
- # # Check a column exists of a particular type
63
- # 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)
64
72
  #
65
- # # Check a column exists with a specific definition
66
- # column_exists?(:suppliers, :name, :string, limit: 100)
67
- # column_exists?(:suppliers, :name, :string, default: 'default')
68
- # column_exists?(:suppliers, :name, :string, null: false)
69
- # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
70
73
  def column_exists?(table_name, column_name, type = nil, options = {})
71
74
  columns(table_name).any?{ |c| c.name == column_name.to_s &&
72
75
  (!type || c.type == type) &&
@@ -84,27 +87,30 @@ module ActiveRecord
84
87
  # form or the regular form, like this:
85
88
  #
86
89
  # === Block form
87
- # # create_table() passes a TableDefinition object to the block.
88
- # # This form will not only create the table, but also columns for the
89
- # # table.
90
90
  #
91
- # create_table(:suppliers) do |t|
92
- # t.column :name, :string, limit: 60
93
- # # Other fields here
94
- # 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
95
99
  #
96
100
  # === Block form, with shorthand
97
- # # You can also use the column types as method calls, rather than calling the column method.
98
- # create_table(:suppliers) do |t|
99
- # t.string :name, limit: 60
100
- # # Other fields here
101
- # 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
102
107
  #
103
108
  # === Regular form
104
- # # Creates a table called 'suppliers' with no columns.
105
- # create_table(:suppliers)
106
- # # Add a column to 'suppliers'.
107
- # 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})
108
114
  #
109
115
  # The +options+ hash can include the following keys:
110
116
  # [<tt>:id</tt>]
@@ -127,37 +133,53 @@ module ActiveRecord
127
133
  # Defaults to false.
128
134
  #
129
135
  # ====== Add a backend specific option to the generated SQL (MySQL)
130
- # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
136
+ #
137
+ # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
138
+ #
131
139
  # generates:
132
- # CREATE TABLE suppliers (
133
- # id int(11) DEFAULT NULL auto_increment PRIMARY KEY
134
- # ) 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
135
144
  #
136
145
  # ====== Rename the primary key column
137
- # create_table(:objects, primary_key: 'guid') do |t|
138
- # t.column :name, :string, limit: 80
139
- # end
146
+ #
147
+ # create_table(:objects, primary_key: 'guid') do |t|
148
+ # t.column :name, :string, limit: 80
149
+ # end
150
+ #
140
151
  # generates:
141
- # CREATE TABLE objects (
142
- # guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
143
- # name varchar(80)
144
- # )
152
+ #
153
+ # CREATE TABLE objects (
154
+ # guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
155
+ # name varchar(80)
156
+ # )
145
157
  #
146
158
  # ====== Do not add a primary key column
147
- # create_table(:categories_suppliers, id: false) do |t|
148
- # t.column :category_id, :integer
149
- # t.column :supplier_id, :integer
150
- # 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
+ #
151
165
  # generates:
152
- # CREATE TABLE categories_suppliers (
153
- # category_id int,
154
- # supplier_id int
155
- # )
166
+ #
167
+ # CREATE TABLE categories_suppliers (
168
+ # category_id int,
169
+ # supplier_id int
170
+ # )
156
171
  #
157
172
  # See also TableDefinition#column for details on how to create columns.
158
173
  def create_table(table_name, options = {})
159
- td = table_definition
160
- 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
161
183
 
162
184
  yield td if block_given?
163
185
 
@@ -165,19 +187,15 @@ module ActiveRecord
165
187
  drop_table(table_name, options)
166
188
  end
167
189
 
168
- create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
169
- create_sql << "#{quote_table_name(table_name)} ("
170
- create_sql << td.to_sql
171
- create_sql << ") #{options[:options]}"
172
- execute create_sql
190
+ execute schema_creation.accept td
173
191
  td.indexes.each_pair { |c,o| add_index table_name, c, o }
174
192
  end
175
193
 
176
194
  # Creates a new join table with the name created using the lexical order of the first two
177
195
  # arguments. These arguments can be a String or a Symbol.
178
196
  #
179
- # # Creates a table called 'assemblies_parts' with no id.
180
- # create_join_table(:assemblies, :parts)
197
+ # # Creates a table called 'assemblies_parts' with no id.
198
+ # create_join_table(:assemblies, :parts)
181
199
  #
182
200
  # You can pass a +options+ hash can include the following keys:
183
201
  # [<tt>:table_name</tt>]
@@ -201,12 +219,16 @@ module ActiveRecord
201
219
  # end
202
220
  #
203
221
  # ====== Add a backend specific option to the generated SQL (MySQL)
204
- # create_join_table(:assemblies, :parts, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
222
+ #
223
+ # create_join_table(:assemblies, :parts, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
224
+ #
205
225
  # generates:
206
- # CREATE TABLE assemblies_parts (
207
- # assembly_id int NOT NULL,
208
- # part_id int NOT NULL,
209
- # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
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
+ #
210
232
  def create_join_table(table_1, table_2, options = {})
211
233
  join_table_name = find_join_table_name(table_1, table_2, options)
212
234
 
@@ -223,7 +245,7 @@ module ActiveRecord
223
245
  end
224
246
 
225
247
  # Drops the join table specified by the given arguments.
226
- # See create_join_table for details.
248
+ # See +create_join_table+ for details.
227
249
  #
228
250
  # Although this command ignores the block if one is given, it can be helpful
229
251
  # to provide one in a migration's +change+ method so it can be reverted.
@@ -235,79 +257,88 @@ module ActiveRecord
235
257
 
236
258
  # A block for changing columns in +table+.
237
259
  #
238
- # # change_table() yields a Table instance
239
- # change_table(:suppliers) do |t|
240
- # t.column :name, :string, limit: 60
241
- # # Other column alterations here
242
- # 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
243
265
  #
244
266
  # The +options+ hash can include the following keys:
245
267
  # [<tt>:bulk</tt>]
246
268
  # Set this to true to make this a bulk alter query, such as
247
- # 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 ...
248
271
  #
249
272
  # Defaults to false.
250
273
  #
251
274
  # ====== Add a column
252
- # change_table(:suppliers) do |t|
253
- # t.column :name, :string, limit: 60
254
- # end
275
+ #
276
+ # change_table(:suppliers) do |t|
277
+ # t.column :name, :string, limit: 60
278
+ # end
255
279
  #
256
280
  # ====== Add 2 integer columns
257
- # change_table(:suppliers) do |t|
258
- # t.integer :width, :height, null: false, default: 0
259
- # end
281
+ #
282
+ # change_table(:suppliers) do |t|
283
+ # t.integer :width, :height, null: false, default: 0
284
+ # end
260
285
  #
261
286
  # ====== Add created_at/updated_at columns
262
- # change_table(:suppliers) do |t|
263
- # t.timestamps
264
- # end
287
+ #
288
+ # change_table(:suppliers) do |t|
289
+ # t.timestamps
290
+ # end
265
291
  #
266
292
  # ====== Add a foreign key column
267
- # change_table(:suppliers) do |t|
268
- # t.references :company
269
- # end
270
293
  #
271
- # 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.
272
299
  #
273
300
  # ====== Add a polymorphic foreign key column
301
+ #
274
302
  # change_table(:suppliers) do |t|
275
303
  # t.belongs_to :company, polymorphic: true
276
304
  # end
277
305
  #
278
- # 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.
279
307
  #
280
308
  # ====== Remove a column
309
+ #
281
310
  # change_table(:suppliers) do |t|
282
311
  # t.remove :company
283
312
  # end
284
313
  #
285
314
  # ====== Remove several columns
315
+ #
286
316
  # change_table(:suppliers) do |t|
287
317
  # t.remove :company_id
288
318
  # t.remove :width, :height
289
319
  # end
290
320
  #
291
321
  # ====== Remove an index
322
+ #
292
323
  # change_table(:suppliers) do |t|
293
324
  # t.remove_index :company_id
294
325
  # end
295
326
  #
296
- # See also Table for details on
297
- # all of the various column transformation
327
+ # See also Table for details on all of the various column transformation.
298
328
  def change_table(table_name, options = {})
299
329
  if supports_bulk_alter? && options[:bulk]
300
330
  recorder = ActiveRecord::Migration::CommandRecorder.new(self)
301
- yield Table.new(table_name, recorder)
331
+ yield update_table_definition(table_name, recorder)
302
332
  bulk_change_table(table_name, recorder.commands)
303
333
  else
304
- yield Table.new(table_name, self)
334
+ yield update_table_definition(table_name, self)
305
335
  end
306
336
  end
307
337
 
308
338
  # Renames a table.
309
339
  #
310
- # rename_table('octopuses', 'octopi')
340
+ # rename_table('octopuses', 'octopi')
341
+ #
311
342
  def rename_table(table_name, new_name)
312
343
  raise NotImplementedError, "rename_table is not implemented"
313
344
  end
@@ -324,14 +355,15 @@ module ActiveRecord
324
355
  # Adds a new column to the named table.
325
356
  # See TableDefinition#column for details of the options you can use.
326
357
  def add_column(table_name, column_name, type, options = {})
327
- 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])}"
328
- add_column_options!(add_column_sql, options)
329
- execute(add_column_sql)
358
+ at = create_alter_table table_name
359
+ at.add_column(column_name, type, options)
360
+ execute schema_creation.accept at
330
361
  end
331
362
 
332
363
  # Removes the given columns from the table definition.
333
364
  #
334
- # remove_columns(:suppliers, :qualification, :experience)
365
+ # remove_columns(:suppliers, :qualification, :experience)
366
+ #
335
367
  def remove_columns(table_name, *column_names)
336
368
  raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.empty?
337
369
  column_names.each do |column_name|
@@ -341,7 +373,7 @@ module ActiveRecord
341
373
 
342
374
  # Removes the column from the table definition.
343
375
  #
344
- # remove_column(:suppliers, :qualification)
376
+ # remove_column(:suppliers, :qualification)
345
377
  #
346
378
  # The +type+ and +options+ parameters will be ignored if present. It can be helpful
347
379
  # to provide these in a migration's +change+ method so it can be reverted.
@@ -353,24 +385,50 @@ module ActiveRecord
353
385
  # Changes the column's definition according to the new options.
354
386
  # See TableDefinition#column for details of the options you can use.
355
387
  #
356
- # change_column(:suppliers, :name, :string, limit: 80)
357
- # change_column(:accounts, :description, :text)
388
+ # change_column(:suppliers, :name, :string, limit: 80)
389
+ # change_column(:accounts, :description, :text)
390
+ #
358
391
  def change_column(table_name, column_name, type, options = {})
359
392
  raise NotImplementedError, "change_column is not implemented"
360
393
  end
361
394
 
362
- # Sets a new default value for a column.
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)
363
403
  #
364
- # change_column_default(:suppliers, :qualification, 'new')
365
- # change_column_default(:accounts, :authorized, 1)
366
- # change_column_default(:users, :email, nil)
367
404
  def change_column_default(table_name, column_name, default)
368
405
  raise NotImplementedError, "change_column_default is not implemented"
369
406
  end
370
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
+
371
428
  # Renames a column.
372
429
  #
373
- # rename_column(:suppliers, :description, :name)
430
+ # rename_column(:suppliers, :description, :name)
431
+ #
374
432
  def rename_column(table_name, column_name, new_column_name)
375
433
  raise NotImplementedError, "rename_column is not implemented"
376
434
  end
@@ -382,60 +440,106 @@ module ActiveRecord
382
440
  # you pass <tt>:name</tt> as an option.
383
441
  #
384
442
  # ====== Creating a simple index
385
- # add_index(:suppliers, :name)
386
- # generates
387
- # 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)
388
449
  #
389
450
  # ====== Creating a unique index
390
- # add_index(:accounts, [:branch_id, :party_id], unique: true)
391
- # generates
392
- # 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)
393
457
  #
394
458
  # ====== Creating a named index
395
- # add_index(:accounts, [:branch_id, :party_id], unique: true, name: 'by_branch_party')
396
- # generates
459
+ #
460
+ # add_index(:accounts, [:branch_id, :party_id], unique: true, name: 'by_branch_party')
461
+ #
462
+ # generates:
463
+ #
397
464
  # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
398
465
  #
399
466
  # ====== Creating an index with specific key length
400
- # add_index(:accounts, :name, name: 'by_name', length: 10)
401
- # generates
402
- # CREATE INDEX by_name ON accounts(name(10))
403
467
  #
404
- # add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
405
- # generates
406
- # 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))
407
479
  #
408
- # Note: SQLite doesn't support index length
480
+ # Note: SQLite doesn't support index length.
409
481
  #
410
482
  # ====== Creating an index with a sort order (desc or asc, asc is the default)
411
- # add_index(:accounts, [:branch_id, :party_id, :surname], order: {branch_id: :desc, party_id: :asc})
412
- # generates
413
- # CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
414
483
  #
415
- # 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).
416
491
  #
417
492
  # ====== Creating a partial index
418
- # add_index(:accounts, [:branch_id, :party_id], unique: true, where: "active")
419
- # generates
420
- # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
421
493
  #
422
- # Note: only supported by PostgreSQL
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)
423
514
  #
515
+ # generates:
516
+ #
517
+ # CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
518
+ #
519
+ # Note: only supported by MySQL. Supported: <tt>:fulltext</tt> and <tt>:spatial</tt> on MyISAM tables.
424
520
  def add_index(table_name, column_name, options = {})
425
521
  index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
426
522
  execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
427
523
  end
428
524
 
429
- # 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.
430
528
  #
431
- # Remove the index_accounts_on_column in the accounts table.
432
529
  # remove_index :accounts, :column
433
- # Remove the index named index_accounts_on_branch_id in the accounts table.
530
+ #
531
+ # Removes the index named +index_accounts_on_branch_id+ in the +accounts+ table.
532
+ #
434
533
  # remove_index :accounts, column: :branch_id
435
- # Remove the index named index_accounts_on_branch_id_and_party_id in the accounts table.
534
+ #
535
+ # Removes the index named +index_accounts_on_branch_id_and_party_id+ in the +accounts+ table.
536
+ #
436
537
  # remove_index :accounts, column: [:branch_id, :party_id]
437
- # Remove the index named by_branch_party in the accounts table.
538
+ #
539
+ # Removes the index named +by_branch_party+ in the +accounts+ table.
540
+ #
438
541
  # remove_index :accounts, name: :by_branch_party
542
+ #
439
543
  def remove_index(table_name, options = {})
440
544
  remove_index!(table_name, index_name_for_remove(table_name, options))
441
545
  end
@@ -444,10 +548,12 @@ module ActiveRecord
444
548
  execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
445
549
  end
446
550
 
447
- # Rename an index.
551
+ # Renames an index.
552
+ #
553
+ # Rename the +index_people_on_last_name+ index to +index_users_on_last_name+:
448
554
  #
449
- # Rename the index_people_on_last_name index to index_users_on_last_name
450
555
  # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
556
+ #
451
557
  def rename_index(table_name, old_name, new_name)
452
558
  # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
453
559
  old_index_def = indexes(table_name).detect { |i| i.name == old_name }
@@ -470,7 +576,7 @@ module ActiveRecord
470
576
  end
471
577
  end
472
578
 
473
- # Verify the existence of an index with a given name.
579
+ # Verifies the existence of an index with a given name.
474
580
  #
475
581
  # The default argument is returned if the underlying implementation does not define the indexes method,
476
582
  # as there's no way to determine the correct answer in that case.
@@ -484,13 +590,16 @@ module ActiveRecord
484
590
  # <tt>add_reference</tt> and <tt>add_belongs_to</tt> are acceptable.
485
591
  #
486
592
  # ====== Create a user_id column
487
- # add_reference(:products, :user)
593
+ #
594
+ # add_reference(:products, :user)
488
595
  #
489
596
  # ====== Create a supplier_id and supplier_type columns
490
- # add_belongs_to(:products, :supplier, polymorphic: true)
597
+ #
598
+ # add_belongs_to(:products, :supplier, polymorphic: true)
491
599
  #
492
600
  # ====== Create a supplier_id, supplier_type columns and appropriate index
493
- # add_reference(:products, :supplier, polymorphic: true, index: true)
601
+ #
602
+ # add_reference(:products, :supplier, polymorphic: true, index: true)
494
603
  #
495
604
  def add_reference(table_name, ref_name, options = {})
496
605
  polymorphic = options.delete(:polymorphic)
@@ -505,10 +614,12 @@ module ActiveRecord
505
614
  # <tt>remove_reference</tt>, <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
506
615
  #
507
616
  # ====== Remove the reference
508
- # remove_reference(:products, :user, index: true)
617
+ #
618
+ # remove_reference(:products, :user, index: true)
509
619
  #
510
620
  # ====== Remove polymorphic reference
511
- # remove_reference(:products, :supplier, polymorphic: true)
621
+ #
622
+ # remove_reference(:products, :supplier, polymorphic: true)
512
623
  #
513
624
  def remove_reference(table_name, ref_name, options = {})
514
625
  remove_column(table_name, "#{ref_name}_id")
@@ -516,11 +627,6 @@ module ActiveRecord
516
627
  end
517
628
  alias :remove_belongs_to :remove_reference
518
629
 
519
- # Returns a string of <tt>CREATE TABLE</tt> SQL statement(s) for recreating the
520
- # entire structure of the database.
521
- def structure_dump
522
- end
523
-
524
630
  def dump_schema_information #:nodoc:
525
631
  sm_table = ActiveRecord::Migrator.schema_migrations_table_name
526
632
 
@@ -594,27 +700,33 @@ module ActiveRecord
594
700
  if options[:null] == false
595
701
  sql << " NOT NULL"
596
702
  end
703
+ if options[:auto_increment] == true
704
+ sql << " AUTO_INCREMENT"
705
+ end
597
706
  end
598
707
 
599
708
  # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
600
709
  # Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax.
601
710
  #
602
711
  # distinct("posts.id", "posts.created_at desc")
712
+ #
603
713
  def distinct(columns, order_by)
604
714
  "DISTINCT #{columns}"
605
715
  end
606
716
 
607
- # Adds timestamps (created_at and updated_at) columns to the named table.
717
+ # Adds timestamps (+created_at+ and +updated_at+) columns to the named table.
718
+ #
719
+ # add_timestamps(:suppliers)
608
720
  #
609
- # add_timestamps(:suppliers)
610
721
  def add_timestamps(table_name)
611
722
  add_column table_name, :created_at, :datetime
612
723
  add_column table_name, :updated_at, :datetime
613
724
  end
614
725
 
615
- # Removes the timestamp columns (created_at and updated_at) from the table definition.
726
+ # Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
616
727
  #
617
728
  # remove_timestamps(:suppliers)
729
+ #
618
730
  def remove_timestamps(table_name)
619
731
  remove_column table_name, :updated_at
620
732
  remove_column table_name, :created_at
@@ -655,12 +767,21 @@ module ActiveRecord
655
767
  index_name = index_name(table_name, column: column_names)
656
768
 
657
769
  if Hash === options # legacy support, since this param was a string
658
- options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal)
770
+ options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
659
771
 
660
772
  index_type = options[:unique] ? "UNIQUE" : ""
773
+ index_type = options[:type].to_s if options.key?(:type)
661
774
  index_name = options[:name].to_s if options.key?(:name)
662
775
  max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
663
776
 
777
+ if options.key?(:algorithm)
778
+ algorithm = index_algorithms.fetch(options[:algorithm]) {
779
+ raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
780
+ }
781
+ end
782
+
783
+ using = "USING #{options[:using]}" if options[:using].present?
784
+
664
785
  if supports_partial_index?
665
786
  index_options = options[:where] ? " WHERE #{options[:where]}" : ""
666
787
  end
@@ -675,6 +796,7 @@ module ActiveRecord
675
796
 
676
797
  index_type = options
677
798
  max_index_length = allowed_index_name_length
799
+ algorithm = using = nil
678
800
  end
679
801
 
680
802
  if index_name.length > max_index_length
@@ -685,13 +807,21 @@ module ActiveRecord
685
807
  end
686
808
  index_columns = quoted_columns_for_index(column_names, options).join(", ")
687
809
 
688
- [index_name, index_type, index_columns, index_options]
810
+ [index_name, index_type, index_columns, index_options, algorithm, using]
689
811
  end
690
812
 
691
813
  def index_name_for_remove(table_name, options = {})
692
814
  index_name = index_name(table_name, options)
693
815
 
694
816
  unless index_name_exists?(table_name, index_name, true)
817
+ if options.is_a?(Hash) && options.has_key?(:name)
818
+ options_without_column = options.dup
819
+ options_without_column.delete :column
820
+ index_name_without_column = index_name(table_name, options_without_column)
821
+
822
+ return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
823
+ end
824
+
695
825
  raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
696
826
  end
697
827
 
@@ -727,8 +857,16 @@ module ActiveRecord
727
857
  end
728
858
 
729
859
  private
730
- def table_definition
731
- TableDefinition.new(self)
860
+ def create_table_definition(name, temporary, options)
861
+ TableDefinition.new native_database_types, name, temporary, options
862
+ end
863
+
864
+ def create_alter_table(name)
865
+ AlterTable.new create_table_definition(name, false, {})
866
+ end
867
+
868
+ def update_table_definition(table_name, base)
869
+ Table.new(table_name, base)
732
870
  end
733
871
  end
734
872
  end