schema_statements.rb 41.6 KB
Newer Older
1
require 'active_record/migration/join_table'
2 3
require 'active_support/core_ext/string/access'
require 'digest'
4

5 6 7
module ActiveRecord
  module ConnectionAdapters # :nodoc:
    module SchemaStatements
8 9
      include ActiveRecord::Migration::JoinTable

10
      # Returns a hash of mappings from the abstract data types to the native
11
      # database types. See TableDefinition#column for details on the recognized
12 13
      # abstract data types.
      def native_database_types
14 15 16
        {}
      end

17 18 19 20
      def table_options(table_name)
        nil
      end

21
      # Truncates a table alias according to the limits of the current adapter.
22
      def table_alias_for(table_name)
23
        table_name[0...table_alias_length].tr('.', '_')
24 25
      end

26
      # Returns an array of table names defined in the database.
27
      def tables(name = nil)
28
        raise NotImplementedError, "#tables is not implemented"
29 30
      end

31 32 33
      # Checks to see if the table +table_name+ exists on the database.
      #
      #   table_exists?(:developers)
34
      #
35
      def table_exists?(table_name)
36
        tables.include?(table_name.to_s)
37 38
      end

39 40 41
      # Returns an array of indexes for the given table.
      # def indexes(table_name, name = nil) end

S
Sebastian Martinez 已提交
42
      # Checks to see if an index exists on a table for a given index definition.
43
      #
44 45 46 47 48
      #   # Check an index exists
      #   index_exists?(:suppliers, :company_id)
      #
      #   # Check an index on multiple columns exists
      #   index_exists?(:suppliers, [:company_id, :company_type])
49
      #
50 51
      #   # Check a unique index exists
      #   index_exists?(:suppliers, :company_id, unique: true)
52
      #
53
      #   # Check an index with a custom name exists
54
      #   index_exists?(:suppliers, :company_id, name: "idx_company_id")
55 56
      #
      def index_exists?(table_name, column_name, options = {})
57 58 59 60 61 62 63 64
        column_names = Array(column_name).map(&:to_s)
        index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, column: column_names)
        checks = []
        checks << lambda { |i| i.name == index_name }
        checks << lambda { |i| i.columns == column_names }
        checks << lambda { |i| i.unique } if options[:unique]

        indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
65 66
      end

67 68
      # Returns an array of Column objects for the table specified by +table_name+.
      # See the concrete implementation for details on the expected parameter values.
69
      def columns(table_name) end
70

71 72
      # Checks to see if a column exists in a given table.
      #
73 74 75 76 77
      #   # Check a column exists
      #   column_exists?(:suppliers, :name)
      #
      #   # Check a column exists of a particular type
      #   column_exists?(:suppliers, :name, :string)
78
      #
79 80 81 82 83
      #   # Check a column exists with a specific definition
      #   column_exists?(:suppliers, :name, :string, limit: 100)
      #   column_exists?(:suppliers, :name, :string, default: 'default')
      #   column_exists?(:suppliers, :name, :string, null: false)
      #   column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
84
      #
85
      def column_exists?(table_name, column_name, type = nil, options = {})
86 87
        column_name = column_name.to_s
        columns(table_name).any?{ |c| c.name == column_name &&
88 89 90 91 92 93
                                      (!type                     || c.type == type) &&
                                      (!options.key?(:limit)     || c.limit == options[:limit]) &&
                                      (!options.key?(:precision) || c.precision == options[:precision]) &&
                                      (!options.key?(:scale)     || c.scale == options[:scale]) &&
                                      (!options.key?(:default)   || c.default == options[:default]) &&
                                      (!options.key?(:null)      || c.null == options[:null]) }
94 95
      end

96 97 98 99 100 101
      # Returns just a table's primary key
      def primary_key(table_name)
        pks = primary_keys(table_name)
        pks.first if pks.one?
      end

P
Pratik Naik 已提交
102 103 104
      # Creates a new table with the name +table_name+. +table_name+ may either
      # be a String or a Symbol.
      #
105
      # There are two ways to work with +create_table+. You can use the block
106 107 108
      # form or the regular form, like this:
      #
      # === Block form
P
Pratik Naik 已提交
109
      #
110 111 112 113 114 115 116 117
      #   # create_table() passes a TableDefinition object to the block.
      #   # This form will not only create the table, but also columns for the
      #   # table.
      #
      #   create_table(:suppliers) do |t|
      #     t.column :name, :string, limit: 60
      #     # Other fields here
      #   end
118
      #
P
Pratik Naik 已提交
119
      # === Block form, with shorthand
120 121 122 123 124 125
      #
      #   # You can also use the column types as method calls, rather than calling the column method.
      #   create_table(:suppliers) do |t|
      #     t.string :name, limit: 60
      #     # Other fields here
      #   end
P
Pratik Naik 已提交
126
      #
127
      # === Regular form
