1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Add support for specifying comments for tables, columns, and indexes.

Comments are specified in migrations, stored in database itself (in its schema),
and dumped into db/schema.rb file.

This allows to generate good documentation and explain columns and tables' purpose
to everyone from new developers to database administrators.

For PostgreSQL and MySQL only. SQLite does not support comments at the moment.

See docs for PostgreSQL: http://www.postgresql.org/docs/current/static/sql-comment.html
See docs for MySQL: http://dev.mysql.com/doc/refman/5.7/en/create-table.html
This commit is contained in:
Andrey Novikov 2016-01-03 21:13:53 +03:00
parent 39e087cbf5
commit c690b9ce39
No known key found for this signature in database
GPG key ID: 301AD540051205A2
15 changed files with 286 additions and 37 deletions

View file

@ -1,3 +1,20 @@
* Add support for database schema comments for tables, columns and indexes
for PostgreSQL and MySQL.
It allows to specify commentaries for database objects in migrations and
store them in database itself, allowing to see them with DBA tools and
in `db/schema.rb` file and thus automatically documents database schema:
create_table "pages", force: :cascade, comment: 'Arbitrary content pages' do |t|
# ...
t.string "path", comment: "Path fragment of page URL used for routing"
t.string "locale", comment: "RFC 3066 locale code of website language section"
t.index ["locale", "path"], name: 'page_uri_index' comment: "Main index used to lookup page by it's URI."
# ...
end
*Andrey Novikov*
* Add `quoted_time` for truncating the date part of a TIME column value.
This fixes queries on TIME column on MariaDB, as it doesn't ignore the
date part of the string when it coerces to time.

View file

@ -54,6 +54,7 @@ module ActiveRecord
end
create_sql << "(#{statements.join(', ')}) " if statements.present?
add_table_options!(create_sql, table_options(o))
create_sql << "#{o.options}"
create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
create_sql
@ -82,6 +83,16 @@ module ActiveRecord
"DROP CONSTRAINT #{quote_column_name(name)}"
end
def table_options(o)
options = {}
options[:comment] = o.comment
options
end
def add_table_options!(sql, _options)
sql
end
def column_options(o)
column_options = {}
column_options[:null] = o.null unless o.null.nil?
@ -92,6 +103,7 @@ module ActiveRecord
column_options[:auto_increment] = o.auto_increment
column_options[:primary_key] = o.primary_key
column_options[:collation] = o.collation
column_options[:comment] = o.comment
column_options
end

View file

@ -3,14 +3,14 @@ module ActiveRecord
# Abstract representation of an index definition on a table. Instances of
# this type are typically created and returned by methods in database
# adapters. e.g. ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#indexes
class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using) #:nodoc:
class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using, :comment) #:nodoc:
end
# Abstract representation of a column definition. Instances of this type
# 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, :collation, :sql_type) #:nodoc:
class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :auto_increment, :primary_key, :collation, :sql_type, :comment) #:nodoc:
def primary_key?
primary_key || type.to_sym == :primary_key
@ -207,9 +207,9 @@ module ActiveRecord
include ColumnMethods
attr_accessor :indexes
attr_reader :name, :temporary, :options, :as, :foreign_keys
attr_reader :name, :temporary, :options, :as, :foreign_keys, :comment
def initialize(name, temporary, options, as = nil)
def initialize(name, temporary, options, as = nil, comment = nil)
@columns_hash = {}
@indexes = {}
@foreign_keys = []
@ -218,6 +218,7 @@ module ActiveRecord
@options = options
@as = as
@name = name
@comment = comment
end
def primary_keys(name = nil) # :nodoc:
@ -373,6 +374,7 @@ module ActiveRecord
column.auto_increment = options[:auto_increment]
column.primary_key = type == :primary_key || options[:primary_key]
column.collation = options[:collation]
column.comment = options[:comment]
column
end

View file

