add table & column comment for postgres(add table comment for mysql) (#2067)

让 postgres 支持字段注释,只在 v1.2.5 上测试过(不知道怎么 import master)

发现 master 分支好像大改了?模式表名带 schema 了

使用方式和 mysql 相同
```go
	type User struct {
		Id int64	`xorm:"pk autoincr"`
		Name string `json:"name" xorm:"not null default '' varchar(50) index(name_age) comment('用户 (it''s) 1; 名')"`
		Salt string
		Age int		`json:"age" xorm:"not null default 0 int(10) index(name_age) comment('年龄')"`
		Passwd string `xorm:"varchar(200)"`
		CreatedAt time.Time `xorm:"created"`
		UpdatedAt time.Time `xorm:"updated"`
	}

	_ = engine.Sync(new(User))

    func (model User) TableComment() string {
    	return "表注释"
    }
```

Co-authored-by: fanybook <fanybook@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/xorm/xorm/pulls/2067
Co-authored-by: fanybook <fanybook@noreply.gitea.io>
Co-committed-by: fanybook <fanybook@noreply.gitea.io>
This commit is contained in:
fanybook 2021-11-12 20:58:05 +08:00 committed by Lunny Xiao
parent a22f5dce83
commit aea91cc7de
7 changed files with 148 additions and 8 deletions

View File

@ -206,7 +206,7 @@ func (db *Base) IsColumnExist(queryer core.Queryer, ctx context.Context, tableNa
// AddColumnSQL returns a SQL to add a column // AddColumnSQL returns a SQL to add a column
func (db *Base) AddColumnSQL(tableName string, col *schemas.Column) string { func (db *Base) AddColumnSQL(tableName string, col *schemas.Column) string {
s, _ := ColumnString(db.dialect, col, true) s, _ := ColumnString(db.dialect, col, true)
return fmt.Sprintf("ALTER TABLE %v ADD %v", db.dialect.Quoter().Quote(tableName), s) return fmt.Sprintf("ALTER TABLE %s ADD %s", db.dialect.Quoter().Quote(tableName), s)
} }
// CreateIndexSQL returns a SQL to create index // CreateIndexSQL returns a SQL to create index

View File

@ -685,6 +685,12 @@ func (db *mysql) CreateTableSQL(ctx context.Context, queryer core.Queryer, table
b.WriteString(db.rowFormat) b.WriteString(db.rowFormat)
} }
if table.Comment != "" {
b.WriteString(" COMMENT='")
b.WriteString(table.Comment)
b.WriteString("'")
}
return b.String(), true, nil return b.String(), true, nil
} }

View File

