diff options
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters')
6 files changed, 300 insertions, 18 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb index d950181566..34627dfaf9 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb @@ -175,7 +175,7 @@ module ActiveRecord end # Establishes the connection to the database. Accepts a hash as input where - # the :adapter key must be specified with the name of a database adapter (in lower-case) + # the <tt>:adapter</tt> key must be specified with the name of a database adapter (in lower-case) # example for regular databases (MySQL, Postgresql, etc): # # ActiveRecord::Base.establish_connection( @@ -194,6 +194,7 @@ module ActiveRecord # ) # # Also accepts keys as strings (for parsing from yaml for example): + # # ActiveRecord::Base.establish_connection( # "adapter" => "sqlite", # "database" => "path/to/dbfile" 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 d30f9b4d32..d73ffc3da6 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -398,8 +398,8 @@ module ActiveRecord # TableDefinition#timestamps that'll add created_at and updated_at as datetimes. # # TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type - # column if the :polymorphic option is supplied. If :polymorphic is a hash of options, these will be - # used when creating the _type column. So what can be written like this: + # 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. So what can be written like this: # # create_table :taggings do |t| # t.integer :tag_id, :tagger_id, :taggable_id @@ -469,5 +469,195 @@ module ActiveRecord @base.native_database_types end end + + # Represents a SQL table in an abstract way for updating a table. + # Also see TableDefinition and SchemaStatements#create_table + # + # Available transformations are: + # + # change_table :table do |t| + # t.column + # t.index + # t.timestamps + # t.change + # t.change_default + # t.rename + # t.references + # t.belongs_to + # t.string + # t.text + # t.integer + # t.float + # t.decimal + # t.datetime + # t.timestamp + # t.time + # t.date + # t.binary + # t.boolean + # t.remove + # t.remove_references + # t.remove_belongs_to + # t.remove_index + # t.remove_timestamps + # end + # + class Table + def initialize(table_name, base) + @table_name = table_name + @base = base + end + + # Adds a new column to the named table. + # See TableDefinition#column for details of the options you can use. + # ===== Examples + # ====== Creating a simple columns + # t.column(:name, :string) + def column(column_name, type, options = {}) + @base.add_column(@table_name, column_name, type, options) + end + + # Adds a new index to the table. +column_name+ can be a single Symbol, or + # an Array of Symbols. See SchemaStatements#add_index + # + # ===== Examples + # ====== Creating a simple index + # t.index(:name) + # ====== Creating a unique index + # t.index([:branch_id, :party_id], :unique => true) + # ====== Creating a named index + # t.index([:branch_id, :party_id], :unique => true, :name => 'by_branch_party') + def index(column_name, options = {}) + @base.add_index(@table_name, column_name, options) + end + + # Adds timestamps (created_at and updated_at) columns to the table. See SchemaStatements#timestamps + # ===== Examples + # t.timestamps + def timestamps + @base.add_timestamps(@table_name) + end + + # Changes the column's definition according to the new options. + # See TableDefinition#column for details of the options you can use. + # ===== Examples + # t.change(:name, :string, :limit => 80) + # t.change(:description, :text) + def change(column_name, type, options = {}) + @base.change_column(@table_name, column_name, type, options) + end + + # Sets a new default value for a column. See + # ===== Examples + # t.change_default(:qualification, 'new') + # t.change_default(:authorized, 1) + def change_default(column_name, default) + @base.change_column_default(@table_name, column_name, default) + end + + # Removes the column(s) from the table definition. + # ===== Examples + # t.remove(:qualification) + # t.remove(:qualification, :experience) + # t.removes(:qualification, :experience) + def remove(*column_names) + @base.remove_column(@table_name, column_names) + end + + # Remove the given index from the table. + # + # Remove the suppliers_name_index in the suppliers table. + # t.remove_index :name + # Remove the index named accounts_branch_id_index in the accounts table. + # t.remove_index :column => :branch_id + # Remove the index named accounts_branch_id_party_id_index in the accounts table. + # t.remove_index :column => [:branch_id, :party_id] + # Remove the index named by_branch_party in the accounts table. + # t.remove_index :name => :by_branch_party + def remove_index(options = {}) + @base.remove_index(@table_name, options) + end + + # Removes the timestamp columns (created_at and updated_at) from the table. + # ===== Examples + # t.remove_timestamps + def remove_timestamps + @base.remove_timestamps(@table_name) + end + + # Renames a column. + # ===== Example + # t.rename(:description, :name) + def rename(column_name, new_column_name) + @base.rename_column(@table_name, column_name, new_column_name) + end + + # Adds a reference. Optionally adds a +type+ column. <tt>reference</tt>, + # <tt>references</tt> and <tt>belongs_to</tt> are all acceptable + # ===== Example + # t.references(:goat) + # t.references(:goat, :polymorphic => true) + # t.references(:goat) + # t.belongs_to(:goat) + def references(*args) + options = args.extract_options! + polymorphic = options.delete(:polymorphic) + args.each do |col| + @base.add_column(@table_name, "#{col}_id", :integer, options) + @base.add_column(@table_name, "#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil? + end + end + alias :belongs_to :references + + # Adds a reference. Optionally removes a +type+ column. <tt>remove_reference</tt>, + # <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are all acceptable + # ===== Example + # t.remove_reference(:goat) + # t.remove_reference(:goat, :polymorphic => true) + # t.remove_references(:goat) + # t.remove_belongs_to(:goat) + def remove_references(*args) + options = args.extract_options! + polymorphic = options.delete(:polymorphic) + args.each do |col| + @base.remove_column(@table_name, "#{col}_id") + @base.remove_column(@table_name, "#{col}_type") unless polymorphic.nil? + end + end + alias :remove_belongs_to :remove_references + + # Adds a column or columns of a specified type + # ===== Example + # t.string(:goat) + # t.string(:goat, :sheep) + %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type| + class_eval <<-EOV + def #{column_type}(*args) + options = args.extract_options! + column_names = args + + column_names.each do |name| + column = ColumnDefinition.new(@base, name, '#{column_type}') + if options[:limit] + column.limit = options[:limit] + elsif native['#{column_type}'.to_sym].is_a?(Hash) + column.limit = native['#{column_type}'.to_sym][:limit] + end + column.precision = options[:precision] + column.scale = options[:scale] + column.default = options[:default] + column.null = options[:null] + @base.add_column(@table_name, name, column.sql_type, options) + end + end + EOV + end + + private + def native + @base.native_database_types + end + end + end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index c986f0c6f1..6aae556d67 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -45,7 +45,7 @@ module ActiveRecord # The +options+ hash can include the following keys: # [<tt>:id</tt>] # Whether to automatically add a primary key column. Defaults to true. - # Join tables for has_and_belongs_to_many should set :id => false. + # Join tables for +has_and_belongs_to_many+ should set <tt>:id => false</tt>. # [<tt>:primary_key</tt>] # The name of the primary key, if one is to be added automatically. # Defaults to +id+. @@ -104,6 +104,67 @@ module ActiveRecord execute create_sql end + # A block for changing columns in +table+. + # + # === Example + # # change_table() yields a Table instance + # change_table(:suppliers) do |t| + # t.column :name, :string, :limit => 60 + # # Other column alterations here + # end + # + # ===== Examples + # ====== Add a column + # change_table(:suppliers) do |t| + # t.column :name, :string, :limit => 60 + # end + # + # ====== Add 2 integer columns + # change_table(:suppliers) do |t| + # t.integer :width, :height, :null => false, :default => 0 + # end + # + # ====== Add created_at/updated_at columns + # change_table(:suppliers) do |t| + # t.timestamps + # end + # + # ====== Add a foreign key column + # change_table(:suppliers) do |t| + # t.references :company + # end + # + # Creates a <tt>company_id(integer)</tt> column + # + # ====== Add a polymorphic foreign key column + # change_table(:suppliers) do |t| + # t.belongs_to :company, :polymorphic => true + # end + # + # Creates <tt>company_type(varchar)</tt> and <tt>company_id(integer)</tt> columns + # + # ====== Remove a column + # change_table(:suppliers) do |t| + # t.remove :company + # end + # + # ====== Remove a column + # change_table(:suppliers) do |t| + # t.remove :company_id + # t.remove :width, :height + # end + # + # ====== Remove an index + # change_table(:suppliers) do |t| + # t.remove_index :company_id + # end + # + # See also Table for details on + # all of the various column transformation + def change_table(table_name) + yield Table.new(table_name, self) + end + # Renames a table. # ===== Example # rename_table('octopuses', 'octopi') @@ -124,13 +185,17 @@ module ActiveRecord execute(add_column_sql) end - # Removes the column from the table definition. + # Removes the column(s) from the table definition. # ===== Examples # remove_column(:suppliers, :qualification) - def remove_column(table_name, column_name) - execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}" + # remove_columns(:suppliers, :qualification, :experience) + def remove_column(table_name, *column_names) + column_names.flatten.each do |column_name| + execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}" + end end - + alias :remove_columns :remove_column + # Changes the column's definition according to the new options. # See TableDefinition#column for details of the options you can use. # ===== Examples @@ -297,7 +362,14 @@ module ActiveRecord def add_column_options!(sql, options) #:nodoc: sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options) - sql << " NOT NULL" if options[:null] == false + # must explcitly check for :null to allow change_column to work on migrations + if options.has_key? :null + if options[:null] == false + sql << " NOT NULL" + else + sql << " NULL" + end + end end # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause. diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 6432c3cfee..e742d60c5f 100755 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -196,7 +196,7 @@ module ActiveRecord :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY", :string => { :name => "varchar", :limit => 255 }, :text => { :name => "text" }, - :integer => { :name => "int", :limit => 11 }, + :integer => { :name => "int"}, :float => { :name => "float" }, :decimal => { :name => "decimal" }, :datetime => { :name => "datetime" }, @@ -365,7 +365,7 @@ module ActiveRecord create_database(name) end - # Create a new MySQL database with optional :charset and :collation. + # Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>. # Charset defaults to utf8. # # Example: @@ -463,6 +463,22 @@ module ActiveRecord execute "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}" end + # Maps logical Rails types to MySQL-specific data types. + def type_to_sql(type, limit = nil, precision = nil, scale = nil) + return super unless type.to_s == 'integer' + + case limit + when 0..3 + "smallint(#{limit})" + when 4..8 + "int(#{limit})" + when 9..20 + "bigint(#{limit})" + else + 'int(11)' + end + end + # SHOW VARIABLES LIKE 'name' def show_variable(name) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 54b50fabd8..e3f7969cdf 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -233,7 +233,7 @@ module ActiveRecord # * <tt>:username</tt> -- Defaults to nothing # * <tt>:password</tt> -- Defaults to nothing # * <tt>:database</tt> -- The name of the database. No default, must be provided. - # * <tt>:schema_search_path</tt> -- An optional schema search path for the connection given as a string of comma-separated schema names. This is backward-compatible with the :schema_order option. + # * <tt>:schema_search_path</tt> -- An optional schema search path for the connection given as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option. # * <tt>:encoding</tt> -- An optional client encoding that is used in a SET client_encoding TO <encoding> call on the connection. # * <tt>:min_messages</tt> -- An optional client min messages that is used in a SET client_min_messages TO <min_messages> call on the connection. # * <tt>:allow_concurrency</tt> -- If true, use async query methods so Ruby threads don't deadlock; otherwise, use blocking query methods. @@ -479,9 +479,9 @@ module ActiveRecord create_database(name) end - # Create a new PostgreSQL database. Options include :owner, :template, - # :encoding, :tablespace, and :connection_limit (note that MySQL uses - # :charset while PostgreSQL uses :encoding). + # Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>, + # <tt>:encoding</tt>, <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses + # <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>). # # Example: # create_database config[:database], config diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 59a51c0279..8fa62c1845 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -219,11 +219,14 @@ module ActiveRecord execute "VACUUM" end - def remove_column(table_name, column_name) #:nodoc: - alter_table(table_name) do |definition| - definition.columns.delete(definition[column_name]) + def remove_column(table_name, *column_names) #:nodoc: + column_names.flatten.each do |column_name| + alter_table(table_name) do |definition| + definition.columns.delete(definition[column_name]) + end end end + alias :remove_columns :remove_column def change_column_default(table_name, column_name, default) #:nodoc: alter_table(table_name) do |definition| |