@ -46,12 +46,14 @@ module ActiveRecord
spec[:collation] = collation
end
spec[:comment] = column.comment.inspect if column.comment
spec
end
# Lists the valid migration options
def migration_keys
[:name, :limit, :precision, :scale, :default, :null, :collation]
[:name, :limit, :precision, :scale, :default, :null, :collation, :comment]
end
private

View file

@ -18,6 +18,11 @@ module ActiveRecord
nil
end
# Returns comment associated with given table in database
def table_comment(table_name)
nil
end
# Truncates a table alias according to the limits of the current adapter.
def table_alias_for(table_name)
table_name[0...table_alias_length].tr('.', '_')
@ -255,7 +260,7 @@ module ActiveRecord
#
# See also TableDefinition#column for details on how to create columns.
def create_table(table_name, options = {})
td = create_table_definition table_name, options[:temporary], options[:options], options[:as]
td = create_table_definition table_name, options[:temporary], options[:options], options[:as], options[:comment]
if options[:id] != false && !options[:as]
pk = options.fetch(:primary_key) do
@ -265,7 +270,7 @@ module ActiveRecord
if pk.is_a?(Array)
td.primary_keys pk
else
td.primary_key pk, options.fetch(:id, :primary_key), options
td.primary_key pk, options.fetch(:id, :primary_key), options.except(:comment)
end
end
@ -283,6 +288,13 @@ module ActiveRecord
end
end
if supports_comments? && !supports_comments_in_create?
change_table_comment(table_name, options[:comment]) if options[:comment]
td.columns.each do |column|
change_column_comment(table_name, column.name, column.comment) if column.comment
end
end
result
end
@ -1078,7 +1090,7 @@ module ActiveRecord
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)
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type, :comment)
index_type = options[:type].to_s if options.key?(:type)
index_type ||= options[:unique] ? "UNIQUE" : ""
@ -1106,13 +1118,25 @@ module ActiveRecord
end
index_columns = quoted_columns_for_index(column_names, options).join(", ")
[index_name, index_type, index_columns, index_options, algorithm, using]
comment = options[:comment] if options.key?(:comment)
[index_name, index_type, index_columns, index_options, algorithm, using, comment]
end
def options_include_default?(options)
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
end
# Adds comment for given table or drops it if +nil+ given
def change_table_comment(table_name, comment)
raise NotImplementedError, "change_table_comment is not implemented"
end
# Adds comment for given table column or drops it if +nil+ given
def change_column_comment(table_name, column_name, comment) #:nodoc:
raise NotImplementedError, "change_column_comment is not implemented"
end
protected
def add_index_sort_order(option_strings, column_names, options = {})
if options.is_a?(Hash) && order = options[:order]
@ -1194,8 +1218,8 @@ module ActiveRecord
end
private
def create_table_definition(name, temporary = false, options = nil, as = nil)
TableDefinition.new(name, temporary, options, as)
def create_table_definition(name, temporary = false, options = nil, as = nil, comment = nil)
TableDefinition.new(name, temporary, options, as, comment)
end
def create_alter_table(name)

View file

@ -278,6 +278,16 @@ module ActiveRecord
false
end
# Does adapter supports comments on database objects (tables, columns, indexes)?
def supports_comments?
false
end
# Can comments for tables, columns, and indexes be specified in create/alter table statements?
def supports_comments_in_create?
false
end
# This is meant to be implemented by the adapters that support extensions
def disable_extension(name)
end

View file

