diff options
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb')
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb | 200 |
1 files changed, 64 insertions, 136 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index a768ee2d70..1cda23dc1d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -1,8 +1,3 @@ -require 'date' -require 'set' -require 'bigdecimal' -require 'bigdecimal/util' - module ActiveRecord module ConnectionAdapters #:nodoc: # Abstract representation of an index definition on a table. Instances of @@ -15,16 +10,22 @@ module ActiveRecord # are typically created by methods in TableDefinition, and added to the # +columns+ attribute of said TableDefinition object, in order to be used # for generating a number of table creation or table changing SQL statements. - class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :auto_increment, :primary_key, :sql_type) #:nodoc: + class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :auto_increment, :primary_key, :collation, :sql_type) #:nodoc: def primary_key? primary_key || type.to_sym == :primary_key end end + class AddColumnDefinition < Struct.new(:column) # :nodoc: + end + class ChangeColumnDefinition < Struct.new(:column, :name) #:nodoc: end + class PrimaryKeyDefinition < Struct.new(:name) # :nodoc: + end + class ForeignKeyDefinition < Struct.new(:from_table, :to_table, :options) #:nodoc: def name options[:name] @@ -122,23 +123,29 @@ module ActiveRecord end def foreign_key_options - as_options(foreign_key) + as_options(foreign_key).merge(column: column_name) end def columns - result = [["#{name}_id", type, options]] + result = [[column_name, type, options]] if polymorphic result.unshift(["#{name}_type", :string, polymorphic_options]) end result end + def column_name + "#{name}_id" + end + def column_names columns.map(&:first) end def foreign_table_name - name.to_s.pluralize + foreign_key_options.fetch(:to_table) do + Base.pluralize_table_names ? name.to_s.pluralize : name + end end end @@ -180,10 +187,10 @@ module ActiveRecord # Represents the schema of an SQL table in an abstract way. This class # provides methods for manipulating the schema representation. # - # Inside migration files, the +t+ object in +create_table+ + # Inside migration files, the +t+ object in {create_table}[rdoc-ref:SchemaStatements#create_table] # is actually of this type: # - # class SomeMigration < ActiveRecord::Migration + # class SomeMigration < ActiveRecord::Migration[5.0] # def up # create_table :foo do |t| # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition" @@ -195,27 +202,29 @@ module ActiveRecord # end # end # - # The table definitions - # The Columns are stored as a ColumnDefinition in the +columns+ attribute. class TableDefinition include ColumnMethods - # An array of ColumnDefinition objects, representing the column changes - # that have been defined. attr_accessor :indexes attr_reader :name, :temporary, :options, :as, :foreign_keys - def initialize(types, name, temporary, options, as = nil) + def initialize(name, temporary, options, as = nil) @columns_hash = {} @indexes = {} @foreign_keys = {} - @native = types + @primary_keys = nil @temporary = temporary @options = options @as = as @name = name end + def primary_keys(name = nil) # :nodoc: + @primary_keys = PrimaryKeyDefinition.new(name) if name + @primary_keys + end + + # Returns an array of ColumnDefinition objects for the columns of the table. def columns; @columns_hash.values; end # Returns a ColumnDefinition for the column with name +name+. @@ -224,90 +233,23 @@ module ActiveRecord end # Instantiates a new column for the table. - # The +type+ parameter is normally one of the migrations native types, - # which is one of the following: - # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>, - # <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>, - # <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>, - # <tt>:binary</tt>, <tt>:boolean</tt>. - # - # You may use a type not in this list as long as it is supported by your - # database (for example, "polygon" in MySQL), but this will not be database - # agnostic and should usually be avoided. - # - # Available options are (none of these exists by default): - # * <tt>:limit</tt> - - # Requests a maximum column length. This is number of characters for <tt>:string</tt> and - # <tt>:text</tt> columns and number of bytes for <tt>:binary</tt> and <tt>:integer</tt> columns. - # * <tt>:default</tt> - - # The column's default value. Use nil for NULL. - # * <tt>:null</tt> - - # Allows or disallows +NULL+ values in the column. This option could - # have been named <tt>:null_allowed</tt>. - # * <tt>:precision</tt> - - # Specifies the precision for a <tt>:decimal</tt> column. - # * <tt>:scale</tt> - - # Specifies the scale for a <tt>:decimal</tt> column. + # See {connection.add_column}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_column] + # for available options. + # + # Additional options are: # * <tt>:index</tt> - # Create an index for the column. Can be either <tt>true</tt> or an options hash. # - # Note: The precision is the total number of significant digits - # and the scale is the number of digits that can be stored following - # the decimal point. For example, the number 123.45 has a precision of 5 - # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can - # range from -999.99 to 999.99. - # - # Please be aware of different RDBMS implementations behavior with - # <tt>:decimal</tt> columns: - # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <= - # <tt>:precision</tt>, and makes no comments about the requirements of - # <tt>:precision</tt>. - # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30]. - # Default is (10,0). - # * PostgreSQL: <tt>:precision</tt> [1..infinity], - # <tt>:scale</tt> [0..infinity]. No default. - # * SQLite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used. - # Internal storage as strings. No default. - # * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>, - # but the maximum supported <tt>:precision</tt> is 16. No default. - # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127]. - # Default is (38,0). - # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62]. - # Default unknown. - # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38]. - # Default (38,0). - # # This method returns <tt>self</tt>. # # == Examples - # # Assuming +td+ is an instance of TableDefinition - # td.column(:granted, :boolean) - # # granted BOOLEAN # - # td.column(:picture, :binary, limit: 2.megabytes) - # # => picture BLOB(2097152) - # - # td.column(:sales_stage, :string, limit: 20, default: 'new', null: false) - # # => sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL - # - # td.column(:bill_gates_money, :decimal, precision: 15, scale: 2) - # # => bill_gates_money DECIMAL(15,2) - # - # td.column(:sensor_reading, :decimal, precision: 30, scale: 20) - # # => sensor_reading DECIMAL(30,20) - # - # # While <tt>:scale</tt> defaults to zero on most databases, it - # # probably wouldn't hurt to include it. - # td.column(:huge_integer, :decimal, precision: 30) - # # => huge_integer DECIMAL(30) - # - # # Defines a column with a database-specific type. - # td.column(:foo, 'polygon') - # # => foo polygon + # # Assuming +td+ is an instance of TableDefinition + # td.column(:granted, :boolean, index: true) # # == Short-hand examples # - # Instead of calling +column+ directly, you can also work with the short-hand definitions for the default types. + # Instead of calling #column directly, you can also work with the short-hand definitions for the default types. # They use the type as the method name instead of as a parameter and allow for multiple columns to be defined # in a single statement. # @@ -339,7 +281,8 @@ module ActiveRecord # TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type # column if the <tt>:polymorphic</tt> option is supplied. If <tt>:polymorphic</tt> is a hash of # options, these will be used when creating the <tt>_type</tt> column. The <tt>:index</tt> option - # will also create an index, similar to calling <tt>add_index</tt>. So what can be written like this: + # will also create an index, similar to calling {add_index}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_index]. + # So what can be written like this: # # create_table :taggings do |t| # t.integer :tag_id, :tagger_id, :taggable_id @@ -359,6 +302,7 @@ module ActiveRecord def column(name, type, options = {}) name = name.to_s type = type.to_sym + options = options.dup if @columns_hash[name] && @columns_hash[name].primary_key? raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table." @@ -370,6 +314,8 @@ module ActiveRecord self end + # remove the column +name+ from the table. + # remove_column(:account_id) def remove_column(name) @columns_hash.delete name.to_s end @@ -387,7 +333,7 @@ module ActiveRecord end # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and - # <tt>:updated_at</tt> to the table. See SchemaStatements#add_timestamps + # <tt>:updated_at</tt> to the table. See {connection.add_timestamps}[rdoc-ref:SchemaStatements#add_timestamps] # # t.timestamps null: false def timestamps(*args) @@ -399,17 +345,12 @@ module ActiveRecord column(:updated_at, :datetime, options) end - # Adds a reference. Optionally adds a +type+ column, if the - # +:polymorphic+ option is provided. +references+ and +belongs_to+ - # are interchangeable. The reference column will be an +integer+ by default, - # the +:type+ option can be used to specify a different type. A foreign - # key will be created if the +:foreign_key+ option is passed. + # Adds a reference. # # t.references(:user) - # t.references(:user, type: "string") - # t.belongs_to(:supplier, polymorphic: true) + # t.belongs_to(:supplier, foreign_key: true) # - # See SchemaStatements#add_reference + # See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference] for details of the options you can use. def references(*args, **options) args.each do |col| ReferenceDefinition.new(col, **options).add_to(self) @@ -420,11 +361,8 @@ module ActiveRecord def new_column_definition(name, type, options) # :nodoc: type = aliased_types(type.to_s, type) column = create_column_definition name, type - limit = options.fetch(:limit) do - native[type][:limit] if native[type].is_a?(Hash) - end - column.limit = limit + column.limit = options[:limit] column.precision = options[:precision] column.scale = options[:scale] column.default = options[:default] @@ -433,6 +371,7 @@ module ActiveRecord column.after = options[:after] column.auto_increment = options[:auto_increment] column.primary_key = type == :primary_key || options[:primary_key] + column.collation = options[:collation] column end @@ -441,10 +380,6 @@ module ActiveRecord ColumnDefinition.new name, type end - def native - @native - end - def aliased_types(name, fallback) 'timestamp' == name ? :datetime : fallback end @@ -475,12 +410,12 @@ module ActiveRecord def add_column(name, type, options) name = name.to_s type = type.to_sym - @adds << @td.new_column_definition(name, type, options) + @adds << AddColumnDefinition.new(@td.new_column_definition(name, type, options)) end end # Represents an SQL table in an abstract way for updating a table. - # Also see TableDefinition and SchemaStatements#create_table + # Also see TableDefinition and {connection.create_table}[rdoc-ref:SchemaStatements#create_table] # # Available transformations are: # @@ -537,7 +472,7 @@ module ActiveRecord # # t.string(:name) unless t.column_exists?(:name, :string) # - # See SchemaStatements#column_exists? + # See {connection.column_exists?}[rdoc-ref:SchemaStatements#column_exists?] def column_exists?(column_name, type = nil, options = {}) @base.column_exists?(name, column_name, type, options) end @@ -549,7 +484,7 @@ module ActiveRecord # t.index([:branch_id, :party_id], unique: true) # t.index([:branch_id, :party_id], unique: true, name: 'by_branch_party') # - # See SchemaStatements#add_index for details of the options you can use. + # See {connection.add_index}[rdoc-ref:SchemaStatements#add_index] for details of the options you can use. def index(column_name, options = {}) @base.add_index(name, column_name, options) end @@ -560,7 +495,7 @@ module ActiveRecord # t.index(:branch_id) # end # - # See SchemaStatements#index_exists? + # See {connection.index_exists?}[rdoc-ref:SchemaStatements#index_exists?] def index_exists?(column_name, options = {}) @base.index_exists?(name, column_name, options) end @@ -569,7 +504,7 @@ module ActiveRecord # # t.rename_index(:user_id, :account_id) # - # See SchemaStatements#rename_index + # See {connection.rename_index}[rdoc-ref:SchemaStatements#rename_index] def rename_index(index_name, new_index_name) @base.rename_index(name, index_name, new_index_name) end @@ -578,7 +513,7 @@ module ActiveRecord # # t.timestamps(null: false) # - # See SchemaStatements#add_timestamps + # See {connection.add_timestamps}[rdoc-ref:SchemaStatements#add_timestamps] def timestamps(options = {}) @base.add_timestamps(name, options) end @@ -597,10 +532,11 @@ module ActiveRecord # # t.change_default(:qualification, 'new') # t.change_default(:authorized, 1) + # t.change_default(:status, from: nil, to: "draft") # - # See SchemaStatements#change_column_default - def change_default(column_name, default) - @base.change_column_default(name, column_name, default) + # See {connection.change_column_default}[rdoc-ref:SchemaStatements#change_column_default] + def change_default(column_name, default_or_changes) + @base.change_column_default(name, column_name, default_or_changes) end # Removes the column(s) from the table definition. @@ -608,7 +544,7 @@ module ActiveRecord # t.remove(:qualification) # t.remove(:qualification, :experience) # - # See SchemaStatements#remove_columns + # See {connection.remove_columns}[rdoc-ref:SchemaStatements#remove_columns] def remove(*column_names) @base.remove_columns(name, *column_names) end @@ -619,7 +555,7 @@ module ActiveRecord # t.remove_index(column: [:branch_id, :party_id]) # t.remove_index(name: :by_branch_party) # - # See SchemaStatements#remove_index + # See {connection.remove_index}[rdoc-ref:SchemaStatements#remove_index] def remove_index(options = {}) @base.remove_index(name, options) end @@ -628,7 +564,7 @@ module ActiveRecord # # t.remove_timestamps # - # See SchemaStatements#remove_timestamps + # See {connection.remove_timestamps}[rdoc-ref:SchemaStatements#remove_timestamps] def remove_timestamps(options = {}) @base.remove_timestamps(name, options) end @@ -637,20 +573,17 @@ module ActiveRecord # # t.rename(:description, :name) # - # See SchemaStatements#rename_column + # See {connection.rename_column}[rdoc-ref:SchemaStatements#rename_column] def rename(column_name, new_column_name) @base.rename_column(name, column_name, new_column_name) end - # Adds a reference. Optionally adds a +type+ column, if - # <tt>:polymorphic</tt> option is provided. + # Adds a reference. # # t.references(:user) - # t.references(:user, type: "string") - # t.belongs_to(:supplier, polymorphic: true) # t.belongs_to(:supplier, foreign_key: true) # - # See SchemaStatements#add_reference + # See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference] for details of the options you can use. def references(*args) options = args.extract_options! args.each do |ref_name| @@ -664,7 +597,7 @@ module ActiveRecord # t.remove_references(:user) # t.remove_belongs_to(:supplier, polymorphic: true) # - # See SchemaStatements#remove_reference + # See {connection.remove_reference}[rdoc-ref:SchemaStatements#remove_reference] def remove_references(*args) options = args.extract_options! args.each do |ref_name| @@ -677,7 +610,7 @@ module ActiveRecord # # t.foreign_key(:authors) # - # See SchemaStatements#add_foreign_key + # See {connection.add_foreign_key}[rdoc-ref:SchemaStatements#add_foreign_key] def foreign_key(*args) # :nodoc: @base.add_foreign_key(name, *args) end @@ -686,15 +619,10 @@ module ActiveRecord # # t.foreign_key(:authors) unless t.foreign_key_exists?(:authors) # - # See SchemaStatements#foreign_key_exists? + # See {connection.foreign_key_exists?}[rdoc-ref:SchemaStatements#foreign_key_exists?] def foreign_key_exists?(*args) # :nodoc: @base.foreign_key_exists?(name, *args) end - - private - def native - @base.native_database_types - end end end end |