|
@@ -0,0 +1,149 @@
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "log"
|
|
|
+ "os"
|
|
|
+ "strings"
|
|
|
+
|
|
|
+ "github.com/olekukonko/tablewriter"
|
|
|
+ "github.com/pkg/errors"
|
|
|
+ "gopkg.in/DATA-DOG/go-sqlmock.v2"
|
|
|
+ "gorm.io/driver/mysql"
|
|
|
+ "gorm.io/driver/postgres"
|
|
|
+ "gorm.io/driver/sqlite"
|
|
|
+ "gorm.io/gorm"
|
|
|
+ "gorm.io/gorm/clause"
|
|
|
+ "gorm.io/gorm/schema"
|
|
|
+
|
|
|
+ "gogs.io/gogs/internal/db"
|
|
|
+)
|
|
|
+
|
|
|
+//go:generate go run main.go ../../../docs/dev/database_schema.md
|
|
|
+
|
|
|
+func main() {
|
|
|
+ w, err := os.Create(os.Args[1])
|
|
|
+ if err != nil {
|
|
|
+ log.Fatalf("Failed to create file: %v", err)
|
|
|
+ }
|
|
|
+ defer func() { _ = w.Close() }()
|
|
|
+
|
|
|
+ conn, _, err := sqlmock.New()
|
|
|
+ if err != nil {
|
|
|
+ log.Fatalf("Failed to get mock connection: %v", err)
|
|
|
+ }
|
|
|
+ defer func() { _ = conn.Close() }()
|
|
|
+
|
|
|
+ dialectors := []gorm.Dialector{
|
|
|
+ postgres.New(postgres.Config{
|
|
|
+ Conn: conn,
|
|
|
+ }),
|
|
|
+ mysql.New(mysql.Config{
|
|
|
+ Conn: conn,
|
|
|
+ SkipInitializeWithVersion: true,
|
|
|
+ }),
|
|
|
+ sqlite.Open(""),
|
|
|
+ }
|
|
|
+ collected := make([][]*tableInfo, 0, len(dialectors))
|
|
|
+ for i, dialector := range dialectors {
|
|
|
+ tableInfos, err := generate(dialector)
|
|
|
+ if err != nil {
|
|
|
+ log.Fatalf("Failed to get table info of %d: %v", i, err)
|
|
|
+ }
|
|
|
+
|
|
|
+ collected = append(collected, tableInfos)
|
|
|
+ }
|
|
|
+
|
|
|
+ for i, ti := range collected[0] {
|
|
|
+ _, _ = w.WriteString(`# Table "` + ti.Name + `"`)
|
|
|
+ _, _ = w.WriteString("\n\n")
|
|
|
+
|
|
|
+ _, _ = w.WriteString("```\n")
|
|
|
+
|
|
|
+ table := tablewriter.NewWriter(w)
|
|
|
+ table.SetHeader([]string{"Field", "Column", "PostgreSQL", "MySQL", "SQLite3"})
|
|
|
+ table.SetBorder(false)
|
|
|
+ for j, f := range ti.Fields {
|
|
|
+ table.Append([]string{
|
|
|
+ f.Name, f.Column,
|
|
|
+ strings.ToUpper(f.Type), // PostgreSQL
|
|
|
+ strings.ToUpper(collected[1][i].Fields[j].Type), // MySQL
|
|
|
+ strings.ToUpper(collected[2][i].Fields[j].Type), // SQLite3
|
|
|
+ })
|
|
|
+ }
|
|
|
+ table.Render()
|
|
|
+ _, _ = w.WriteString("\n")
|
|
|
+
|
|
|
+ _, _ = w.WriteString("Primary keys: ")
|
|
|
+ _, _ = w.WriteString(strings.Join(ti.PrimaryKeys, ", "))
|
|
|
+ _, _ = w.WriteString("\n")
|
|
|
+
|
|
|
+ _, _ = w.WriteString("```\n\n")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+type tableField struct {
|
|
|
+ Name string
|
|
|
+ Column string
|
|
|
+ Type string
|
|
|
+}
|
|
|
+
|
|
|
+type tableInfo struct {
|
|
|
+ Name string
|
|
|
+ Fields []*tableField
|
|
|
+ PrimaryKeys []string
|
|
|
+}
|
|
|
+
|
|
|
+// This function is derived from gorm.io/gorm/migrator/migrator.go:Migrator.CreateTable.
|
|
|
+func generate(dialector gorm.Dialector) ([]*tableInfo, error) {
|
|
|
+ conn, err := gorm.Open(dialector, &gorm.Config{
|
|
|
+ NamingStrategy: schema.NamingStrategy{
|
|
|
+ SingularTable: true,
|
|
|
+ },
|
|
|
+ DryRun: true,
|
|
|
+ DisableAutomaticPing: true,
|
|
|
+ })
|
|
|
+ if err != nil {
|
|
|
+ return nil, errors.Wrap(err, "open database")
|
|
|
+ }
|
|
|
+
|
|
|
+ m := conn.Migrator().(interface {
|
|
|
+ RunWithValue(value interface{}, fc func(*gorm.Statement) error) error
|
|
|
+ FullDataTypeOf(*schema.Field) clause.Expr
|
|
|
+ })
|
|
|
+ tableInfos := make([]*tableInfo, 0, len(db.Tables))
|
|
|
+ for _, table := range db.Tables {
|
|
|
+ err = m.RunWithValue(table, func(stmt *gorm.Statement) error {
|
|
|
+ fields := make([]*tableField, 0, len(stmt.Schema.DBNames))
|
|
|
+ for _, field := range stmt.Schema.Fields {
|
|
|
+ if field.DBName == "" {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ fields = append(fields, &tableField{
|
|
|
+ Name: field.Name,
|
|
|
+ Column: field.DBName,
|
|
|
+ Type: m.FullDataTypeOf(field).SQL,
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ primaryKeys := make([]string, 0, len(stmt.Schema.PrimaryFields))
|
|
|
+ if len(stmt.Schema.PrimaryFields) > 0 {
|
|
|
+ for _, field := range stmt.Schema.PrimaryFields {
|
|
|
+ primaryKeys = append(primaryKeys, field.DBName)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ tableInfos = append(tableInfos, &tableInfo{
|
|
|
+ Name: stmt.Table,
|
|
|
+ Fields: fields,
|
|
|
+ PrimaryKeys: primaryKeys,
|
|
|
+ })
|
|
|
+ return nil
|
|
|
+ })
|
|
|
+ if err != nil {
|
|
|
+ return nil, errors.Wrap(err, "gather table information")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return tableInfos, nil
|
|
|
+}
|