@ -160,8 +160,8 @@ module ActiveRecord
raise NotImplementedError
end
def new_column(field, default, sql_type_metadata, null, table_name, default_function = nil, collation = nil) # :nodoc:
MySQL::Column.new(field, default, sql_type_metadata, null, table_name, default_function, collation)
def new_column(field, default, sql_type_metadata, null, table_name, default_function = nil, collation = nil, comment = nil) # :nodoc:
MySQL::Column.new(field, default, sql_type_metadata, null, table_name, default_function, collation, comment)
end
# Must return the MySQL error number from the exception, if the exception has an
@ -373,7 +373,7 @@ module ActiveRecord
mysql_index_type = row[:Index_type].downcase.to_sym
index_type = INDEX_TYPES.include?(mysql_index_type) ? mysql_index_type : nil
index_using = INDEX_USINGS.include?(mysql_index_type) ? mysql_index_type : nil
indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [], nil, nil, index_type, index_using)
indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [], nil, nil, index_type, index_using, row[:Index_comment].presence)
end
indexes.last.columns << row[:Column_name]
@ -394,10 +394,18 @@ module ActiveRecord
else
default, default_function = field[:Default], nil
end
new_column(field[:Field], default, type_metadata, field[:Null] == "YES", table_name, default_function, field[:Collation])
new_column(field[:Field], default, type_metadata, field[:Null] == "YES", table_name, default_function, field[:Collation], field[:Comment].presence)
end
end
def table_comment(table_name)
select_value(<<-SQL.strip_heredoc, 'SCHEMA')
SELECT table_comment
FROM INFORMATION_SCHEMA.TABLES
WHERE table_name=#{quote(table_name)};
SQL
end
def create_table(table_name, options = {}) #:nodoc:
super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB"))
end
@ -482,8 +490,10 @@ module ActiveRecord
end
def add_index(table_name, column_name, options = {}) #:nodoc:
index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, options)
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}"
index_name, index_type, index_columns, _, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
sql = "CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}"
sql << " COMMENT #{quote(comment)}" if comment
execute sql
end
def foreign_keys(table_name)
@ -521,7 +531,12 @@ module ActiveRecord
raw_table_options = create_table_info.sub(/\A.*\n\) /m, '').sub(/\n\/\*!.*\*\/\n\z/m, '').strip
# strip AUTO_INCREMENT
raw_table_options.sub(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
# strip COMMENT
raw_table_options.sub!(/ COMMENT='.+'/, '')
raw_table_options
end
# Maps logical Rails types to MySQL-specific data types.
@ -866,8 +881,8 @@ module ActiveRecord
create_table_info_cache[table_name] ||= select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
end
def create_table_definition(name, temporary = false, options = nil, as = nil) # :nodoc:
MySQL::TableDefinition.new(name, temporary, options, as)
def create_table_definition(name, temporary = false, options = nil, as = nil, comment = nil) # :nodoc:
MySQL::TableDefinition.new(name, temporary, options, as, comment)
end
def integer_to_sql(limit) # :nodoc:

View file

@ -5,7 +5,7 @@ module ActiveRecord
module ConnectionAdapters
# An abstract definition of a column in a table.
class Column
attr_reader :name, :default, :sql_type_metadata, :null, :table_name, :default_function, :collation
attr_reader :name, :default, :sql_type_metadata, :null, :table_name, :default_function, :collation, :comment
delegate :precision, :scale, :limit, :type, :sql_type, to: :sql_type_metadata, allow_nil: true
@ -15,7 +15,7 @@ module ActiveRecord
# +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
# +sql_type_metadata+ is various information about the type of the column
# +null+ determines if this column allows +NULL+ values.
def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil)
def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment = nil)
@name = name.freeze
@table_name = table_name
@sql_type_metadata = sql_type_metadata
@ -23,6 +23,7 @@ module ActiveRecord
@default = default
@default_function = default_function
@collation = collation
@comment = comment
end
def has_default?

View file