@ -991,13 +991,37 @@ func (db *postgres) IsTableExist(queryer core.Queryer, ctx context.Context, tabl
db.getSchema(), tableName) db.getSchema(), tableName)
} }
func (db *postgres) ModifyColumnSQL(tableName string, col *schemas.Column) string { func (db *postgres) AddColumnSQL(tableName string, col *schemas.Column) string {
s, _ := ColumnString(db.dialect, col, true)
quoter := db.dialect.Quoter()
addColumnSQL := ""
commentSQL := "; "
if len(db.getSchema()) == 0 || strings.Contains(tableName, ".") { if len(db.getSchema()) == 0 || strings.Contains(tableName, ".") {
return fmt.Sprintf("alter table %s ALTER COLUMN %s TYPE %s", addColumnSQL = fmt.Sprintf("ALTER TABLE %s ADD %s", quoter.Quote(tableName), s)
db.quoter.Quote(tableName), db.quoter.Quote(col.Name), db.SQLType(col)) commentSQL += fmt.Sprintf("COMMENT ON COLUMN %s.%s IS '%s'", quoter.Quote(tableName), quoter.Quote(col.Name), col.Comment)
return addColumnSQL + commentSQL
} }
return fmt.Sprintf("alter table %s.%s ALTER COLUMN %s TYPE %s",
db.quoter.Quote(db.getSchema()), db.quoter.Quote(tableName), db.quoter.Quote(col.Name), db.SQLType(col)) addColumnSQL = fmt.Sprintf("ALTER TABLE %s.%s ADD %s", quoter.Quote(db.getSchema()), quoter.Quote(tableName), s)
commentSQL += fmt.Sprintf("COMMENT ON COLUMN %s.%s.%s IS '%s'", quoter.Quote(db.getSchema()), quoter.Quote(tableName), quoter.Quote(col.Name), col.Comment)
return addColumnSQL + commentSQL
}
func (db *postgres) ModifyColumnSQL(tableName string, col *schemas.Column) string {
quoter := db.dialect.Quoter()
modifyColumnSQL := ""
commentSQL := "; "
if len(db.getSchema()) == 0 || strings.Contains(tableName, ".") {
modifyColumnSQL = fmt.Sprintf("ALTER TABLE %s ALTER COLUMN %s TYPE %s", quoter.Quote(tableName), quoter.Quote(col.Name), db.SQLType(col))
commentSQL += fmt.Sprintf("COMMENT ON COLUMN %s.%s IS '%s'", quoter.Quote(tableName), quoter.Quote(col.Name), col.Comment)
return modifyColumnSQL + commentSQL
}
modifyColumnSQL = fmt.Sprintf("ALTER TABLE %s.%s ALTER COLUMN %s TYPE %s", quoter.Quote(db.getSchema()), quoter.Quote(tableName), quoter.Quote(col.Name), db.SQLType(col))
commentSQL += fmt.Sprintf("COMMENT ON COLUMN %s.%s.%s IS '%s'", quoter.Quote(db.getSchema()), quoter.Quote(tableName), quoter.Quote(col.Name), col.Comment)
return modifyColumnSQL + commentSQL
} }
func (db *postgres) DropIndexSQL(tableName string, index *schemas.Index) string { func (db *postgres) DropIndexSQL(tableName string, index *schemas.Index) string {
@ -1302,6 +1326,26 @@ func (db *postgres) GetIndexes(queryer core.Queryer, ctx context.Context, tableN
return indexes, nil return indexes, nil
} }
func (db *postgres) CreateTableSQL(ctx context.Context, queryer core.Queryer, table *schemas.Table, tableName string) (string, bool, error) {
quoter := db.dialect.Quoter()
if len(db.getSchema()) != 0 && !strings.Contains(tableName, ".") {
tableName = fmt.Sprintf("%s.%s", db.getSchema(), tableName)
}
createTableSQL, ok, err := db.Base.CreateTableSQL(ctx, queryer, table, tableName)
if err != nil {
return "", ok, err
}
commentSql := "; "
if table.Comment != "" {
// support schema.table -> "schema"."table"
commentSql += fmt.Sprintf("COMMENT ON TABLE %s IS '%s'", quoter.Quote(tableName), table.Comment)
}
return createTableSQL + commentSql, true, nil
}
func (db *postgres) Filters() []Filter { func (db *postgres) Filters() []Filter {
return []Filter{&SeqFilter{Prefix: "$", Start: 1}} return []Filter{&SeqFilter{Prefix: "$", Start: 1}}
} }

View File

@ -14,9 +14,15 @@ type TableName interface {
TableName() string TableName() string
} }
type TableComment interface {
TableComment() string
}
var ( var (
tpTableName = reflect.TypeOf((*TableName)(nil)).Elem() tpTableName = reflect.TypeOf((*TableName)(nil)).Elem()
tpTableComment = reflect.TypeOf((*TableComment)(nil)).Elem()
tvCache sync.Map tvCache sync.Map
tcCache sync.Map
) )
// GetTableName returns table name // GetTableName returns table name
@ -55,3 +61,40 @@ func GetTableName(mapper Mapper, v reflect.Value) string {
return mapper.Obj2Table(v.Type().Name()) return mapper.Obj2Table(v.Type().Name())
} }
// GetTableComment returns table comment
func GetTableComment(v reflect.Value) string {
if v.Type().Implements(tpTableComment) {
return v.Interface().(TableComment).TableComment()
}
if v.Kind() == reflect.Ptr {
v = v.Elem()
if v.Type().Implements(tpTableComment) {
return v.Interface().(TableComment).TableComment()
}
} else if v.CanAddr() {
v1 := v.Addr()
if v1.Type().Implements(tpTableComment) {
return v1.Interface().(TableComment).TableComment()
}
} else {
comment, ok := tcCache.Load(v.Type())
if ok {
if comment.(string) != "" {
return comment.(string)
}
} else {
v2 := reflect.New(v.Type())
if v2.Type().Implements(tpTableComment) {
tableComment := v2.Interface().(TableComment).TableComment()
tcCache.Store(v.Type(), tableComment)
return tableComment
}
tcCache.Store(v.Type(), "")
}
}
return ""
}