128 129 130 131 132
      #
      #   # Creates a table called 'suppliers' with no columns.
      #   create_table(:suppliers)
      #   # Add a column to 'suppliers'.
      #   add_column(:suppliers, :name, :string, {limit: 60})
133 134 135
      #
      # The +options+ hash can include the following keys:
      # [<tt>:id</tt>]
136
      #   Whether to automatically add a primary key column. Defaults to true.
X
Xavier Noria 已提交
137
      #   Join tables for +has_and_belongs_to_many+ should set it to false.
138 139
      #
      #   A Symbol can be used to specify the type of the generated primary key column.
140 141
      # [<tt>:primary_key</tt>]
      #   The name of the primary key, if one is to be added automatically.
X
Xavier Noria 已提交
142
      #   Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
143
      #
144 145 146
      #   Note that Active Record models will automatically detect their
      #   primary key. This can be avoided by using +self.primary_key=+ on the model
      #   to define the key explicitly.
147
      #
148 149 150 151
      # [<tt>:options</tt>]
      #   Any extra options you want appended to the table definition.
      # [<tt>:temporary</tt>]
      #   Make a temporary table.
152
      # [<tt>:force</tt>]
153
      #   Set to true to drop the table before creating it.
154
      #   Set to +:cascade+ to drop dependent objects as well.
155
      #   Defaults to false.
156
      # [<tt>:as</tt>]
157 158
      #   SQL to use to generate the table. When this option is used, the block is
      #   ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
159 160
      #
      # ====== Add a backend specific option to the generated SQL (MySQL)
161 162 163
      #
      #   create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
      #
164
      # generates:
165 166
      #
      #   CREATE TABLE suppliers (
167
      #     id int(11) auto_increment PRIMARY KEY
168
      #   ) ENGINE=InnoDB DEFAULT CHARSET=utf8
169 170
      #
      # ====== Rename the primary key column
171 172 173 174 175
      #
      #   create_table(:objects, primary_key: 'guid') do |t|
      #     t.column :name, :string, limit: 80
      #   end
      #
176
      # generates:
177 178
      #
      #   CREATE TABLE objects (
179
      #     guid int(11) auto_increment PRIMARY KEY,
180 181
      #     name varchar(80)
      #   )
182
      #
183 184
      # ====== Change the primary key column type
      #
185 186
      #   create_table(:tags, id: :string) do |t|
      #     t.column :label, :string
187 188 189 190
      #   end
      #
      # generates:
      #
191
      #   CREATE TABLE tags (
192
      #     id varchar PRIMARY KEY,
193
      #     label varchar
194 195
      #   )
      #
196
      # ====== Do not add a primary key column
197 198 199 200 201 202
      #
      #   create_table(:categories_suppliers, id: false) do |t|
      #     t.column :category_id, :integer
      #     t.column :supplier_id, :integer
      #   end
      #
203
      # generates:
204 205 206 207 208
      #
      #   CREATE TABLE categories_suppliers (
      #     category_id int,
      #     supplier_id int
      #   )
209
      #
210 211 212 213 214 215 216 217 218 219
      # ====== Create a temporary table based on a query
      #
      #   create_table(:long_query, temporary: true,
      #     as: "SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id")
      #
      # generates:
      #
      #   CREATE TEMPORARY TABLE long_query AS
      #     SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
      #
220
      # See also TableDefinition#column for details on how to create columns.
221
      def create_table(table_name, options = {})
222
        td = create_table_definition table_name, options[:temporary], options[:options], options[:as]
223

224 225
        if options[:id] != false && !options[:as]
          pk = options.fetch(:primary_key) do
226
            Base.get_primary_key table_name.to_s.singularize
227
          end
228

229 230 231 232 233
          if pk.is_a?(Array)
            td.primary_keys pk
          else
            td.primary_key pk, options.fetch(:id, :primary_key), options
          end
234
        end
235

236 237
        yield td if block_given?

238
        if options[:force] && table_exists?(table_name)
239
          drop_table(table_name, options)
240 241
        end

242
        result = execute schema_creation.accept td
243 244 245 246 247 248 249 250 251 252 253

        unless supports_indexes_in_create?
          td.indexes.each_pair do |column_name, index_options|
            add_index(table_name, column_name, index_options)
          end
        end

        td.foreign_keys.each_pair do |other_table_name, foreign_key_options|
          add_foreign_key(table_name, other_table_name, foreign_key_options)
        end

254
        result
255
      end
256

257
      # Creates a new join table with the name created using the lexical order of the first two
W
Waseem Ahmad 已提交
258
      # arguments. These arguments can be a String or a Symbol.
259
      #
260 261
      #   # Creates a table called 'assemblies_parts' with no id.
      #   create_join_table(:assemblies, :parts)