@ -2,6 +2,9 @@ module ActiveRecord
module ConnectionAdapters
module MySQL
class SchemaCreation < AbstractAdapter::SchemaCreation
delegate :quote, to: :@conn
private :quote
private
def visit_DropForeignKey(name)
@ -22,6 +25,13 @@ module ActiveRecord
add_column_position!(change_column_sql, column_options(o.column))
end
def add_table_options!(sql, options)
super
if options[:comment]
sql << "COMMENT #{quote(options[:comment])} "
end
end
def column_options(o)
column_options = super
column_options[:charset] = o.charset
@ -35,7 +45,11 @@ module ActiveRecord
if options[:collation]
sql << " COLLATE #{options[:collation]}"
end
super
new_sql = super
if options[:comment]
new_sql << " COMMENT #{quote(options[:comment])}"
end
new_sql
end
def add_column_position!(sql, options)
@ -48,8 +62,9 @@ module ActiveRecord
end
def index_in_create(table_name, column_name, options)
index_name, index_type, index_columns, _, _, index_using = @conn.add_index_options(table_name, column_name, options)
"#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns}) "
index_name, index_type, index_columns, _, _, index_using, comment = @conn.add_index_options(table_name, column_name, options)
index_option = " COMMENT #{quote(comment)}" if comment
"#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_option} "
end
end
end

View file

@ -45,6 +45,14 @@ module ActiveRecord
!mariadb? && version >= '5.7.8'
end
def supports_comments?
true
end
def supports_comments_in_create?
true
end
# HELPER METHODS ===========================================
def each_hash(result) # :nodoc:

View file

@ -1,3 +1,5 @@
require 'active_support/core_ext/string/strip'
module ActiveRecord
module ConnectionAdapters
module PostgreSQL
@ -172,7 +174,8 @@ module ActiveRecord
table = Utils.extract_schema_qualified_name(table_name.to_s)
result = query(<<-SQL, 'SCHEMA')
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
pg_catalog.obj_description(i.oid, 'pg_class') AS comment
FROM pg_class t
INNER JOIN pg_index d ON t.oid = d.indrelid
INNER JOIN pg_class i ON d.indexrelid = i.oid
@ -190,6 +193,7 @@ module ActiveRecord
indkey = row[2].split(" ").map(&:to_i)
inddef = row[3]
oid = row[4]
comment = row[5]
columns = Hash[query(<<-SQL, "SCHEMA")]
SELECT a.attnum, a.attname
@ -207,7 +211,7 @@ module ActiveRecord
where = inddef.scan(/WHERE (.+)$/).flatten[0]
using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using)
IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using, comment)
end
end.compact
end
@ -215,18 +219,33 @@ module ActiveRecord
# Returns the list of all column definitions for a table.
def columns(table_name) # :nodoc:
table_name = table_name.to_s
column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod, collation|
column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod, collation, comment|
oid = oid.to_i
fmod = fmod.to_i
type_metadata = fetch_type_metadata(column_name, type, oid, fmod)
default_value = extract_value_from_default(default)
default_function = extract_default_function(default_value, default)
new_column(column_name, default_value, type_metadata, !notnull, table_name, default_function, collation)
new_column(column_name, default_value, type_metadata, !notnull, table_name, default_function, collation, comment)
end
end
def new_column(name, default, sql_type_metadata, null, table_name, default_function = nil, collation = nil) # :nodoc:
PostgreSQLColumn.new(name, default, sql_type_metadata, null, table_name, default_function, collation)
def new_column(name, default, sql_type_metadata, null, table_name, default_function = nil, collation = nil, comment = nil) # :nodoc:
PostgreSQLColumn.new(name, default, sql_type_metadata, null, table_name, default_function, collation, comment)
end
# Returns a comment stored in database for given table
def table_comment(table_name) # :nodoc:
name = Utils.extract_schema_qualified_name(table_name.to_s)
return nil unless name.identifier
select_value(<<-SQL.strip_heredoc, 'SCHEMA')
SELECT pg_catalog.obj_description(c.oid, 'pg_class')
FROM pg_catalog.pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = '#{name.identifier}'
AND c.relkind IN ('r') -- (r)elation/table
AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
SQL
end
# Returns the current database name.
@ -445,6 +464,7 @@ module ActiveRecord
def add_column(table_name, column_name, type, options = {}) #:nodoc:
clear_cache!
super
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
end
def change_column(table_name, column_name, type, options = {}) #:nodoc:
@ -466,6 +486,7 @@ module ActiveRecord
change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
end
# Changes the default value of a table column.
@ -494,6 +515,18 @@ module ActiveRecord
execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
end
# Adds comment for given table column or drops it if +comment+ is a +nil+
def change_column_comment(table_name, column_name, comment) # :nodoc:
clear_cache!
execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}"
end
# Adds comment for given table or drops it if +comment+ is a +nil+
def change_table_comment(table_name, comment) # :nodoc:
clear_cache!
execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}"
end
# Renames a column in a table.
def rename_column(table_name, column_name, new_column_name) #:nodoc:
clear_cache!
@ -502,8 +535,10 @@ module ActiveRecord
end
def add_index(table_name, column_name, options = {}) #:nodoc:
index_name, index_type, index_columns, index_options, index_algorithm, index_using = add_index_options(table_name, column_name, options)
execute "CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns})#{index_options}"
index_name, index_type, index_columns, index_options, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
result = execute "CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns})#{index_options}"
execute "COMMENT ON INDEX #{quote_column_name(index_name)} IS #{quote(comment)}" if comment
result # Result of execute is used in tests in activerecord/test/cases/adapters/postgresql/active_schema_test.rb
end
def remove_index(table_name, options = {}) #:nodoc:

