mirror of https://github.com/dexidp/dex.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
6.7 KiB
247 lines
6.7 KiB
// Copyright 2012 James Cooper. All rights reserved. |
|
// Use of this source code is governed by a MIT-style |
|
// license that can be found in the LICENSE file. |
|
|
|
// Package gorp provides a simple way to marshal Go structs to and from |
|
// SQL databases. It uses the database/sql package, and should work with any |
|
// compliant database/sql driver. |
|
// |
|
// Source code and project home: |
|
// https://github.com/go-gorp/gorp |
|
// |
|
package gorp |
|
|
|
import ( |
|
"bytes" |
|
"fmt" |
|
"reflect" |
|
"strings" |
|
) |
|
|
|
// TableMap represents a mapping between a Go struct and a database table |
|
// Use dbmap.AddTable() or dbmap.AddTableWithName() to create these |
|
type TableMap struct { |
|
// Name of database table. |
|
TableName string |
|
SchemaName string |
|
gotype reflect.Type |
|
Columns []*ColumnMap |
|
keys []*ColumnMap |
|
indexes []*IndexMap |
|
uniqueTogether [][]string |
|
version *ColumnMap |
|
insertPlan bindPlan |
|
updatePlan bindPlan |
|
deletePlan bindPlan |
|
getPlan bindPlan |
|
dbmap *DbMap |
|
} |
|
|
|
// ResetSql removes cached insert/update/select/delete SQL strings |
|
// associated with this TableMap. Call this if you've modified |
|
// any column names or the table name itself. |
|
func (t *TableMap) ResetSql() { |
|
t.insertPlan = bindPlan{} |
|
t.updatePlan = bindPlan{} |
|
t.deletePlan = bindPlan{} |
|
t.getPlan = bindPlan{} |
|
} |
|
|
|
// SetKeys lets you specify the fields on a struct that map to primary |
|
// key columns on the table. If isAutoIncr is set, result.LastInsertId() |
|
// will be used after INSERT to bind the generated id to the Go struct. |
|
// |
|
// Automatically calls ResetSql() to ensure SQL statements are regenerated. |
|
// |
|
// Panics if isAutoIncr is true, and fieldNames length != 1 |
|
// |
|
func (t *TableMap) SetKeys(isAutoIncr bool, fieldNames ...string) *TableMap { |
|
if isAutoIncr && len(fieldNames) != 1 { |
|
panic(fmt.Sprintf( |
|
"gorp: SetKeys: fieldNames length must be 1 if key is auto-increment. (Saw %v fieldNames)", |
|
len(fieldNames))) |
|
} |
|
t.keys = make([]*ColumnMap, 0) |
|
for _, name := range fieldNames { |
|
colmap := t.ColMap(name) |
|
colmap.isPK = true |
|
colmap.isAutoIncr = isAutoIncr |
|
t.keys = append(t.keys, colmap) |
|
} |
|
t.ResetSql() |
|
|
|
return t |
|
} |
|
|
|
// SetUniqueTogether lets you specify uniqueness constraints across multiple |
|
// columns on the table. Each call adds an additional constraint for the |
|
// specified columns. |
|
// |
|
// Automatically calls ResetSql() to ensure SQL statements are regenerated. |
|
// |
|
// Panics if fieldNames length < 2. |
|
// |
|
func (t *TableMap) SetUniqueTogether(fieldNames ...string) *TableMap { |
|
if len(fieldNames) < 2 { |
|
panic(fmt.Sprintf( |
|
"gorp: SetUniqueTogether: must provide at least two fieldNames to set uniqueness constraint.")) |
|
} |
|
|
|
columns := make([]string, 0) |
|
for _, name := range fieldNames { |
|
columns = append(columns, name) |
|
} |
|
t.uniqueTogether = append(t.uniqueTogether, columns) |
|
t.ResetSql() |
|
|
|
return t |
|
} |
|
|
|
// ColMap returns the ColumnMap pointer matching the given struct field |
|
// name. It panics if the struct does not contain a field matching this |
|
// name. |
|
func (t *TableMap) ColMap(field string) *ColumnMap { |
|
col := colMapOrNil(t, field) |
|
if col == nil { |
|
e := fmt.Sprintf("No ColumnMap in table %s type %s with field %s", |
|
t.TableName, t.gotype.Name(), field) |
|
|
|
panic(e) |
|
} |
|
return col |
|
} |
|
|
|
func colMapOrNil(t *TableMap, field string) *ColumnMap { |
|
for _, col := range t.Columns { |
|
if col.fieldName == field || col.ColumnName == field { |
|
return col |
|
} |
|
} |
|
return nil |
|
} |
|
|
|
// IdxMap returns the IndexMap pointer matching the given index name. |
|
func (t *TableMap) IdxMap(field string) *IndexMap { |
|
for _, idx := range t.indexes { |
|
if idx.IndexName == field { |
|
return idx |
|
} |
|
} |
|
return nil |
|
} |
|
|
|
// AddIndex registers the index with gorp for specified table with given parameters. |
|
// This operation is idempotent. If index is already mapped, the |
|
// existing *IndexMap is returned |
|
// Function will panic if one of the given for index columns does not exists |
|
// |
|
// Automatically calls ResetSql() to ensure SQL statements are regenerated. |
|
// |
|
func (t *TableMap) AddIndex(name string, idxtype string, columns []string) *IndexMap { |
|
// check if we have a index with this name already |
|
for _, idx := range t.indexes { |
|
if idx.IndexName == name { |
|
return idx |
|
} |
|
} |
|
for _, icol := range columns { |
|
if res := t.ColMap(icol); res == nil { |
|
e := fmt.Sprintf("No ColumnName in table %s to create index on", t.TableName) |
|
panic(e) |
|
} |
|
} |
|
|
|
idx := &IndexMap{IndexName: name, Unique: false, IndexType: idxtype, columns: columns} |
|
t.indexes = append(t.indexes, idx) |
|
t.ResetSql() |
|
return idx |
|
} |
|
|
|
// SetVersionCol sets the column to use as the Version field. By default |
|
// the "Version" field is used. Returns the column found, or panics |
|
// if the struct does not contain a field matching this name. |
|
// |
|
// Automatically calls ResetSql() to ensure SQL statements are regenerated. |
|
func (t *TableMap) SetVersionCol(field string) *ColumnMap { |
|
c := t.ColMap(field) |
|
t.version = c |
|
t.ResetSql() |
|
return c |
|
} |
|
|
|
// SqlForCreateTable gets a sequence of SQL commands that will create |
|
// the specified table and any associated schema |
|
func (t *TableMap) SqlForCreate(ifNotExists bool) string { |
|
s := bytes.Buffer{} |
|
dialect := t.dbmap.Dialect |
|
|
|
if strings.TrimSpace(t.SchemaName) != "" { |
|
schemaCreate := "create schema" |
|
if ifNotExists { |
|
s.WriteString(dialect.IfSchemaNotExists(schemaCreate, t.SchemaName)) |
|
} else { |
|
s.WriteString(schemaCreate) |
|
} |
|
s.WriteString(fmt.Sprintf(" %s;", t.SchemaName)) |
|
} |
|
|
|
tableCreate := "create table" |
|
if ifNotExists { |
|
s.WriteString(dialect.IfTableNotExists(tableCreate, t.SchemaName, t.TableName)) |
|
} else { |
|
s.WriteString(tableCreate) |
|
} |
|
s.WriteString(fmt.Sprintf(" %s (", dialect.QuotedTableForQuery(t.SchemaName, t.TableName))) |
|
|
|
x := 0 |
|
for _, col := range t.Columns { |
|
if !col.Transient { |
|
if x > 0 { |
|
s.WriteString(", ") |
|
} |
|
stype := dialect.ToSqlType(col.gotype, col.MaxSize, col.isAutoIncr) |
|
s.WriteString(fmt.Sprintf("%s %s", dialect.QuoteField(col.ColumnName), stype)) |
|
|
|
if col.isPK || col.isNotNull { |
|
s.WriteString(" not null") |
|
} |
|
if col.isPK && len(t.keys) == 1 { |
|
s.WriteString(" primary key") |
|
} |
|
if col.Unique { |
|
s.WriteString(" unique") |
|
} |
|
if col.isAutoIncr { |
|
s.WriteString(fmt.Sprintf(" %s", dialect.AutoIncrStr())) |
|
} |
|
|
|
x++ |
|
} |
|
} |
|
if len(t.keys) > 1 { |
|
s.WriteString(", primary key (") |
|
for x := range t.keys { |
|
if x > 0 { |
|
s.WriteString(", ") |
|
} |
|
s.WriteString(dialect.QuoteField(t.keys[x].ColumnName)) |
|
} |
|
s.WriteString(")") |
|
} |
|
if len(t.uniqueTogether) > 0 { |
|
for _, columns := range t.uniqueTogether { |
|
s.WriteString(", unique (") |
|
for i, column := range columns { |
|
if i > 0 { |
|
s.WriteString(", ") |
|
} |
|
s.WriteString(dialect.QuoteField(column)) |
|
} |
|
s.WriteString(")") |
|
} |
|
} |
|
s.WriteString(") ") |
|
s.WriteString(dialect.CreateTableSuffix()) |
|
s.WriteString(dialect.QuerySuffix()) |
|
return s.String() |
|
}
|
|
|