From 3b7715b5529407bbd5e7d2328bf854709b5856de Mon Sep 17 00:00:00 2001 From: Ariel Mashraki <7413593+a8m@users.noreply.github.com> Date: Sat, 4 Mar 2023 20:50:26 +0200 Subject: [PATCH] dialect/sql/schema: allow setting table comments (#3365) --- dialect/sql/schema/atlas.go | 3 +++ dialect/sql/schema/schema.go | 9 ++++++++- doc/md/schema-annotations.md | 12 +++++++----- entc/gen/graph.go | 3 ++- entc/gen/template/migrate/schema.tmpl | 3 +++ entc/gen/type.go | 16 ++++++++++++++++ entc/integration/migrate/entv2/media.go | 2 +- entc/integration/migrate/entv2/migrate/schema.go | 1 + entc/integration/migrate/entv2/schema/media.go | 1 + entc/integration/migrate/migrate_test.go | 12 ++++++++++++ schema/schema.go | 2 +- 11 files changed, 55 insertions(+), 9 deletions(-) diff --git a/dialect/sql/schema/atlas.go b/dialect/sql/schema/atlas.go index dd89f3b39..519edba09 100644 --- a/dialect/sql/schema/atlas.go +++ b/dialect/sql/schema/atlas.go @@ -872,6 +872,9 @@ func (a *Atlas) tables(tables []*Table) ([]*schema.Table, error) { ts := make([]*schema.Table, len(tables)) for i, et := range tables { at := schema.NewTable(et.Name) + if et.Comment != "" { + at.SetComment(et.Comment) + } a.sqlDialect.atTable(et, at) if a.universalID && et.Name != TypeTable && len(et.PrimaryKey) == 1 { r, err := a.pkRange(et) diff --git a/dialect/sql/schema/schema.go b/dialect/sql/schema/schema.go index 400185267..2b613a47e 100644 --- a/dialect/sql/schema/schema.go +++ b/dialect/sql/schema/schema.go @@ -36,6 +36,7 @@ type Table struct { PrimaryKey []*Column ForeignKeys []*ForeignKey Annotation *entsql.Annotation + Comment string } // NewTable returns a new table with the given name. @@ -46,6 +47,12 @@ func NewTable(name string) *Table { } } +// SetComment sets the table comment. +func (t *Table) SetComment(c string) *Table { + t.Comment = c + return t +} + // AddPrimary adds a new primary key to the table. func (t *Table) AddPrimary(c *Column) *Table { c.Key = PrimaryKey @@ -295,7 +302,7 @@ type Column struct { typ string // row column type (used for Rows.Scan). indexes Indexes // linked indexes. foreign *ForeignKey // linked foreign-key. - Comment string // column comment. + Comment string // optional column comment. } // Expr represents a raw expression. It is used to distinguish between diff --git a/doc/md/schema-annotations.md b/doc/md/schema-annotations.md index 34c048084..634cc22b8 100644 --- a/doc/md/schema-annotations.md +++ b/doc/md/schema-annotations.md @@ -88,15 +88,16 @@ rows in the child table. ## Database Comments -By default, column comments are not stored in the database. However, this functionality can be enabled by using the -`WithComments(true)` annotation. For example: +By default, table and column comments are not stored in the database. However, this functionality can be enabled by +using the `WithComments(true)` annotation. For example: -```go title="ent/schema/user.go" {17-19,32-35} +```go title="ent/schema/user.go" {18-21,34-37} package schema import ( "entgo.io/ent" "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" "entgo.io/ent/schema/field" ) @@ -108,9 +109,10 @@ type User struct { // Annotations of the User. func (User) Annotations() []schema.Annotation { return []schema.Annotation{ - // Adding this annotation on the - // type enables it for all fields. + // Adding this annotation to the schema enables + // comments for the table and all its fields. entsql.WithComments(true), + schema.Comment("Comment that appears in both the schema and the generated code"), } } diff --git a/entc/gen/graph.go b/entc/gen/graph.go index 5b2b05803..1f57895c5 100644 --- a/entc/gen/graph.go +++ b/entc/gen/graph.go @@ -612,7 +612,8 @@ func (g *Graph) edgeSchemas() error { func (g *Graph) Tables() (all []*schema.Table, err error) { tables := make(map[string]*schema.Table) for _, n := range g.Nodes { - table := schema.NewTable(n.Table()) + table := schema.NewTable(n.Table()). + SetComment(n.sqlComment()) if n.HasOneFieldID() { table.AddPrimary(n.ID.PK()) } diff --git a/entc/gen/template/migrate/schema.tmpl b/entc/gen/template/migrate/schema.tmpl index 0bd52379e..8c117abae 100644 --- a/entc/gen/template/migrate/schema.tmpl +++ b/entc/gen/template/migrate/schema.tmpl @@ -57,6 +57,9 @@ var ( // {{ $table }} holds the schema information for the "{{ $t.Name }}" table. {{ $table }} = &schema.Table{ Name: "{{ $t.Name }}", + {{- with $t.Comment }} + Comment: "{{ . }}", + {{- end }} Columns: {{ $columns }}, PrimaryKey: []*schema.Column{ {{- range $pk := $t.PrimaryKey }} diff --git a/entc/gen/type.go b/entc/gen/type.go index 5d6cecb03..04488d76c 100644 --- a/entc/gen/type.go +++ b/entc/gen/type.go @@ -23,6 +23,7 @@ import ( "entgo.io/ent/dialect/entsql" "entgo.io/ent/dialect/sql/schema" "entgo.io/ent/entc/load" + entschema "entgo.io/ent/schema" "entgo.io/ent/schema/edge" "entgo.io/ent/schema/field" ) @@ -1018,6 +1019,21 @@ func aliases(g *Graph) { } } +// sqlComment returns the SQL database comment for the node (table), if defined and enabled. +func (t Type) sqlComment() string { + if ant := t.EntSQL(); ant == nil || ant.WithComments == nil || !*ant.WithComments { + return "" + } + ant := &entschema.CommentAnnotation{} + if t.Annotations == nil || t.Annotations[ant.Name()] == nil { + return "" + } + if b, err := json.Marshal(t.Annotations[ant.Name()]); err == nil { + _ = json.Unmarshal(b, &ant) + } + return ant.Text +} + // Constant returns the constant name of the field. func (f Field) Constant() string { return "Field" + pascal(f.Name) diff --git a/entc/integration/migrate/entv2/media.go b/entc/integration/migrate/entv2/media.go index dff494ac1..7c1aab9e5 100644 --- a/entc/integration/migrate/entv2/media.go +++ b/entc/integration/migrate/entv2/media.go @@ -14,7 +14,7 @@ import ( "entgo.io/ent/entc/integration/migrate/entv2/media" ) -// Media is the model entity for the Media schema. +// Comment that appears in both the schema and the generated code type Media struct { config `json:"-"` // ID of the ent. diff --git a/entc/integration/migrate/entv2/migrate/schema.go b/entc/integration/migrate/entv2/migrate/schema.go index 762bbe97d..8a61f9abf 100644 --- a/entc/integration/migrate/entv2/migrate/schema.go +++ b/entc/integration/migrate/entv2/migrate/schema.go @@ -96,6 +96,7 @@ var ( // MediaTable holds the schema information for the "media" table. MediaTable = &schema.Table{ Name: "media", + Comment: "Comment that appears in both the schema and the generated code", Columns: MediaColumns, PrimaryKey: []*schema.Column{MediaColumns[0]}, Indexes: []*schema.Index{ diff --git a/entc/integration/migrate/entv2/schema/media.go b/entc/integration/migrate/entv2/schema/media.go index 52ad93b9e..12e70c6c6 100644 --- a/entc/integration/migrate/entv2/schema/media.go +++ b/entc/integration/migrate/entv2/schema/media.go @@ -47,6 +47,7 @@ func (Media) Indexes() []ent.Index { // Annotations of the Media. func (Media) Annotations() []schema.Annotation { return []schema.Annotation{ + schema.Comment("Comment that appears in both the schema and the generated code"), entsql.WithComments(true), entsql.Check("text <> 'boring'"), entsql.Checks(map[string]string{ diff --git a/entc/integration/migrate/migrate_test.go b/entc/integration/migrate/migrate_test.go index 5ddb6e7b4..eb7ff2ea9 100644 --- a/entc/integration/migrate/migrate_test.go +++ b/entc/integration/migrate/migrate_test.go @@ -74,6 +74,7 @@ func TestMySQL(t *testing.T) { NicknameSearch(t, clientv2) TimePrecision(t, drv, "SELECT datetime_precision FROM information_schema.columns WHERE table_schema = 'migrate' AND table_name = ? AND column_name = ?") ColumnComments(t, drv, "SELECT column_name as name, column_comment as comment FROM information_schema.columns WHERE table_schema = 'migrate' AND table_name = 'media' ORDER BY ordinal_position") + TableComment(t, drv, "SELECT table_comment FROM information_schema.tables WHERE table_schema = 'migrate' AND table_name = 'media'") require.NoError(t, err, root.Exec(ctx, "DROP DATABASE IF EXISTS versioned_migrate", []any{}, new(sql.Result))) require.NoError(t, root.Exec(ctx, "CREATE DATABASE IF NOT EXISTS versioned_migrate", []any{}, new(sql.Result))) @@ -142,6 +143,7 @@ func TestPostgres(t *testing.T) { PKDefault(t, drv, `SELECT column_default FROM information_schema.columns WHERE table_name = 'zoos' AND column_name = $1`, "floor((random() * ((~ (1 << 31)))::double precision))") IndexOpClass(t, drv) ColumnComments(t, drv, `SELECT column_name as name, col_description(table_name::regclass::oid, ordinal_position) as comment FROM information_schema.columns WHERE table_name = 'media' ORDER BY ordinal_position`) + TableComment(t, drv, "SELECT obj_description('media'::regclass::oid)") if version != "10" { IncludeColumns(t, drv) } @@ -712,6 +714,16 @@ func JSONDefault(t *testing.T, drv *sql.Driver, query string) { require.NotEmpty(t, s) } +func TableComment(t *testing.T, drv *sql.Driver, query string) { + ctx := context.Background() + rows, err := drv.QueryContext(ctx, query) + require.NoError(t, err) + comment, err := sql.ScanString(rows) + require.NoError(t, err) + require.NoError(t, rows.Close()) + require.Equal(t, "Comment that appears in both the schema and the generated code", comment) +} + func ColumnComments(t *testing.T, drv *sql.Driver, query string) { ctx := context.Background() rows, err := drv.QueryContext(ctx, query) diff --git a/schema/schema.go b/schema/schema.go index 1cd60983c..c893b9e37 100644 --- a/schema/schema.go +++ b/schema/schema.go @@ -30,7 +30,7 @@ type CommentAnnotation struct { } // Name implements the Annotation interface. -func (c *CommentAnnotation) Name() string { +func (*CommentAnnotation) Name() string { return "Comment" }