View file

@ -159,6 +159,10 @@ module ActiveRecord
postgresql_version >= 90200
end
def supports_comments?
true
end
def index_algorithms
{ concurrently: 'CONCURRENTLY' }
end
@ -712,7 +716,7 @@ module ActiveRecord
# Returns the list of a table's column names, data types, and default values.
#
# The underlying query is roughly:
# SELECT column.name, column.type, default.value
# SELECT column.name, column.type, default.value, column.comment
# FROM column LEFT JOIN default
# ON column.table_id = default.table_id
# AND column.num = default.column_num
@ -732,7 +736,8 @@ module ActiveRecord
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
(SELECT c.collname FROM pg_collation c, pg_type t
WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation)
WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation),
col_description(a.attrelid, a.attnum) AS comment
FROM pg_attribute a LEFT JOIN pg_attrdef d
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
@ -746,8 +751,8 @@ module ActiveRecord
$1.strip if $1
end
def create_table_definition(name, temporary = false, options = nil, as = nil) # :nodoc:
PostgreSQL::TableDefinition.new(name, temporary, options, as)
def create_table_definition(name, temporary = false, options = nil, as = nil, comment = nil) # :nodoc:
PostgreSQL::TableDefinition.new(name, temporary, options, as, comment)
end
def can_perform_case_insensitive_comparison_for?(column)

View file

@ -138,6 +138,9 @@ HEADER
table_options = @connection.table_options(table)
tbl.print ", options: #{table_options.inspect}" unless table_options.blank?
comment = @connection.table_comment(table)
tbl.print ", comment: #{comment.inspect}" if comment
tbl.puts " do |t|"
# then dump all non-primary key columns
@ -209,6 +212,7 @@ HEADER
statement_parts << "where: #{index.where.inspect}" if index.where
statement_parts << "using: #{index.using.inspect}" if index.using
statement_parts << "type: #{index.type.inspect}" if index.type
statement_parts << "comment: #{index.comment.inspect}" if index.comment
" #{statement_parts.join(', ')}"
end

View file