View File

@ -394,6 +394,8 @@ func (session *Session) Sync(beans ...interface{}) error {
_, err = session.exec(engine.dialect.ModifyColumnSQL(tbNameWithSchema, col)) _, err = session.exec(engine.dialect.ModifyColumnSQL(tbNameWithSchema, col))
} }
} }
} else if col.Comment != oriCol.Comment {
_, err = session.exec(engine.dialect.ModifyColumnSQL(tbNameWithSchema, col))
} }
if col.Default != oriCol.Default { if col.Default != oriCol.Default {

View File

@ -316,6 +316,7 @@ func (parser *Parser) Parse(v reflect.Value) (*schemas.Table, error) {
table := schemas.NewEmptyTable() table := schemas.NewEmptyTable()
table.Type = t table.Type = t
table.Name = names.GetTableName(parser.tableMapper, v) table.Name = names.GetTableName(parser.tableMapper, v)
table.Comment = names.GetTableComment(v)
for i := 0; i < t.NumField(); i++ { for i := 0; i < t.NumField(); i++ {
col, err := parser.parseField(table, i, t.Field(i), v.Field(i)) col, err := parser.parseField(table, i, t.Field(i), v.Field(i))

View File

@ -26,6 +26,20 @@ func (p ParseTableName2) TableName() string {
return "p_parseTableName" return "p_parseTableName"
} }
type ParseTableComment struct{}
type ParseTableComment1 struct{}
type ParseTableComment2 struct{}
func (p ParseTableComment1) TableComment() string {
return "p_parseTableComment1"
}
func (p *ParseTableComment2) TableComment() string {
return "p_parseTableComment2"
}
func TestParseTableName(t *testing.T) { func TestParseTableName(t *testing.T) {
parser := NewParser( parser := NewParser(
"xorm", "xorm",
@ -47,6 +61,36 @@ func TestParseTableName(t *testing.T) {
assert.EqualValues(t, "p_parseTableName", table.Name) assert.EqualValues(t, "p_parseTableName", table.Name)
} }
func TestParseTableComment(t *testing.T) {
parser := NewParser(
"xorm",
dialects.QueryDialect("mysql"),
names.SnakeMapper{},
names.SnakeMapper{},
caches.NewManager(),
)
table, err := parser.Parse(reflect.ValueOf(new(ParseTableComment)))
assert.NoError(t, err)
assert.EqualValues(t, "", table.Comment)
table, err = parser.Parse(reflect.ValueOf(new(ParseTableComment1)))
assert.NoError(t, err)
assert.EqualValues(t, "p_parseTableComment1", table.Comment)
table, err = parser.Parse(reflect.ValueOf(ParseTableComment1{}))
assert.NoError(t, err)
assert.EqualValues(t, "p_parseTableComment1", table.Comment)
table, err = parser.Parse(reflect.ValueOf(new(ParseTableComment2)))
assert.NoError(t, err)
assert.EqualValues(t, "p_parseTableComment2", table.Comment)
table, err = parser.Parse(reflect.ValueOf(ParseTableComment2{}))
assert.NoError(t, err)
assert.EqualValues(t, "p_parseTableComment2", table.Comment)
}
func TestUnexportField(t *testing.T) { func TestUnexportField(t *testing.T) {
parser := NewParser( parser := NewParser(
"xorm", "xorm",