Help us shape the future of Go! Take the 2026 Go Developer Survey

erd

package
v0.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 8, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package erd draws a connection's schema as an entity-relationship diagram: its tables, their columns and keys, and the foreign keys between them.

It renders the same three ways the explain package renders a plan, for the same reasons: a PNG or JPEG for a chat, a ticket or a slide (drawn in Go, picture.go), and Mermaid erDiagram source (mermaid.go) for a pull request or a wiki page that draws ```mermaid blocks.

db.Manager.Schema ──► *erd.Schema ──┬─► Mermaid()      text
(catalog queries,       (this file)  ├─► PNG(opt)       bytes
 one per engine)                     └─► JPEG(opt)      bytes
                        Around(names, depth) narrows it to one
                        table's neighbourhood first

The package is a leaf — it knows nothing of drivers or connections. The db package reads each engine's catalog and builds a Schema; everything here works on that model alone, so it is testable without a database.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Dir

func Dir() string

Dir is where the TUI keeps the diagrams it saves and opens.

func SafeFileName

func SafeFileName(s string) string

SafeFileName keeps a connection name usable in a file name (and in a Content-Disposition header): anything but letters, digits, - and _ becomes _.

Types

type Column

type Column struct {
	Name     string
	Type     string
	Nullable bool
	PK       bool // part of the primary key
	FK       bool // part of some foreign key
	Unique   bool // part of some unique key (not the primary key)
}

Column is one column and the key roles it plays.

type Options

type Options struct {
	// Palette colors the picture; the zero value is theme.Default(). dbc
	// web passes the palette the page is wearing; the shell and the TUI
	// pass plan_theme (or `dbc erd --theme`), the one setting for every
	// picture dbc draws.
	Palette theme.Palette
	// Scale is device pixels per CSS pixel; 0 means 2 (sharp when zoomed).
	// A diagram too big to draw at it is drawn smaller (see maxPixels).
	Scale float64
}

Options choose how the picture looks. The zero value is dbc's dark palette at 2×.

type Rel

type Rel struct {
	Name       string
	Child      *Table
	Parent     *Table
	ChildCols  []string
	ParentCols []string
}

Rel is one foreign key: Child's ChildCols reference Parent's ParentCols, pairwise in order. A self-reference (an employee's manager) has Child == Parent.

func (*Rel) Identifying

func (r *Rel) Identifying() bool

Identifying reports whether the foreign key is part of the child's identity: every one of its columns is in the child's primary key (an order line keyed by (order_id, line_no)). Mermaid draws these solid and the rest dashed.

func (*Rel) OneToOne

func (r *Rel) OneToOne() bool

OneToOne reports whether at most one child row can reference a parent row: the foreign key's columns are exactly the child's primary key or one of its unique keys (as a set — key order does not matter for uniqueness). It decides the child end's notation: zero-or-one rather than zero-or-many.

func (*Rel) Optional

func (r *Rel) Optional() bool

Optional reports whether a child row may have no parent: any of the foreign key's columns is nullable. A composite key with one NULL column is not checked at all under the default MATCH SIMPLE, so one is enough. It decides the parent end's notation: zero-or-one rather than exactly one.

type Schema

type Schema struct {
	Conn   string // connection name, for the title and file names
	Driver string // engine, for the title
	Tables []*Table
	Rels   []*Rel
}

Schema is the diagram's subject: a set of tables and the foreign keys among them. Rels only ever point at tables in Tables — a foreign key to a table outside the set (another MySQL database, a schema the user cannot see) is dropped when the Schema is built, as the diagram has no box to draw it to.

func (*Schema) Around

func (s *Schema) Around(names []string, depth int) (out *Schema, missing []string)

Around narrows the schema to the named tables and every table within depth foreign-key hops of them, in either direction — the tables a person asking about orders wants to see: what orders references, and what references orders. depth 0 is the named tables alone; a negative depth is unlimited (the named tables' whole connected component).

A name that does not resolve (see Find) is returned in missing rather than failing the whole call, so a caller can say which one was wrong.

func (*Schema) FileName

func (s *Schema) FileName(ext string) string

FileName is the name a saved or downloaded rendering takes: erd-<conn>-<yyyymmdd-hhmmss>.<ext>, or erd-<stamp>.<ext> with no connection — the plan files' pattern (explain.Plan.FileName), so the two sort side by side in a downloads folder.

func (*Schema) Find

func (s *Schema) Find(name string) (*Table, bool)

Find resolves a name as a caller spells it — the table's Label, or schema.name, or the bare name when that is unique — to a table. An exact match wins over a case-insensitive one, and a name that fits several tables (a bare name in two schemas) is not guessed at.

func (*Schema) JPEG

func (s *Schema) JPEG(opt Options) ([]byte, error)

JPEG renders the picture as a JPEG. Quality 90 keeps text edges clean.

func (*Schema) MarkKeys

func (s *Schema) MarkKeys()

MarkKeys sets each column's PK, FK and Unique flags from the tables' keys and the relationships. The builder calls it once the Schema is complete; it is idempotent, so a caller that edits keys can call it again.

func (*Schema) Mermaid

func (s *Schema) Mermaid() string

Mermaid renders the schema as a Mermaid erDiagram, for a pull request, an issue or a wiki page that draws ```mermaid blocks (GitHub, GitLab, Notion, Obsidian …). The page that draws it applies its own theme, so there is no palette here.