@ -0,0 +1,91 @@
require 'cases/helper'
require 'support/schema_dumping_helper'
class CommentTest < ActiveRecord::TestCase
include SchemaDumpingHelper
self.use_transactional_tests = false if current_adapter?(:Mysql2Adapter)
class Commented < ActiveRecord::Base
self.table_name = 'commenteds'
end
def setup
@connection = ActiveRecord::Base.connection
@connection.transaction do
@connection.create_table('commenteds', comment: 'A table with comment', force: true) do |t|
t.string 'name', comment: 'Comment should help clarify the column purpose'
t.boolean 'obvious', comment: 'Question is: should you comment obviously named objects?'
t.string 'content'
t.index 'name', comment: %Q["Very important" index that powers all the performance.\nAnd it's fun!]
end
end
end
teardown do
@connection.drop_table 'commenteds', if_exists: true
end
if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)
def test_column_created_in_block
Commented.reset_column_information
column = Commented.columns_hash['name']
assert_equal :string, column.type
assert_equal 'Comment should help clarify the column purpose', column.comment
end
def test_add_column_with_comment_later
@connection.add_column :commenteds, :rating, :integer, comment: 'I am running out of imagination'
Commented.reset_column_information
column = Commented.columns_hash['rating']
assert_equal :integer, column.type
assert_equal 'I am running out of imagination', column.comment
end
def test_add_index_with_comment_later
@connection.add_index :commenteds, :obvious, name: 'idx_obvious', comment: 'We need to see obvious comments'
index = @connection.indexes('commenteds').find { |idef| idef.name == 'idx_obvious' }
assert_equal 'We need to see obvious comments', index.comment
end
def test_add_comment_to_column
@connection.change_column :commenteds, :content, :string, comment: 'Whoa, content describes itself!'
Commented.reset_column_information
column = Commented.columns_hash['content']
assert_equal :string, column.type
assert_equal 'Whoa, content describes itself!', column.comment
end
def test_remove_comment_from_column
@connection.change_column :commenteds, :obvious, :string, comment: nil
Commented.reset_column_information
column = Commented.columns_hash['obvious']
assert_equal :string, column.type
assert_nil column.comment
end
def test_schema_dump_with_comments
# Do all the stuff from other tests
@connection.add_column :commenteds, :rating, :integer, comment: 'I am running out of imagination'
@connection.change_column :commenteds, :content, :string, comment: 'Whoa, content describes itself!'
@connection.change_column :commenteds, :obvious, :string, comment: nil
@connection.add_index :commenteds, :obvious, name: 'idx_obvious', comment: 'We need to see obvious comments'
# And check that these changes are reflected in dump
output = dump_table_schema 'commenteds'
assert_match %r[create_table "commenteds",.+\s+comment: "A table with comment"], output
assert_match %r[t\.string\s+"name",\s+comment: "Comment should help clarify the column purpose"], output
assert_match %r[t\.string\s+"obvious"\n], output
assert_match %r[t\.string\s+"content",\s+comment: "Whoa, content describes itself!"], output
assert_match %r[t\.integer\s+"rating",\s+comment: "I am running out of imagination"], output
assert_match %r[add_index\s+.+\s+comment: "\\\"Very important\\\" index that powers all the performance.\\nAnd it's fun!"], output
assert_match %r[add_index\s+.+\s+name: "idx_obvious",.+\s+comment: "We need to see obvious comments"], output
end
end
end

View file

@ -355,6 +355,13 @@ end
will append `ENGINE=BLACKHOLE` to the SQL statement used to create the table
(when using MySQL or MariaDB, the default is `ENGINE=InnoDB`).
Also you can pass the `:comment` option with any description for the table
that will be stored in database itself and can be viewed with database administration
tools, such as MySQL Workbench or PgAdmin III. It's highly recommended to specify
comments in migrations for applications with large databases as it helps people
to understand data model and generate documentation.
Currently only MySQL and PostgreSQL supports comments.
### Creating a Join Table
The migration method `create_join_table` creates an HABTM (has and belongs to
@ -454,6 +461,7 @@ number of digits after the decimal point.
are using a dynamic value (such as a date), the default will only be calculated
the first time (i.e. on the date the migration is applied).
* `index` Adds an index for the column.
* `comment` Adds a comment for the column.
Some adapters may support additional options; see the adapter specific API docs
for further information.