2016-06-23 08:09:56 +02:00
|
|
|
// Package bdb supplies the sql(b)oiler (d)ata(b)ase abstractions.
|
|
|
|
package bdb
|
2016-02-23 09:27:32 +01:00
|
|
|
|
2016-06-13 00:34:57 +02:00
|
|
|
import "github.com/pkg/errors"
|
2016-04-03 09:15:35 +02:00
|
|
|
|
2016-03-23 05:25:57 +01:00
|
|
|
// Interface for a database driver. Functionality required to support a specific
|
|
|
|
// database type (eg, MySQL, Postgres etc.)
|
|
|
|
type Interface interface {
|
2016-04-03 09:15:35 +02:00
|
|
|
TableNames() ([]string, error)
|
|
|
|
Columns(tableName string) ([]Column, error)
|
|
|
|
PrimaryKeyInfo(tableName string) (*PrimaryKey, error)
|
|
|
|
ForeignKeyInfo(tableName string) ([]ForeignKey, error)
|
2016-03-23 05:25:57 +01:00
|
|
|
|
2016-04-03 09:15:35 +02:00
|
|
|
// TranslateColumnType takes a Database column type and returns a go column type.
|
2016-03-23 06:05:23 +01:00
|
|
|
TranslateColumnType(Column) Column
|
2016-02-24 10:17:58 +01:00
|
|
|
|
2016-02-23 09:27:32 +01:00
|
|
|
// Open the database connection
|
|
|
|
Open() error
|
2016-02-24 10:17:58 +01:00
|
|
|
|
2016-02-23 09:27:32 +01:00
|
|
|
// Close the database connection
|
|
|
|
Close()
|
|
|
|
}
|
|
|
|
|
2016-04-03 09:15:35 +02:00
|
|
|
// Tables returns the table metadata for the given tables, or all tables if
|
|
|
|
// no tables are provided.
|
|
|
|
func Tables(db Interface, names ...string) ([]Table, error) {
|
|
|
|
var err error
|
|
|
|
if len(names) == 0 {
|
|
|
|
if names, err = db.TableNames(); err != nil {
|
2016-06-13 00:34:57 +02:00
|
|
|
return nil, errors.Wrap(err, "unable to get table names")
|
2016-04-03 09:15:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var tables []Table
|
|
|
|
for _, name := range names {
|
|
|
|
t := Table{Name: name}
|
|
|
|
|
|
|
|
if t.Columns, err = db.Columns(name); err != nil {
|
2016-06-13 00:34:57 +02:00
|
|
|
return nil, errors.Wrapf(err, "unable to fetch table column info (%s)", name)
|
2016-04-03 09:15:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for i, c := range t.Columns {
|
|
|
|
t.Columns[i] = db.TranslateColumnType(c)
|
|
|
|
}
|
|
|
|
|
|
|
|
if t.PKey, err = db.PrimaryKeyInfo(name); err != nil {
|
2016-06-13 00:34:57 +02:00
|
|
|
return nil, errors.Wrapf(err, "unable to fetch table pkey info (%s)", name)
|
2016-04-03 09:15:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if t.FKeys, err = db.ForeignKeyInfo(name); err != nil {
|
2016-06-13 00:34:57 +02:00
|
|
|
return nil, errors.Wrapf(err, "unable to fetch table fkey info (%s)", name)
|
2016-04-03 09:15:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
setIsJoinTable(&t)
|
2016-07-09 18:53:21 +02:00
|
|
|
setForeignKeyNullability(&t)
|
2016-04-03 09:15:35 +02:00
|
|
|
|
|
|
|
tables = append(tables, t)
|
|
|
|
}
|
|
|
|
|
|
|
|
return tables, nil
|
|
|
|
}
|
|
|
|
|
2016-04-03 10:02:41 +02:00
|
|
|
// setIsJoinTable iff there are:
|
2016-06-23 08:09:56 +02:00
|
|
|
// A composite primary key involving two columns
|
2016-04-03 10:02:41 +02:00
|
|
|
// Both primary key columns are also foreign keys
|
2016-04-03 09:15:35 +02:00
|
|
|
func setIsJoinTable(t *Table) {
|
2016-04-03 10:02:41 +02:00
|
|
|
if t.PKey == nil || len(t.PKey.Columns) != 2 || len(t.FKeys) < 2 {
|
2016-04-03 09:15:35 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range t.PKey.Columns {
|
|
|
|
found := false
|
|
|
|
for _, f := range t.FKeys {
|
|
|
|
if c == f.Column {
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
t.IsJoinTable = true
|
|
|
|
}
|
2016-07-09 18:53:21 +02:00
|
|
|
|
|
|
|
func setForeignKeyNullability(t *Table) {
|
|
|
|
for i, fkey := range t.FKeys {
|
|
|
|
|
|
|
|
found := -1
|
|
|
|
for j, col := range t.Columns {
|
|
|
|
if col.Name == fkey.Column {
|
|
|
|
found = j
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if found < 0 {
|
|
|
|
panic("could not find foreign key column in table")
|
|
|
|
}
|
|
|
|
|
2016-07-09 19:13:35 +02:00
|
|
|
t.FKeys[i].Nullable = t.Columns[found].Nullable
|
2016-07-09 18:53:21 +02:00
|
|
|
}
|
|
|
|
}
|