262 263 264 265 266 267 268 269 270 271 272 273 274 275
      #
      # You can pass a +options+ hash can include the following keys:
      # [<tt>:table_name</tt>]
      #   Sets the table name overriding the default
      # [<tt>:column_options</tt>]
      #   Any extra options you want appended to the columns definition.
      # [<tt>:options</tt>]
      #   Any extra options you want appended to the table definition.
      # [<tt>:temporary</tt>]
      #   Make a temporary table.
      # [<tt>:force</tt>]
      #   Set to true to drop the table before creating it.
      #   Defaults to false.
      #
276 277 278
      # Note that +create_join_table+ does not create any indices by default; you can use
      # its block form to do so yourself:
      #
279
      #   create_join_table :products, :categories do |t|
280 281
      #     t.index :product_id
      #     t.index :category_id
282
      #   end
283
      #
284
      # ====== Add a backend specific option to the generated SQL (MySQL)
285 286 287
      #
      #   create_join_table(:assemblies, :parts, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
      #
288
      # generates:
289 290 291 292 293 294
      #
      #   CREATE TABLE assemblies_parts (
      #     assembly_id int NOT NULL,
      #     part_id int NOT NULL,
      #   ) ENGINE=InnoDB DEFAULT CHARSET=utf8
      #
295 296 297 298
      def create_join_table(table_1, table_2, options = {})
        join_table_name = find_join_table_name(table_1, table_2, options)

        column_options = options.delete(:column_options) || {}
299
        column_options.reverse_merge!(null: false)
300

301
        t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
302

303
        create_table(join_table_name, options.merge!(id: false)) do |td|
304 305 306
          td.integer t1_column, column_options
          td.integer t2_column, column_options
          yield td if block_given?
307 308 309
        end
      end

M
Marc-Andre Lafortune 已提交
310
      # Drops the join table specified by the given arguments.
311
      # See +create_join_table+ for details.
M
Marc-Andre Lafortune 已提交
312 313 314 315 316 317 318 319 320
      #
      # Although this command ignores the block if one is given, it can be helpful
      # to provide one in a migration's +change+ method so it can be reverted.
      # In that case, the block will be used by create_join_table.
      def drop_join_table(table_1, table_2, options = {})
        join_table_name = find_join_table_name(table_1, table_2, options)
        drop_table(join_table_name)
      end

321 322
      # A block for changing columns in +table+.
      #
323 324 325 326 327
      #   # change_table() yields a Table instance
      #   change_table(:suppliers) do |t|
      #     t.column :name, :string, limit: 60
      #     # Other column alterations here
      #   end
328
      #
329 330 331
      # The +options+ hash can include the following keys:
      # [<tt>:bulk</tt>]
      #   Set this to true to make this a bulk alter query, such as
332 333
      #
      #     ALTER TABLE `users` ADD COLUMN age INT(11), ADD COLUMN birthdate DATETIME ...
334 335 336
      #
      #   Defaults to false.
      #
337
      # ====== Add a column
338 339 340 341
      #
      #   change_table(:suppliers) do |t|
      #     t.column :name, :string, limit: 60
      #   end
342 343
      #
      # ====== Add 2 integer columns
344 345 346 347
      #
      #   change_table(:suppliers) do |t|
      #     t.integer :width, :height, null: false, default: 0
      #   end
348 349
      #
      # ====== Add created_at/updated_at columns
350 351 352 353
      #
      #   change_table(:suppliers) do |t|
      #     t.timestamps
      #   end
354 355 356
      #
      # ====== Add a foreign key column
      #
357 358 359 360 361
      #   change_table(:suppliers) do |t|
      #     t.references :company
      #   end
      #
      # Creates a <tt>company_id(integer)</tt> column.
362 363
      #
      # ====== Add a polymorphic foreign key column
364
      #
365
      #  change_table(:suppliers) do |t|
A
AvnerCohen 已提交
366
      #    t.belongs_to :company, polymorphic: true
367 368
      #  end
      #
369
      # Creates <tt>company_type(varchar)</tt> and <tt>company_id(integer)</tt> columns.
370 371
      #
      # ====== Remove a column
372
      #
373 374 375 376
      #  change_table(:suppliers) do |t|
      #    t.remove :company
      #  end
      #
377
      # ====== Remove several columns
378
      #
379 380 381 382 383 384
      #  change_table(:suppliers) do |t|
      #    t.remove :company_id
      #    t.remove :width, :height
      #  end
      #
      # ====== Remove an index
385
      #
386 387 388 389
      #  change_table(:suppliers) do |t|
      #    t.remove_index :company_id
      #  end
      #
390
      # See also Table for details on all of the various column transformation.
391 392 393
      def change_table(table_name, options = {})
        if supports_bulk_alter? && options[:bulk]
          recorder = ActiveRecord::Migration::CommandRecorder.new(self)
394
          yield update_table_definition(table_name, recorder)
395 396
          bulk_change_table(table_name, recorder.commands)
        else
397
          yield update_table_definition(table_name, self)
398
        end
399
      end
400

401
      # Renames a table.
402
      #
403 404
      #   rename_table('octopuses', 'octopi')
      #
405
      def rename_table(table_name, new_name)