erDiagram
    %% dbc · demo (sqlite) · 3 tables · 2 relationships
    owners ||--o{ cats : "owner_id"
    cats ||--|{ visits : "cat_id"
    cats {
        INTEGER id PK
        INTEGER owner_id FK
    }

THE NOTATION. A relationship line is written parent-first, so its left end is the "one" side and its right end the child's:

parent end  ||  exactly one      the foreign key's columns are NOT NULL
            |o  zero or one      one of them is nullable (Rel.Optional)
child end   o{  zero or many     the usual foreign key
            o|  zero or one      the key's columns are unique in the
                                 child (Rel.OneToOne): a 1:1 extension
line        --  identifying      the key is inside the child's PK
            ..  non-identifying  the rest (Rel.Identifying)

The label is the child's key columns, which is what a reader needs to write the join; the constraint's name is usually a generated "<table>_<col>_fkey" that says the same thing less usefully.

NAMES. Mermaid's grammar is narrow: an entity name is a word, and an attribute's type and name are words of letters, digits, _ - ( ) [ ]. A table whose label is not such a word ("public.cats" on a multi-schema connection, a name with a space) is given a generated id with the label as its alias — id["label"] — which Mermaid draws as the label (entity name aliases need Mermaid 10.5 or later; a plain word is used whenever possible so older renderers still read most diagrams). A column name or type that is not a word is made one (spaces and commas become _), and the original is kept as the attribute's comment, so "numeric(10,2)" is still readable in the drawing.

func (*Schema) PNG

func (s *Schema) PNG(opt Options) ([]byte, error)

PNG renders the picture as a PNG: lossless, and for flat fills and text usually smaller than the JPEG as well.

func (*Schema) Picture

func (s *Schema) Picture(opt Options) (*image.RGBA, error)

Picture draws the diagram.

Two passes: the layout is measured at 1× (so it does not depend on the scale), then the image is allocated at its final size and painted.

func (*Schema) Select

func (s *Schema) Select(sel Selection) (out *Schema, missing []string)

Select narrows the schema to sel. Names that do not resolve come back in missing, and the rest of the selection still applies.

func (*Schema) Sort

func (s *Schema) Sort()

Sort puts the tables in Label order and the relationships in (child, name) order, so every rendering of the same schema is byte-for-byte the same whatever order the catalog returned rows in.

func (*Schema) Title

func (s *Schema) Title() string

Title is the one-line description every rendering carries: where the schema came from and how big the diagram is.

func (*Schema) WithoutViews

func (s *Schema) WithoutViews() *Schema

WithoutViews drops the views. Most diagrams want only tables: a view has no keys, so it would be a box with no lines, and a schema with many views would be mostly those.

func (*Schema) WriteFile

func (s *Schema) WriteFile(dir, ext string, data []byte) (string, error)

WriteFile saves one rendering into dir (created if need be) under FileName and returns its path. 0600: a schema is the user's business, and dir is usually the shared temp directory.

type Selection

type Selection struct {
	// Tables, when set, centres the diagram on these tables (as Find
	// resolves names) and their neighbours within Depth hops.
	Tables []string
	// Depth is Around's depth; ignored without Tables.
	Depth int
	// Views keeps the views. A view named in Tables is kept regardless —
	// asking for a diagram of a view is asking to see it.
	Views bool
	// Schema, when set and Tables is not, keeps only that schema's tables:
	// "diagram everything" from a sidebar that shows one schema at a time
	// means everything in that schema, not every table in the database.
	// With Tables it is ignored, since a neighbourhood follows its keys
	// into whatever schema they lead.
	Schema string
}

Selection is what a caller asks a diagram to show. The zero value is every table (no views) on the connection.

type Table

type Table struct {
	Schema string
	Name   string
	// Label is how the table is named on the diagram, and how a caller
	// names it to Around: bare when the connection has one schema,
	// schema.name when it has several — the sidebar's rule, so the name a
	// user clicks is the name that resolves.
	Label   string
	View    bool
	Cols    []*Column
	PK      []string   // primary key columns, in key order; nil when none
	Uniques [][]string // each unique key's columns, in key order
}

Table is one entity: a table, or a view when asked for.

func (*Table) Col

func (t *Table) Col(name string) *Column

Col finds a column by name, or nil.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL