Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/go-sql-driver/mysql"
// We must import the mysql driver as it is required by sqlx.
_ "github.com/go-sql-driver/mysql"

"github.com/ntindall/sql-gen-doc/format"
)

Expand Down Expand Up @@ -71,7 +72,12 @@ func Execute() {
logger.Fatalf("couldn't convert data to logical indexes: table %s. reason: %s", tableName, err)
}

_, err = markdown.WriteString(format.CreateTableMarkdown(tableName, table.Comment, columns, logicalIndexes))
foreignKeyData, err := format.GetForeignKeyDescriptions(ctx, db, tableName)
if err != nil {
logger.Fatalf("couldn't query database to fetch foreign key data: table %s. reason: %s", tableName, err)
}

_, err = markdown.WriteString(format.CreateTableMarkdown(tableName, table.Comment, columns, logicalIndexes, foreignKeyData))
if err != nil {
logger.Fatalf("error writing to buffer: table %s. reason: %s", tableName, err)
}
Expand Down
5 changes: 5 additions & 0 deletions fixtures/expected1.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ this is a table comment
|------------------------|---------|---------------------------|---------|
| `PRIMARY` | `true` | `(company_id, person_id)` | |
| `fk_persons_person_id` | `false` | `(person_id)` | |
#### Foreign Key
| KEY NAME | TABLE NAME | COLUMN NAME | REFERENCES |
|--------------------|-------------|--------------|------------------------|
| `employees_ibfk_1` | `employees` | `company_id` | `companies.company_id` |
| `employees_ibfk_2` | `employees` | `person_id` | `persons.person_id` |

## goose_db_version
#### SCHEMA
Expand Down
5 changes: 5 additions & 0 deletions fixtures/expected2.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ this is a table comment
|------------------------|---------|---------------------------|---------|
| `PRIMARY` | `true` | `(company_id, person_id)` | |
| `fk_persons_person_id` | `false` | `(person_id)` | |
#### Foreign Key
| KEY NAME | TABLE NAME | COLUMN NAME | REFERENCES |
|--------------------|-------------|--------------|------------------------|
| `employees_ibfk_1` | `employees` | `company_id` | `companies.company_id` |
| `employees_ibfk_2` | `employees` | `person_id` | `persons.person_id` |

## goose_db_version
#### SCHEMA
Expand Down
13 changes: 13 additions & 0 deletions format/foreign_description.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package format

// ForeignDescription is generated from INFORMATION_SCHEMA.KEY_COLUMN_USAGE
type ForeignDescription struct {
TableName string `db:"table_name"`
ColumnName string `db:"column_name"`
ConstraintName string `db:"constraint_name"`
ReferencedTableName string `db:"referenced_table_name"`
ReferencedColumnName string `db:"referenced_column_name"`
}

// ForeignDescriptions is a set of foreign key descriptions
type ForeignDescriptions []ForeignDescription
35 changes: 26 additions & 9 deletions format/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func wrapBackTicks(s string) string {
// CreateTableMarkdown takes the name of a table in a database and a list of
// ColumnDescription and returns a formatted markdown table with the
// corresponding data.
func CreateTableMarkdown(tableName string, comment string, columns []ColumnDescription, indexes []LogicalIndex) string {
func CreateTableMarkdown(tableName string, comment string, columns []ColumnDescription, indexes []LogicalIndex, foreignKeys ForeignDescriptions) string {
tableMarkdown := bytes.NewBufferString(`## ` + tableName + "\n")

if comment != "" {
Expand All @@ -27,10 +27,7 @@ func CreateTableMarkdown(tableName string, comment string, columns []ColumnDescr

tableMarkdown.WriteString("#### SCHEMA\n")
columnsTable := tablewriter.NewWriter(tableMarkdown)
columnsTable.SetHeader([]string{"FIELD", "TYPE", "NULL", "KEY", "DEFAULT", "EXTRA", "COMMENT"})
columnsTable.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
columnsTable.SetAutoWrapText(false)
columnsTable.SetCenterSeparator("|")
formatTable(columnsTable, []string{"FIELD", "TYPE", "NULL", "KEY", "DEFAULT", "EXTRA", "COMMENT"})

for _, col := range columns {
columnsTable.Append([]string{
Expand Down Expand Up @@ -65,10 +62,7 @@ func CreateTableMarkdown(tableName string, comment string, columns []ColumnDescr
header = []string{"KEY NAME", "UNIQUE", "COLUMNS", "COMMENT", "EXPRESSION"}
}

indexesTable.SetHeader(header)
indexesTable.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
indexesTable.SetAutoWrapText(false)
indexesTable.SetCenterSeparator("|")
formatTable(indexesTable, header)

for _, idx := range indexes {
indexRow := []string{
Expand All @@ -87,9 +81,32 @@ func CreateTableMarkdown(tableName string, comment string, columns []ColumnDescr
// write the indexes table to the buf
indexesTable.Render()

if len(foreignKeys) != 0 {
tableMarkdown.WriteString("#### Foreign Key\n")
foreignKeyTable := tablewriter.NewWriter(tableMarkdown)
formatTable(foreignKeyTable, []string{"KEY NAME", "TABLE NAME", "COLUMN NAME", "REFERENCES"})

for _, fk := range foreignKeys {
foreignKeyTable.Append([]string{
wrapBackTicks(fk.ConstraintName),
wrapBackTicks(fk.TableName),
wrapBackTicks(fk.ColumnName),
wrapBackTicks(fmt.Sprintf("%s.%s", fk.ReferencedTableName, fk.ReferencedColumnName)),
})
}
foreignKeyTable.Render()
}

return tableMarkdown.String()
}

func formatTable(table *tablewriter.Table, header []string) {
table.SetHeader(header)
table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
table.SetAutoWrapText(false)
table.SetCenterSeparator("|")
}

func insertBetweenTags(
file string,
markdown string,
Expand Down
44 changes: 44 additions & 0 deletions format/format_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package format

import (
"bytes"
"fmt"
"strings"
"testing"

"github.com/olekukonko/tablewriter"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -74,3 +78,43 @@ markdown
}
}
}

func TestRenderForeignKeys(t *testing.T) {

testForeignKeys := []ForeignDescription{
{
ConstraintName: "fk1",
TableName: "table1",
ColumnName: "col1",
ReferencedTableName: "reftable",
ReferencedColumnName: "refcol",
},
}

expected := `
| KEY NAME | TABLE NAME | COLUMN NAME | REFERENCES |
|----------|------------|-------------|-------------------|
| ` + "`fk1` " + ` | ` + "`table1` " + ` | ` + "`col1` " + ` | ` + "`reftable.refcol`" + ` |
`

buf := &bytes.Buffer{}

foreignKeyTable := tablewriter.NewWriter(buf)
formatTable(foreignKeyTable, []string{"KEY NAME", "TABLE NAME", "COLUMN NAME", "REFERENCES"})

for _, fk := range testForeignKeys {
foreignKeyTable.Append([]string{
wrapBackTicks(fk.ConstraintName),
wrapBackTicks(fk.TableName),
wrapBackTicks(fk.ColumnName),
wrapBackTicks(fmt.Sprintf("%s.%s", fk.ReferencedTableName, fk.ReferencedColumnName)),
})
}

foreignKeyTable.Render()

actual := buf.String()

assert.Equal(t, strings.TrimLeft(expected, "\n"), strings.TrimLeft(actual, "\n"))

}
21 changes: 21 additions & 0 deletions format/io.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,27 @@ func GetIndexDescriptions(
return result, nil
}

// GetForeignKeyDescriptions queries INFORMATION_SCHEMA.KEY_COLUMN_USAGE table about references information
func GetForeignKeyDescriptions(
ctx context.Context,
db *sqlx.DB,
tableName string,
) (ForeignDescriptions, error) {
result := []ForeignDescription{}

if err := db.SelectContext(ctx, &result,
`SELECT table_name AS table_name, column_name AS column_name, constraint_name AS constraint_name, referenced_table_name AS referenced_table_name, referenced_column_name AS referenced_column_name
FROM information_schema.key_column_usage
WHERE table_name = ? AND referenced_table_name IS NOT NULL
ORDER BY 1,2`,
tableName,
); err != nil {
return nil, err
}

return result, nil
}

// WriteToFile takes a filename and a markdown string and writes the markdown
// to the file. If the file is annotated with markdown comments, the markdown
// will be inserted in between the comments. e.g.
Expand Down
6 changes: 3 additions & 3 deletions scripts/integrate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ had_err=0

# test case 1
log "testing create a new file"
./bin/sql-gen-doc -dsn 'user:password@tcp(mysql:3306)/example' -o tmp/example.md
cp tmp/example.md logs/out1.md
if [ "$(diff --text tmp/example.md fixtures/expected1.md |& tee logs/test1.diff)" ]; then
./bin/sql-gen-doc -dsn 'user:password@tcp(mysql:3306)/example' -o tmp/example1.md
cp tmp/example1.md logs/out1.md
if [ "$(diff --text tmp/example1.md fixtures/expected1.md |& tee logs/test1.diff)" ]; then
cat tmp/example1.md
log_error "output did not match fixture -- see logs/test1.diff"
had_err=1
Expand Down