406 407
        raise NotImplementedError, "rename_table is not implemented"
      end
408

409
      # Drops a table from the database.
410
      #
411 412 413
      # [<tt>:force</tt>]
      #   Set to +:cascade+ to drop dependent objects as well.
      #   Defaults to false.
414 415 416
      # [<tt>:if_exists</tt>]
      #   Set to +true+ to only drop the table if it exists.
      #   Defaults to false.
417 418 419
      #
      # Although this command ignores most +options+ and the block if one is given,
      # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
420
      # In that case, +options+ and the block will be used by create_table.
421
      def drop_table(table_name, options = {})
422
        execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
423 424
      end

425 426
      # Adds a new column to the named table.
      # See TableDefinition#column for details of the options you can use.
427 428 429 430
      #
      # Note: Not all options will be available, generally this command should
      # ignore most of them. In favor of doing a low-level call to simply
      # create a column.
431
      def add_column(table_name, column_name, type, options = {})
432 433 434
        at = create_alter_table table_name
        at.add_column(column_name, type, options)
        execute schema_creation.accept at
435 436
      end

437
      # Removes the given columns from the table definition.
438
      #
439 440
      #   remove_columns(:suppliers, :qualification, :experience)
      #
441 442 443 444 445 446 447 448 449
      def remove_columns(table_name, *column_names)
        raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.empty?
        column_names.each do |column_name|
          remove_column(table_name, column_name)
        end
      end

      # Removes the column from the table definition.
      #
450
      #   remove_column(:suppliers, :qualification)
451 452 453 454 455
      #
      # The +type+ and +options+ parameters will be ignored if present. It can be helpful
      # to provide these in a migration's +change+ method so it can be reverted.
      # In that case, +type+ and +options+ will be used by add_column.
      def remove_column(table_name, column_name, type = nil, options = {})
456
        execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
457
      end
458

459 460
      # Changes the column's definition according to the new options.
      # See TableDefinition#column for details of the options you can use.
461
      #
462 463 464
      #   change_column(:suppliers, :name, :string, limit: 80)
      #   change_column(:accounts, :description, :text)
      #
465 466 467 468
      def change_column(table_name, column_name, type, options = {})
        raise NotImplementedError, "change_column is not implemented"
      end

469 470 471 472 473 474
      # Sets a new default value for a column:
      #
      #   change_column_default(:suppliers, :qualification, 'new')
      #   change_column_default(:accounts, :authorized, 1)
      #
      # Setting the default to +nil+ effectively drops the default:
475
      #
476
      #   change_column_default(:users, :email, nil)
477
      #
478 479 480 481 482 483
      # Passing a hash containing +:from+ and +:to+ will make this change
      # reversible in migration:
      #
      #   change_column_default(:posts, :state, from: nil, to: "draft")
      #
      def change_column_default(table_name, column_name, default_or_changes)
484 485 486
        raise NotImplementedError, "change_column_default is not implemented"
      end

487
      # Sets or removes a +NOT NULL+ constraint on a column. The +null+ flag
C
Carlos Antonio da Silva 已提交
488
      # indicates whether the value can be +NULL+. For example
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
      #
      #   change_column_null(:users, :nickname, false)
      #
      # says nicknames cannot be +NULL+ (adds the constraint), whereas
      #
      #   change_column_null(:users, :nickname, true)
      #
      # allows them to be +NULL+ (drops the constraint).
      #
      # The method accepts an optional fourth argument to replace existing
      # +NULL+s with some other value. Use that one when enabling the
      # constraint if needed, since otherwise those rows would not be valid.
      #
      # Please note the fourth argument does not set a column's default.
      def change_column_null(table_name, column_name, null, default = nil)
        raise NotImplementedError, "change_column_null is not implemented"
      end

507
      # Renames a column.
508
      #
509 510
      #   rename_column(:suppliers, :description, :name)
      #
511 512 513 514
      def rename_column(table_name, column_name, new_column_name)
        raise NotImplementedError, "rename_column is not implemented"
      end

515
      # Adds a new index to the table. +column_name+ can be a single Symbol, or
516 517
      # an Array of Symbols.
      #
518 519
      # The index will be named after the table and the column name(s), unless
      # you pass <tt>:name</tt> as an option.
520
      #
521
      # ====== Creating a simple index
522 523 524
      #
      #   add_index(:suppliers, :name)
      #
525
      # generates:
526 527
      #
      #   CREATE INDEX suppliers_name_index ON suppliers(name)
528
      #
529
      # ====== Creating a unique index
530 531 532
      #
      #   add_index(:accounts, [:branch_id, :party_id], unique: true)
      #
533
      # generates:
534 535
      #
      #   CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
536
      #
537
      # ====== Creating a named index
538 539 540
      #
      #   add_index(:accounts, [:branch_id, :party_id], unique: true, name: 'by_branch_party')
      #
541
      # generates:
542
      #
543
      #  CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
544 545
      #
      # ====== Creating an index with specific key length
546 547 548
      #
      #   add_index(:accounts, :name, name: 'by_name', length: 10)
      #
549
      # generates:
550
      #
551 552 553 554
      #   CREATE INDEX by_name ON accounts(name(10))
      #
      #   add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
      #
555
      # generates:
556
      #
557 558 559
      #   CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
      #
      # Note: SQLite doesn't support index length.
560 561
      #
      # ====== Creating an index with a sort order (desc or asc, asc is the default)
562 563 564
      #
      #   add_index(:accounts, [:branch_id, :party_id, :surname], order: {branch_id: :desc, party_id: :asc})
      #
565
      # generates:
566
      #
567 568 569
      #   CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
      #
      # Note: MySQL doesn't yet support index order (it accepts the syntax but ignores it).
570
      #
571
      # ====== Creating a partial index
572 573 574
      #
      #   add_index(:accounts, [:branch_id, :party_id], unique: true, where: "active")
      #
575
      # generates:
576
      #
577
      #   CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
578
      #
579 580
      # Note: Partial indexes are only supported for PostgreSQL and SQLite 3.8.0+.
      #
D
doabit 已提交
581
      # ====== Creating an index with a specific method
582
      #
583
      #   add_index(:developers, :name, using: 'btree')
584
      #
585
      # generates:
586
      #
587 588
      #   CREATE INDEX index_developers_on_name ON developers USING btree (name) -- PostgreSQL
      #   CREATE INDEX index_developers_on_name USING btree ON developers (name) -- MySQL
D
doabit 已提交
589 590 591
      #
      # Note: only supported by PostgreSQL and MySQL
      #
592 593
      # ====== Creating an index with a specific type
      #
594
      #   add_index(:developers, :name, type: :fulltext)
595
      #
596
      # generates:
597 598 599 600
      #
      #   CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
      #
      # Note: only supported by MySQL. Supported: <tt>:fulltext</tt> and <tt>:spatial</tt> on MyISAM tables.
601
      def add_index(table_name, column_name, options = {})
602 603
        index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
        execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
604 605
      end

606 607 608
      # Removes the given index from the table.
      #
      # Removes the +index_accounts_on_column+ in the +accounts+ table.
609
      #
610
      #   remove_index :accounts, :branch_id
611 612 613
      #
      # Removes the index named +index_accounts_on_branch_id+ in the +accounts+ table.
      #
A
AvnerCohen 已提交
614
      #   remove_index :accounts, column: :branch_id
615 616 617
      #
      # Removes the index named +index_accounts_on_branch_id_and_party_id+ in the +accounts+ table.
      #
A
AvnerCohen 已提交
618
      #   remove_index :accounts, column: [:branch_id, :party_id]
619 620 621
      #
      # Removes the index named +by_branch_party+ in the +accounts+ table.
      #
A
AvnerCohen 已提交
622
      #   remove_index :accounts, name: :by_branch_party
623
      #
624
      def remove_index(table_name, options = {})
625
        index_name = index_name_for_remove(table_name, options)
626
        execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
627 628
      end

629 630 631
      # Renames an index.
      #
      # Rename the +index_people_on_last_name+ index to +index_users_on_last_name+:
632 633
      #
      #   rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
634
      #
635
      def rename_index(table_name, old_name, new_name)
636 637
        validate_index_length!(table_name, new_name)

638 639 640
        # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
        old_index_def = indexes(table_name).detect { |i| i.name == old_name }
        return unless old_index_def
641 642
        add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
        remove_index(table_name, name: old_name)
643 644 645
      end

      def index_name(table_name, options) #:nodoc:
R
Rafael Mendonça França 已提交
646
        if Hash === options
647
          if options[:column]
648
            "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
649
          elsif options[:name]
650
            options[:name]
651 652 653 654
          else
            raise ArgumentError, "You must specify the index name"
          end
        else
655
          index_name(table_name, :column => options)
656 657 658
        end
      end

659
      # Verifies the existence of an index with a given name.
660 661 662
      #
      # The default argument is returned if the underlying implementation does not define the indexes method,
      # as there's no way to determine the correct answer in that case.
663
      def index_name_exists?(table_name, index_name, default)
664
        return default unless respond_to?(:indexes)
665 666
        index_name = index_name.to_s
        indexes(table_name).detect { |i| i.name == index_name }
667 668
      end

669 670 671
      # Adds a reference. The reference column is an integer by default,
      # the <tt>:type</tt> option can be used to specify a different type.
      # Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
672 673
      # <tt>add_reference</tt> and <tt>add_belongs_to</tt> are acceptable.
      #
674 675 676 677 678 679 680 681
      # The +options+ hash can include the following keys:
      # [<tt>:type</tt>]
      #   The reference column type. Defaults to +:integer+.
      # [<tt>:index</tt>]
      #   Add an appropriate index. Defaults to false.
      # [<tt>:foreign_key</tt>]
      #   Add an appropriate foreign key. Defaults to false.
      # [<tt>:polymorphic</tt>]
682
      #   Whether an additional +_type+ column should be added. Defaults to false.
683
      #
684
      # ====== Create a user_id integer column
685 686
      #
      #   add_reference(:products, :user)
687
      #
688 689 690 691
      # ====== Create a user_id string column
      #
      #   add_reference(:products, :user, type: :string)
      #
692
      # ====== Create supplier_id, supplier_type columns and appropriate index
693 694
      #
      #   add_reference(:products, :supplier, polymorphic: true, index: true)
695
      #
696 697 698 699
      # ====== Create a supplier_id column and appropriate foreign key
      #
      #   add_reference(:products, :supplier, foreign_key: true)
      #
700 701
      def add_reference(table_name, *args)
        ReferenceDefinition.new(*args).add_to(update_table_definition(table_name, self))
702 703 704 705
      end
      alias :add_belongs_to :add_reference

      # Removes the reference(s). Also removes a +type+ column if one exists.
706
      # <tt>remove_reference</tt> and <tt>remove_belongs_to</tt> are acceptable.
707 708
      #
      # ====== Remove the reference
709 710
      #
      #   remove_reference(:products, :user, index: true)
711 712
      #
      # ====== Remove polymorphic reference
713 714
      #
      #   remove_reference(:products, :supplier, polymorphic: true)
715
      #
716 717 718 719
      # ====== Remove the reference with a foreign key
      #
      #   remove_reference(:products, :user, index: true, foreign_key: true)
      #
720
      def remove_reference(table_name, ref_name, options = {})
721 722 723 724
        if options[:foreign_key]
          reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
          remove_foreign_key(table_name, reference_name)
        end
725

726
        remove_column(table_name, "#{ref_name}_id")
727
        remove_column(table_name, "#{ref_name}_type") if options[:polymorphic]
728 729 730
      end
      alias :remove_belongs_to :remove_reference

Y
Yves Senn 已提交
731 732
      # Returns an array of foreign keys for the given table.
      # The foreign keys are represented as +ForeignKeyDefinition+ objects.
733 734 735 736
      def foreign_keys(table_name)
        raise NotImplementedError, "foreign_keys is not implemented"
      end

Y
Yves Senn 已提交
737 738 739 740
      # Adds a new foreign key. +from_table+ is the table with the key column,
      # +to_table+ contains the referenced primary key.
      #
      # The foreign key will be named after the following pattern: <tt>fk_rails_<identifier></tt>.
741 742
      # +identifier+ is a 10 character long string which is deterministically generated from the
      # +from_table+ and +column+. A custom name can be specified with the <tt>:name</tt> option.
Y
Yves Senn 已提交
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
      #
      # ====== Creating a simple foreign key
      #
      #   add_foreign_key :articles, :authors
      #
      # generates:
      #
      #   ALTER TABLE "articles" ADD CONSTRAINT articles_author_id_fk FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
      #
      # ====== Creating a foreign key on a specific column
      #
      #   add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id"
      #
      # generates:
      #
      #   ALTER TABLE "articles" ADD CONSTRAINT fk_rails_58ca3d3a82 FOREIGN KEY ("author_id") REFERENCES "users" ("lng_id")
      #
      # ====== Creating a cascading foreign key
      #
      #   add_foreign_key :articles, :authors, on_delete: :cascade
      #
      # generates:
      #
      #   ALTER TABLE "articles" ADD CONSTRAINT articles_author_id_fk FOREIGN KEY ("author_id") REFERENCES "authors" ("id") ON DELETE CASCADE
      #
      # The +options+ hash can include the following keys:
      # [<tt>:column</tt>]
      #   The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt>
      # [<tt>:primary_key</tt>]
      #   The primary key column name on +to_table+. Defaults to +id+.
      # [<tt>:name</tt>]
      #   The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
      # [<tt>:on_delete</tt>]
776
      #   Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
Y
Yves Senn 已提交
777
      # [<tt>:on_update</tt>]
778
      #   Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
779
      def add_foreign_key(from_table, to_table, options = {})
780 781
        return unless supports_foreign_keys?

782
        options[:column] ||= foreign_key_column_for(to_table)
783

784
        options = {
785
          column: options[:column],
Y
Yves Senn 已提交
786
          primary_key: options[:primary_key],
787
          name: foreign_key_name(from_table, options),
Y
Yves Senn 已提交
788 789
          on_delete: options[:on_delete],
          on_update: options[:on_update]
790 791 792 793
        }
        at = create_alter_table from_table
        at.add_foreign_key to_table, options

794
        execute schema_creation.accept(at)
795 796
      end

797 798
      # Removes the given foreign key from the table. Any option parameters provided
      # will be used to re-add the foreign key in case of a migration rollback.
799 800
      # It is recommended that you provide any options used when creating the foreign
      # key so that the migration can be reverted properly.
Y
Yves Senn 已提交
801 802 803 804 805 806 807 808 809 810 811 812 813
      #
      # Removes the foreign key on +accounts.branch_id+.
      #
      #   remove_foreign_key :accounts, :branches
      #
      # Removes the foreign key on +accounts.owner_id+.
      #
      #   remove_foreign_key :accounts, column: :owner_id
      #
      # Removes the foreign key named +special_fk_name+ on the +accounts+ table.
      #
      #   remove_foreign_key :accounts, name: :special_fk_name
      #
814
      # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
815
      def remove_foreign_key(from_table, options_or_to_table = {})
816 817
        return unless supports_foreign_keys?

818
        fk_name_to_delete = foreign_key_for!(from_table, options_or_to_table).name
Y
Yves Senn 已提交
819

820
        at = create_alter_table from_table
Y
Yves Senn 已提交
821
        at.drop_foreign_key fk_name_to_delete
822

823
        execute schema_creation.accept(at)
824 825
      end

826 827 828 829 830
      # Checks to see if a foreign key exists on a table for a given foreign key definition.
      #
      #   # Check a foreign key exists
      #   foreign_key_exists?(:accounts, :branches)
      #
831
      #   # Check a foreign key on a specified column exists
832 833 834 835 836 837 838 839 840 841 842 843 844
      #   foreign_key_exists?(:accounts, column: :owner_id)
      #
      #   # Check a foreign key with a custom name exists
      #   foreign_key_exists?(:accounts, name: "special_fk_name")
      #
      def foreign_key_exists?(from_table, options_or_to_table = {})
        foreign_key_for(from_table, options_or_to_table).present?
      end

      def foreign_key_for(from_table, options_or_to_table = {}) # :nodoc:
        return unless supports_foreign_keys?
        foreign_keys(from_table).detect {|fk| fk.defined_for? options_or_to_table }
      end
845

846 847 848 849 850
      def foreign_key_for!(from_table, options_or_to_table = {}) # :nodoc:
        foreign_key_for(from_table, options_or_to_table) or \
          raise ArgumentError, "Table '#{from_table}' has no foreign key for #{options_or_to_table}"
      end

851
      def foreign_key_column_for(table_name) # :nodoc:
852 853 854 855
        prefix = Base.table_name_prefix
        suffix = Base.table_name_suffix
        name = table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
        "#{name.singularize}_id"
856 857
      end

858 859
      def dump_schema_information #:nodoc:
        sm_table = ActiveRecord::Migrator.schema_migrations_table_name
860

J
Jon Leighton 已提交
861
        ActiveRecord::SchemaMigration.order('version').map { |sm|
862 863
          "INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');"
        }.join "\n\n"
864 865
      end

866 867
      # Should not be called normally, but this operation is non-destructive.
      # The migrations module handles this automatically.
868
      def initialize_schema_migrations_table
869
        ActiveRecord::SchemaMigration.create_table
870 871
      end

872
      def assume_migrated_upto_version(version, migrations_paths)
873
        migrations_paths = Array(migrations_paths)
874
        version = version.to_i
875
        sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
876

877
        migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
878 879
        paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" }
        versions = Dir[*paths].map do |filename|
880 881 882
          filename.split('/').last.split('_').first.to_i
        end

883
        unless migrated.include?(version)
884
          execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
885 886 887 888 889 890 891
        end

        inserted = Set.new
        (versions - migrated).each do |v|
          if inserted.include?(v)
            raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
          elsif v < version
892
            execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
893 894
            inserted << v
          end
895 896
        end
      end
897

898
      def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
899
        if native = native_database_types[type.to_sym]
900
          column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
901

902
          if type == :decimal # ignore limit, use precision and scale
903
            scale ||= native[:scale]
904 905

            if precision ||= native[:precision]
906 907 908 909 910
              if scale
                column_type_sql << "(#{precision},#{scale})"
              else
                column_type_sql << "(#{precision})"
              end
911
            elsif scale
C
Typo  
Chris Patuzzo 已提交
912
              raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
913
            end
914

915 916 917 918 919 920
          elsif [:datetime, :time].include?(type) && precision ||= native[:precision]
            if (0..6) === precision
              column_type_sql << "(#{precision})"
            else
              raise(ActiveRecordError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6")
            end
921
          elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
922
            column_type_sql << "(#{limit})"
923
          end
924 925

          column_type_sql
926
        else
927
          type.to_s
928
        end
929 930
      end

931 932 933 934 935
      # Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
      # Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax - they
      # require the order columns appear in the SELECT.
      #
      #   columns_for_distinct("posts.id", ["posts.created_at desc"])
936
      def columns_for_distinct(columns, orders) #:nodoc:
937
        columns
938
      end
939

940 941
      # Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
      # Additional options (like <tt>null: false</tt>) are forwarded to #add_column.
942
      #
943
      #   add_timestamps(:suppliers, null: false)
944
      #
945
      def add_timestamps(table_name, options = {})
946 947
        options[:null] = false if options[:null].nil?

948 949
        add_column table_name, :created_at, :datetime, options
        add_column table_name, :updated_at, :datetime, options
950
      end
951

952
      # Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
953
      #
954
      #  remove_timestamps(:suppliers)
955
      #
956
      def remove_timestamps(table_name, options = {})
957 958
        remove_column table_name, :updated_at
        remove_column table_name, :created_at
959
      end
960

961 962 963 964
      def update_table_definition(table_name, base) #:nodoc:
        Table.new(table_name, base)
      end

965 966 967 968 969 970
      def add_index_options(table_name, column_name, options = {}) #:nodoc:
        column_names = Array(column_name)

        options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)

        index_type = options[:type].to_s if options.key?(:type)
971
        index_type ||= options[:unique] ? "UNIQUE" : ""
972
        index_name = options[:name].to_s if options.key?(:name)
973
        index_name ||= index_name(table_name, column: column_names)
974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998
        max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length

        if options.key?(:algorithm)
          algorithm = index_algorithms.fetch(options[:algorithm]) {
            raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
          }
        end

        using = "USING #{options[:using]}" if options[:using].present?

        if supports_partial_index?
          index_options = options[:where] ? " WHERE #{options[:where]}" : ""
        end

        if index_name.length > max_index_length
          raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
        end
        if table_exists?(table_name) && index_name_exists?(table_name, index_name, false)
          raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
        end
        index_columns = quoted_columns_for_index(column_names, options).join(", ")

        [index_name, index_type, index_columns, index_options, algorithm, using]
      end

999 1000 1001 1002
      def options_include_default?(options)
        options.include?(:default) && !(options[:null] == false && options[:default].nil?)
      end

1003
      protected
1004 1005 1006 1007
        def add_index_sort_order(option_strings, column_names, options = {})
          if options.is_a?(Hash) && order = options[:order]
            case order
            when Hash
1008
              column_names.each {|name| option_strings[name] += " #{order[name].upcase}" if order.has_key?(name)}
1009 1010 1011 1012 1013 1014 1015 1016
            when String
              column_names.each {|name| option_strings[name] += " #{order.upcase}"}
            end
          end

          return option_strings
        end

1017
        # Overridden by the MySQL adapter for supporting index lengths
1018
        def quoted_columns_for_index(column_names, options = {})
1019 1020 1021 1022 1023 1024 1025 1026
          option_strings = Hash[column_names.map {|name| [name, '']}]

          # add index sort order if supported
          if supports_index_sort_order?
            option_strings = add_index_sort_order(option_strings, column_names, options)
          end

          column_names.map {|name| quote_column_name(name) + option_strings[name]}
1027 1028
        end

1029 1030 1031 1032
        def index_name_for_remove(table_name, options = {})
          index_name = index_name(table_name, options)

          unless index_name_exists?(table_name, index_name, true)
1033
            if options.is_a?(Hash) && options.has_key?(:name)
1034 1035 1036
              options_without_column = options.dup
              options_without_column.delete :column
              index_name_without_column = index_name(table_name, options_without_column)
1037

1038 1039
              return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
            end
1040

1041 1042 1043 1044 1045 1046
            raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
          end

          index_name
        end

1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068
        def rename_table_indexes(table_name, new_name)
          indexes(new_name).each do |index|
            generated_index_name = index_name(table_name, column: index.columns)
            if generated_index_name == index.name
              rename_index new_name, generated_index_name, index_name(new_name, column: index.columns)
            end
          end
        end

        def rename_column_indexes(table_name, column_name, new_column_name)
          column_name, new_column_name = column_name.to_s, new_column_name.to_s
          indexes(table_name).each do |index|
            next unless index.columns.include?(new_column_name)
            old_columns = index.columns.dup
            old_columns[old_columns.index(new_column_name)] = column_name
            generated_index_name = index_name(table_name, column: old_columns)
            if generated_index_name == index.name
              rename_index table_name, generated_index_name, index_name(table_name, column: index.columns)
            end
          end
        end

1069
      private
1070
      def create_table_definition(name, temporary = false, options = nil, as = nil)
1071
        TableDefinition.new native_database_types, name, temporary, options, as
1072
      end
1073

1074
      def create_alter_table(name)
1075
        AlterTable.new create_table_definition(name)
1076
      end
1077 1078

      def foreign_key_name(table_name, options) # :nodoc:
1079 1080
        identifier = "#{table_name}_#{options.fetch(:column)}_fk"
        hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
1081
        options.fetch(:name) do
1082
          "fk_rails_#{hashed_identifier}"
1083 1084
        end
      end
1085

1086
      def validate_index_length!(table_name, new_name) # :nodoc:
1087 1088 1089 1090
        if new_name.length > allowed_index_name_length
          raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
        end
      end
1091 1092 1093 1094 1095 1096 1097 1098

      def extract_new_default_value(default_or_changes)
        if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
          default_or_changes[:to]
        else
          default_or_changes
        end
      end
1099 1100
    end
  end
1101
end