forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/go-gorp/gorp/v3/db.go

1730 lines
28 KiB
Go
Raw Normal View History

2024-02-18 10:42:21 +00:00
// Copyright 2012 James Cooper. All rights reserved.
2024-02-18 10:42:21 +00:00
// Use of this source code is governed by a MIT-style
2024-02-18 10:42:21 +00:00
// license that can be found in the LICENSE file.
package gorp
import (
"bytes"
"context"
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"log"
"reflect"
"strconv"
"strings"
"time"
)
// DbMap is the root gorp mapping object. Create one of these for each
2024-02-18 10:42:21 +00:00
// database schema you wish to map. Each DbMap contains a list of
2024-02-18 10:42:21 +00:00
// mapped tables.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Example:
2024-02-18 10:42:21 +00:00
//
2024-06-14 08:41:36 +00:00
// dialect := gorp.MySQLDialect{"InnoDB", "UTF8"}
2024-06-14 08:41:36 +00:00
// dbmap := &gorp.DbMap{Db: db, Dialect: dialect}
2024-02-18 10:42:21 +00:00
type DbMap struct {
ctx context.Context
// Db handle to use with this map
2024-02-18 10:42:21 +00:00
Db *sql.DB
// Dialect implementation to use with this map
2024-02-18 10:42:21 +00:00
Dialect Dialect
TypeConverter TypeConverter
// ExpandSlices when enabled will convert slice arguments in mappers into flat
2024-02-18 10:42:21 +00:00
// values. It will modify the query, adding more placeholders, and the mapper,
2024-02-18 10:42:21 +00:00
// adding each item of the slice as a new unique entry in the mapper. For
2024-02-18 10:42:21 +00:00
// example, given the scenario bellow:
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// dbmap.Select(&output, "SELECT 1 FROM example WHERE id IN (:IDs)", map[string]interface{}{
2024-02-18 10:42:21 +00:00
// "IDs": []int64{1, 2, 3},
2024-02-18 10:42:21 +00:00
// })
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// The executed query would be:
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// SELECT 1 FROM example WHERE id IN (:IDs0,:IDs1,:IDs2)
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// With the mapper:
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// map[string]interface{}{
2024-02-18 10:42:21 +00:00
// "IDs": []int64{1, 2, 3},
2024-02-18 10:42:21 +00:00
// "IDs0": int64(1),
2024-02-18 10:42:21 +00:00
// "IDs1": int64(2),
2024-02-18 10:42:21 +00:00
// "IDs2": int64(3),
2024-02-18 10:42:21 +00:00
// }
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// It is also flexible for custom slice types. The value just need to
2024-02-18 10:42:21 +00:00
// implement stringer or numberer interfaces.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// type CustomValue string
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// const (
2024-02-18 10:42:21 +00:00
// CustomValueHey CustomValue = "hey"
2024-02-18 10:42:21 +00:00
// CustomValueOh CustomValue = "oh"
2024-02-18 10:42:21 +00:00
// )
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// type CustomValues []CustomValue
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// func (c CustomValues) ToStringSlice() []string {
2024-02-18 10:42:21 +00:00
// values := make([]string, len(c))
2024-02-18 10:42:21 +00:00
// for i := range c {
2024-02-18 10:42:21 +00:00
// values[i] = string(c[i])
2024-02-18 10:42:21 +00:00
// }
2024-02-18 10:42:21 +00:00
// return values
2024-02-18 10:42:21 +00:00
// }
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// func query() {
2024-02-18 10:42:21 +00:00
// // ...
2024-02-18 10:42:21 +00:00
// result, err := dbmap.Select(&output, "SELECT 1 FROM example WHERE value IN (:Values)", map[string]interface{}{
2024-02-18 10:42:21 +00:00
// "Values": CustomValues([]CustomValue{CustomValueHey}),
2024-02-18 10:42:21 +00:00
// })
2024-02-18 10:42:21 +00:00
// // ...
2024-02-18 10:42:21 +00:00
// }
2024-02-18 10:42:21 +00:00
ExpandSliceArgs bool
tables []*TableMap
2024-02-18 10:42:21 +00:00
tablesDynamic map[string]*TableMap // tables that use same go-struct and different db table names
logger GorpLogger
logPrefix string
2024-02-18 10:42:21 +00:00
}
func (m *DbMap) dynamicTableAdd(tableName string, tbl *TableMap) {
2024-02-18 10:42:21 +00:00
if m.tablesDynamic == nil {
2024-02-18 10:42:21 +00:00
m.tablesDynamic = make(map[string]*TableMap)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
m.tablesDynamic[tableName] = tbl
2024-02-18 10:42:21 +00:00
}
func (m *DbMap) dynamicTableFind(tableName string) (*TableMap, bool) {
2024-02-18 10:42:21 +00:00
if m.tablesDynamic == nil {
2024-02-18 10:42:21 +00:00
return nil, false
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
tbl, found := m.tablesDynamic[tableName]
2024-02-18 10:42:21 +00:00
return tbl, found
2024-02-18 10:42:21 +00:00
}
func (m *DbMap) dynamicTableMap() map[string]*TableMap {
2024-02-18 10:42:21 +00:00
if m.tablesDynamic == nil {
2024-02-18 10:42:21 +00:00
m.tablesDynamic = make(map[string]*TableMap)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return m.tablesDynamic
2024-02-18 10:42:21 +00:00
}
func (m *DbMap) WithContext(ctx context.Context) SqlExecutor {
2024-02-18 10:42:21 +00:00
copy := &DbMap{}
2024-02-18 10:42:21 +00:00
*copy = *m
2024-02-18 10:42:21 +00:00
copy.ctx = ctx
2024-02-18 10:42:21 +00:00
return copy
2024-02-18 10:42:21 +00:00
}
func (m *DbMap) CreateIndex() error {
2024-02-18 10:42:21 +00:00
var err error
2024-02-18 10:42:21 +00:00
dialect := reflect.TypeOf(m.Dialect)
2024-02-18 10:42:21 +00:00
for _, table := range m.tables {
2024-02-18 10:42:21 +00:00
for _, index := range table.indexes {
2024-02-18 10:42:21 +00:00
err = m.createIndexImpl(dialect, table, index)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
break
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
for _, table := range m.dynamicTableMap() {
2024-02-18 10:42:21 +00:00
for _, index := range table.indexes {
2024-02-18 10:42:21 +00:00
err = m.createIndexImpl(dialect, table, index)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
break
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
return err
2024-02-18 10:42:21 +00:00
}
func (m *DbMap) createIndexImpl(dialect reflect.Type,
2024-02-18 10:42:21 +00:00
table *TableMap,
2024-02-18 10:42:21 +00:00
index *IndexMap) error {
2024-02-18 10:42:21 +00:00
s := bytes.Buffer{}
2024-02-18 10:42:21 +00:00
s.WriteString("create")
2024-02-18 10:42:21 +00:00
if index.Unique {
2024-02-18 10:42:21 +00:00
s.WriteString(" unique")
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
s.WriteString(" index")
2024-02-18 10:42:21 +00:00
s.WriteString(fmt.Sprintf(" %s on %s", index.IndexName, table.TableName))
2024-02-18 10:42:21 +00:00
if dname := dialect.Name(); dname == "PostgresDialect" && index.IndexType != "" {
2024-02-18 10:42:21 +00:00
s.WriteString(fmt.Sprintf(" %s %s", m.Dialect.CreateIndexSuffix(), index.IndexType))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
s.WriteString(" (")
2024-02-18 10:42:21 +00:00
for x, col := range index.columns {
2024-02-18 10:42:21 +00:00
if x > 0 {
2024-02-18 10:42:21 +00:00
s.WriteString(", ")
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
s.WriteString(m.Dialect.QuoteField(col))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
s.WriteString(")")
if dname := dialect.Name(); dname == "MySQLDialect" && index.IndexType != "" {
2024-02-18 10:42:21 +00:00
s.WriteString(fmt.Sprintf(" %s %s", m.Dialect.CreateIndexSuffix(), index.IndexType))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
s.WriteString(";")
2024-02-18 10:42:21 +00:00
_, err := m.Exec(s.String())
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
func (t *TableMap) DropIndex(name string) error {
var err error
2024-02-18 10:42:21 +00:00
dialect := reflect.TypeOf(t.dbmap.Dialect)
2024-02-18 10:42:21 +00:00
for _, idx := range t.indexes {
2024-02-18 10:42:21 +00:00
if idx.IndexName == name {
2024-02-18 10:42:21 +00:00
s := bytes.Buffer{}
2024-02-18 10:42:21 +00:00
s.WriteString(fmt.Sprintf("DROP INDEX %s", idx.IndexName))
if dname := dialect.Name(); dname == "MySQLDialect" {
2024-02-18 10:42:21 +00:00
s.WriteString(fmt.Sprintf(" %s %s", t.dbmap.Dialect.DropIndexSuffix(), t.TableName))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
s.WriteString(";")
2024-02-18 10:42:21 +00:00
_, e := t.dbmap.Exec(s.String())
2024-02-18 10:42:21 +00:00
if e != nil {
2024-02-18 10:42:21 +00:00
err = e
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
break
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
t.ResetSql()
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
// AddTable registers the given interface type with gorp. The table name
2024-02-18 10:42:21 +00:00
// will be given the name of the TypeOf(i). You must call this function,
2024-02-18 10:42:21 +00:00
// or AddTableWithName, for any struct type you wish to persist with
2024-02-18 10:42:21 +00:00
// the given DbMap.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// This operation is idempotent. If i's type is already mapped, the
2024-02-18 10:42:21 +00:00
// existing *TableMap is returned
2024-02-18 10:42:21 +00:00
func (m *DbMap) AddTable(i interface{}) *TableMap {
2024-02-18 10:42:21 +00:00
return m.AddTableWithName(i, "")
2024-02-18 10:42:21 +00:00
}
// AddTableWithName has the same behavior as AddTable, but sets
2024-02-18 10:42:21 +00:00
// table.TableName to name.
2024-02-18 10:42:21 +00:00
func (m *DbMap) AddTableWithName(i interface{}, name string) *TableMap {
2024-02-18 10:42:21 +00:00
return m.AddTableWithNameAndSchema(i, "", name)
2024-02-18 10:42:21 +00:00
}
// AddTableWithNameAndSchema has the same behavior as AddTable, but sets
2024-02-18 10:42:21 +00:00
// table.TableName to name.
2024-02-18 10:42:21 +00:00
func (m *DbMap) AddTableWithNameAndSchema(i interface{}, schema string, name string) *TableMap {
2024-02-18 10:42:21 +00:00
t := reflect.TypeOf(i)
2024-02-18 10:42:21 +00:00
if name == "" {
2024-02-18 10:42:21 +00:00
name = t.Name()
2024-02-18 10:42:21 +00:00
}
// check if we have a table for this type already
2024-02-18 10:42:21 +00:00
// if so, update the name and return the existing pointer
2024-02-18 10:42:21 +00:00
for i := range m.tables {
2024-02-18 10:42:21 +00:00
table := m.tables[i]
2024-02-18 10:42:21 +00:00
if table.gotype == t {
2024-02-18 10:42:21 +00:00
table.TableName = name
2024-02-18 10:42:21 +00:00
return table
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
tmap := &TableMap{gotype: t, TableName: name, SchemaName: schema, dbmap: m}
2024-02-18 10:42:21 +00:00
var primaryKey []*ColumnMap
2024-02-18 10:42:21 +00:00
tmap.Columns, primaryKey = m.readStructColumns(t)
2024-02-18 10:42:21 +00:00
m.tables = append(m.tables, tmap)
2024-02-18 10:42:21 +00:00
if len(primaryKey) > 0 {
2024-02-18 10:42:21 +00:00
tmap.keys = append(tmap.keys, primaryKey...)
2024-02-18 10:42:21 +00:00
}
return tmap
2024-02-18 10:42:21 +00:00
}
// AddTableDynamic registers the given interface type with gorp.
2024-02-18 10:42:21 +00:00
// The table name will be dynamically determined at runtime by
2024-02-18 10:42:21 +00:00
// using the GetTableName method on DynamicTable interface
2024-02-18 10:42:21 +00:00
func (m *DbMap) AddTableDynamic(inp DynamicTable, schema string) *TableMap {
val := reflect.ValueOf(inp)
2024-02-18 10:42:21 +00:00
elm := val.Elem()
2024-02-18 10:42:21 +00:00
t := elm.Type()
2024-02-18 10:42:21 +00:00
name := inp.TableName()
2024-02-18 10:42:21 +00:00
if name == "" {
2024-02-18 10:42:21 +00:00
panic("Missing table name in DynamicTable instance")
2024-02-18 10:42:21 +00:00
}
// Check if there is another dynamic table with the same name
2024-02-18 10:42:21 +00:00
if _, found := m.dynamicTableFind(name); found {
2024-02-18 10:42:21 +00:00
panic(fmt.Sprintf("A table with the same name %v already exists", name))
2024-02-18 10:42:21 +00:00
}
tmap := &TableMap{gotype: t, TableName: name, SchemaName: schema, dbmap: m}
2024-02-18 10:42:21 +00:00
var primaryKey []*ColumnMap
2024-02-18 10:42:21 +00:00
tmap.Columns, primaryKey = m.readStructColumns(t)
2024-02-18 10:42:21 +00:00
if len(primaryKey) > 0 {
2024-02-18 10:42:21 +00:00
tmap.keys = append(tmap.keys, primaryKey...)
2024-02-18 10:42:21 +00:00
}
m.dynamicTableAdd(name, tmap)
return tmap
2024-02-18 10:42:21 +00:00
}
func (m *DbMap) readStructColumns(t reflect.Type) (cols []*ColumnMap, primaryKey []*ColumnMap) {
2024-02-18 10:42:21 +00:00
primaryKey = make([]*ColumnMap, 0)
2024-02-18 10:42:21 +00:00
n := t.NumField()
2024-02-18 10:42:21 +00:00
for i := 0; i < n; i++ {
2024-02-18 10:42:21 +00:00
f := t.Field(i)
2024-02-18 10:42:21 +00:00
if f.Anonymous && f.Type.Kind() == reflect.Struct {
2024-02-18 10:42:21 +00:00
// Recursively add nested fields in embedded structs.
2024-02-18 10:42:21 +00:00
subcols, subpk := m.readStructColumns(f.Type)
2024-02-18 10:42:21 +00:00
// Don't append nested fields that have the same field
2024-02-18 10:42:21 +00:00
// name as an already-mapped field.
2024-02-18 10:42:21 +00:00
for _, subcol := range subcols {
2024-02-18 10:42:21 +00:00
shouldAppend := true
2024-02-18 10:42:21 +00:00
for _, col := range cols {
2024-02-18 10:42:21 +00:00
if !subcol.Transient && subcol.fieldName == col.fieldName {
2024-02-18 10:42:21 +00:00
shouldAppend = false
2024-02-18 10:42:21 +00:00
break
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if shouldAppend {
2024-02-18 10:42:21 +00:00
cols = append(cols, subcol)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if subpk != nil {
2024-02-18 10:42:21 +00:00
primaryKey = append(primaryKey, subpk...)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
// Tag = Name { ',' Option }
2024-02-18 10:42:21 +00:00
// Option = OptionKey [ ':' OptionValue ]
2024-02-18 10:42:21 +00:00
cArguments := strings.Split(f.Tag.Get("db"), ",")
2024-02-18 10:42:21 +00:00
columnName := cArguments[0]
2024-02-18 10:42:21 +00:00
var maxSize int
2024-02-18 10:42:21 +00:00
var defaultValue string
2024-02-18 10:42:21 +00:00
var isAuto bool
2024-02-18 10:42:21 +00:00
var isPK bool
2024-02-18 10:42:21 +00:00
var isNotNull bool
2024-02-18 10:42:21 +00:00
for _, argString := range cArguments[1:] {
2024-02-18 10:42:21 +00:00
argString = strings.TrimSpace(argString)
2024-02-18 10:42:21 +00:00
arg := strings.SplitN(argString, ":", 2)
// check mandatory/unexpected option values
2024-02-18 10:42:21 +00:00
switch arg[0] {
2024-02-18 10:42:21 +00:00
case "size", "default":
2024-02-18 10:42:21 +00:00
// options requiring value
2024-02-18 10:42:21 +00:00
if len(arg) == 1 {
2024-02-18 10:42:21 +00:00
panic(fmt.Sprintf("missing option value for option %v on field %v", arg[0], f.Name))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
// options where value is invalid (currently all other options)
2024-02-18 10:42:21 +00:00
if len(arg) == 2 {
2024-02-18 10:42:21 +00:00
panic(fmt.Sprintf("unexpected option value for option %v on field %v", arg[0], f.Name))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
switch arg[0] {
2024-02-18 10:42:21 +00:00
case "size":
2024-02-18 10:42:21 +00:00
maxSize, _ = strconv.Atoi(arg[1])
2024-02-18 10:42:21 +00:00
case "default":
2024-02-18 10:42:21 +00:00
defaultValue = arg[1]
2024-02-18 10:42:21 +00:00
case "primarykey":
2024-02-18 10:42:21 +00:00
isPK = true
2024-02-18 10:42:21 +00:00
case "autoincrement":
2024-02-18 10:42:21 +00:00
isAuto = true
2024-02-18 10:42:21 +00:00
case "notnull":
2024-02-18 10:42:21 +00:00
isNotNull = true
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
panic(fmt.Sprintf("Unrecognized tag option for field %v: %v", f.Name, arg))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if columnName == "" {
2024-02-18 10:42:21 +00:00
columnName = f.Name
2024-02-18 10:42:21 +00:00
}
gotype := f.Type
2024-02-18 10:42:21 +00:00
valueType := gotype
2024-02-18 10:42:21 +00:00
if valueType.Kind() == reflect.Ptr {
2024-02-18 10:42:21 +00:00
valueType = valueType.Elem()
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
value := reflect.New(valueType).Interface()
2024-02-18 10:42:21 +00:00
if m.TypeConverter != nil {
2024-02-18 10:42:21 +00:00
// Make a new pointer to a value of type gotype and
2024-02-18 10:42:21 +00:00
// pass it to the TypeConverter's FromDb method to see
2024-02-18 10:42:21 +00:00
// if a different type should be used for the column
2024-02-18 10:42:21 +00:00
// type during table creation.
2024-02-18 10:42:21 +00:00
scanner, useHolder := m.TypeConverter.FromDb(value)
2024-02-18 10:42:21 +00:00
if useHolder {
2024-02-18 10:42:21 +00:00
value = scanner.Holder
2024-02-18 10:42:21 +00:00
gotype = reflect.TypeOf(value)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if typer, ok := value.(SqlTyper); ok {
2024-02-18 10:42:21 +00:00
gotype = reflect.TypeOf(typer.SqlType())
2024-02-18 10:42:21 +00:00
} else if typer, ok := value.(legacySqlTyper); ok {
2024-02-18 10:42:21 +00:00
log.Printf("Deprecation Warning: update your SqlType methods to return a driver.Value")
2024-02-18 10:42:21 +00:00
gotype = reflect.TypeOf(typer.SqlType())
2024-02-18 10:42:21 +00:00
} else if valuer, ok := value.(driver.Valuer); ok {
2024-02-18 10:42:21 +00:00
// Only check for driver.Valuer if SqlTyper wasn't
2024-02-18 10:42:21 +00:00
// found.
2024-02-18 10:42:21 +00:00
v, err := valuer.Value()
2024-02-18 10:42:21 +00:00
if err == nil && v != nil {
2024-02-18 10:42:21 +00:00
gotype = reflect.TypeOf(v)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
cm := &ColumnMap{
ColumnName: columnName,
2024-02-18 10:42:21 +00:00
DefaultValue: defaultValue,
Transient: columnName == "-",
fieldName: f.Name,
gotype: gotype,
isPK: isPK,
isAutoIncr: isAuto,
isNotNull: isNotNull,
MaxSize: maxSize,
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if isPK {
2024-02-18 10:42:21 +00:00
primaryKey = append(primaryKey, cm)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
// Check for nested fields of the same field name and
2024-02-18 10:42:21 +00:00
// override them.
2024-02-18 10:42:21 +00:00
shouldAppend := true
2024-02-18 10:42:21 +00:00
for index, col := range cols {
2024-02-18 10:42:21 +00:00
if !col.Transient && col.fieldName == cm.fieldName {
2024-02-18 10:42:21 +00:00
cols[index] = cm
2024-02-18 10:42:21 +00:00
shouldAppend = false
2024-02-18 10:42:21 +00:00
break
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if shouldAppend {
2024-02-18 10:42:21 +00:00
cols = append(cols, cm)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
}
2024-02-18 10:42:21 +00:00
return
2024-02-18 10:42:21 +00:00
}
// CreateTables iterates through TableMaps registered to this DbMap and
2024-02-18 10:42:21 +00:00
// executes "create table" statements against the database for each.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// This is particularly useful in unit tests where you want to create
2024-02-18 10:42:21 +00:00
// and destroy the schema automatically.
2024-02-18 10:42:21 +00:00
func (m *DbMap) CreateTables() error {
2024-02-18 10:42:21 +00:00
return m.createTables(false)
2024-02-18 10:42:21 +00:00
}
// CreateTablesIfNotExists is similar to CreateTables, but starts
2024-02-18 10:42:21 +00:00
// each statement with "create table if not exists" so that existing
2024-02-18 10:42:21 +00:00
// tables do not raise errors
2024-02-18 10:42:21 +00:00
func (m *DbMap) CreateTablesIfNotExists() error {
2024-02-18 10:42:21 +00:00
return m.createTables(true)
2024-02-18 10:42:21 +00:00
}
func (m *DbMap) createTables(ifNotExists bool) error {
2024-02-18 10:42:21 +00:00
var err error
2024-02-18 10:42:21 +00:00
for i := range m.tables {
2024-02-18 10:42:21 +00:00
table := m.tables[i]
2024-02-18 10:42:21 +00:00
sql := table.SqlForCreate(ifNotExists)
2024-02-18 10:42:21 +00:00
_, err = m.Exec(sql)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
for _, tbl := range m.dynamicTableMap() {
2024-02-18 10:42:21 +00:00
sql := tbl.SqlForCreate(ifNotExists)
2024-02-18 10:42:21 +00:00
_, err = m.Exec(sql)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
return err
2024-02-18 10:42:21 +00:00
}
// DropTable drops an individual table.
2024-02-18 10:42:21 +00:00
// Returns an error when the table does not exist.
2024-02-18 10:42:21 +00:00
func (m *DbMap) DropTable(table interface{}) error {
2024-02-18 10:42:21 +00:00
t := reflect.TypeOf(table)
tableName := ""
2024-02-18 10:42:21 +00:00
if dyn, ok := table.(DynamicTable); ok {
2024-02-18 10:42:21 +00:00
tableName = dyn.TableName()
2024-02-18 10:42:21 +00:00
}
return m.dropTable(t, tableName, false)
2024-02-18 10:42:21 +00:00
}
// DropTableIfExists drops an individual table when the table exists.
2024-02-18 10:42:21 +00:00
func (m *DbMap) DropTableIfExists(table interface{}) error {
2024-02-18 10:42:21 +00:00
t := reflect.TypeOf(table)
tableName := ""
2024-02-18 10:42:21 +00:00
if dyn, ok := table.(DynamicTable); ok {
2024-02-18 10:42:21 +00:00
tableName = dyn.TableName()
2024-02-18 10:42:21 +00:00
}
return m.dropTable(t, tableName, true)
2024-02-18 10:42:21 +00:00
}
// DropTables iterates through TableMaps registered to this DbMap and
2024-02-18 10:42:21 +00:00
// executes "drop table" statements against the database for each.
2024-02-18 10:42:21 +00:00
func (m *DbMap) DropTables() error {
2024-02-18 10:42:21 +00:00
return m.dropTables(false)
2024-02-18 10:42:21 +00:00
}
// DropTablesIfExists is the same as DropTables, but uses the "if exists" clause to
2024-02-18 10:42:21 +00:00
// avoid errors for tables that do not exist.
2024-02-18 10:42:21 +00:00
func (m *DbMap) DropTablesIfExists() error {
2024-02-18 10:42:21 +00:00
return m.dropTables(true)
2024-02-18 10:42:21 +00:00
}
// Goes through all the registered tables, dropping them one by one.
2024-02-18 10:42:21 +00:00
// If an error is encountered, then it is returned and the rest of
2024-02-18 10:42:21 +00:00
// the tables are not dropped.
2024-02-18 10:42:21 +00:00
func (m *DbMap) dropTables(addIfExists bool) (err error) {
2024-02-18 10:42:21 +00:00
for _, table := range m.tables {
2024-02-18 10:42:21 +00:00
err = m.dropTableImpl(table, addIfExists)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
for _, table := range m.dynamicTableMap() {
2024-02-18 10:42:21 +00:00
err = m.dropTableImpl(table, addIfExists)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
return err
2024-02-18 10:42:21 +00:00
}
// Implementation of dropping a single table.
2024-02-18 10:42:21 +00:00
func (m *DbMap) dropTable(t reflect.Type, name string, addIfExists bool) error {
2024-02-18 10:42:21 +00:00
table := tableOrNil(m, t, name)
2024-02-18 10:42:21 +00:00
if table == nil {
2024-02-18 10:42:21 +00:00
return fmt.Errorf("table %s was not registered", table.TableName)
2024-02-18 10:42:21 +00:00
}
return m.dropTableImpl(table, addIfExists)
2024-02-18 10:42:21 +00:00
}
func (m *DbMap) dropTableImpl(table *TableMap, ifExists bool) (err error) {
2024-02-18 10:42:21 +00:00
tableDrop := "drop table"
2024-02-18 10:42:21 +00:00
if ifExists {
2024-02-18 10:42:21 +00:00
tableDrop = m.Dialect.IfTableExists(tableDrop, table.SchemaName, table.TableName)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
_, err = m.Exec(fmt.Sprintf("%s %s;", tableDrop, m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName)))
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
// TruncateTables iterates through TableMaps registered to this DbMap and
2024-02-18 10:42:21 +00:00
// executes "truncate table" statements against the database for each, or in the case of
2024-02-18 10:42:21 +00:00
// sqlite, a "delete from" with no "where" clause, which uses the truncate optimization
2024-02-18 10:42:21 +00:00
// (http://www.sqlite.org/lang_delete.html)
2024-02-18 10:42:21 +00:00
func (m *DbMap) TruncateTables() error {
2024-02-18 10:42:21 +00:00
var err error
2024-02-18 10:42:21 +00:00
for i := range m.tables {
2024-02-18 10:42:21 +00:00
table := m.tables[i]
2024-02-18 10:42:21 +00:00
_, e := m.Exec(fmt.Sprintf("%s %s;", m.Dialect.TruncateClause(), m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName)))
2024-02-18 10:42:21 +00:00
if e != nil {
2024-02-18 10:42:21 +00:00
err = e
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
for _, table := range m.dynamicTableMap() {
2024-02-18 10:42:21 +00:00
_, e := m.Exec(fmt.Sprintf("%s %s;", m.Dialect.TruncateClause(), m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName)))
2024-02-18 10:42:21 +00:00
if e != nil {
2024-02-18 10:42:21 +00:00
err = e
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
return err
2024-02-18 10:42:21 +00:00
}
// Insert runs a SQL INSERT statement for each element in list. List
2024-02-18 10:42:21 +00:00
// items must be pointers.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Any interface whose TableMap has an auto-increment primary key will
2024-02-18 10:42:21 +00:00
// have its last insert id bound to the PK field on the struct.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// The hook functions PreInsert() and/or PostInsert() will be executed
2024-02-18 10:42:21 +00:00
// before/after the INSERT statement if the interface defines them.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Panics if any interface in the list has not been registered with AddTable
2024-02-18 10:42:21 +00:00
func (m *DbMap) Insert(list ...interface{}) error {
2024-02-18 10:42:21 +00:00
return insert(m, m, list...)
2024-02-18 10:42:21 +00:00
}
// Update runs a SQL UPDATE statement for each element in list. List
2024-02-18 10:42:21 +00:00
// items must be pointers.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// The hook functions PreUpdate() and/or PostUpdate() will be executed
2024-02-18 10:42:21 +00:00
// before/after the UPDATE statement if the interface defines them.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Returns the number of rows updated.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Returns an error if SetKeys has not been called on the TableMap
2024-02-18 10:42:21 +00:00
// Panics if any interface in the list has not been registered with AddTable
2024-02-18 10:42:21 +00:00
func (m *DbMap) Update(list ...interface{}) (int64, error) {
2024-02-18 10:42:21 +00:00
return update(m, m, nil, list...)
2024-02-18 10:42:21 +00:00
}
// UpdateColumns runs a SQL UPDATE statement for each element in list. List
2024-02-18 10:42:21 +00:00
// items must be pointers.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Only the columns accepted by filter are included in the UPDATE.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// The hook functions PreUpdate() and/or PostUpdate() will be executed
2024-02-18 10:42:21 +00:00
// before/after the UPDATE statement if the interface defines them.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Returns the number of rows updated.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Returns an error if SetKeys has not been called on the TableMap
2024-02-18 10:42:21 +00:00
// Panics if any interface in the list has not been registered with AddTable
2024-02-18 10:42:21 +00:00
func (m *DbMap) UpdateColumns(filter ColumnFilter, list ...interface{}) (int64, error) {
2024-02-18 10:42:21 +00:00
return update(m, m, filter, list...)
2024-02-18 10:42:21 +00:00
}
// Delete runs a SQL DELETE statement for each element in list. List
2024-02-18 10:42:21 +00:00
// items must be pointers.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// The hook functions PreDelete() and/or PostDelete() will be executed
2024-02-18 10:42:21 +00:00
// before/after the DELETE statement if the interface defines them.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Returns the number of rows deleted.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Returns an error if SetKeys has not been called on the TableMap
2024-02-18 10:42:21 +00:00
// Panics if any interface in the list has not been registered with AddTable
2024-02-18 10:42:21 +00:00
func (m *DbMap) Delete(list ...interface{}) (int64, error) {
2024-02-18 10:42:21 +00:00
return delete(m, m, list...)
2024-02-18 10:42:21 +00:00
}
// Get runs a SQL SELECT to fetch a single row from the table based on the
2024-02-18 10:42:21 +00:00
// primary key(s)
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// i should be an empty value for the struct to load. keys should be
2024-02-18 10:42:21 +00:00
// the primary key value(s) for the row to load. If multiple keys
2024-02-18 10:42:21 +00:00
// exist on the table, the order should match the column order
2024-02-18 10:42:21 +00:00
// specified in SetKeys() when the table mapping was defined.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// The hook function PostGet() will be executed after the SELECT
2024-02-18 10:42:21 +00:00
// statement if the interface defines them.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Returns a pointer to a struct that matches or nil if no row is found.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Returns an error if SetKeys has not been called on the TableMap
2024-02-18 10:42:21 +00:00
// Panics if any interface in the list has not been registered with AddTable
2024-02-18 10:42:21 +00:00
func (m *DbMap) Get(i interface{}, keys ...interface{}) (interface{}, error) {
2024-02-18 10:42:21 +00:00
return get(m, m, i, keys...)
2024-02-18 10:42:21 +00:00
}
// Select runs an arbitrary SQL query, binding the columns in the result
2024-02-18 10:42:21 +00:00
// to fields on the struct specified by i. args represent the bind
2024-02-18 10:42:21 +00:00
// parameters for the SQL statement.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Column names on the SELECT statement should be aliased to the field names
2024-02-18 10:42:21 +00:00
// on the struct i. Returns an error if one or more columns in the result
2024-02-18 10:42:21 +00:00
// do not match. It is OK if fields on i are not part of the SQL
2024-02-18 10:42:21 +00:00
// statement.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// The hook function PostGet() will be executed after the SELECT
2024-02-18 10:42:21 +00:00
// statement if the interface defines them.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Values are returned in one of two ways:
2024-02-18 10:42:21 +00:00
// 1. If i is a struct or a pointer to a struct, returns a slice of pointers to
2024-02-18 10:42:21 +00:00
// matching rows of type i.
2024-02-18 10:42:21 +00:00
// 2. If i is a pointer to a slice, the results will be appended to that slice
2024-02-18 10:42:21 +00:00
// and nil returned.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// i does NOT need to be registered with AddTable()
2024-02-18 10:42:21 +00:00
func (m *DbMap) Select(i interface{}, query string, args ...interface{}) ([]interface{}, error) {
2024-02-18 10:42:21 +00:00
if m.ExpandSliceArgs {
2024-02-18 10:42:21 +00:00
expandSliceArgs(&query, args...)
2024-02-18 10:42:21 +00:00
}
return hookedselect(m, m, i, query, args...)
2024-02-18 10:42:21 +00:00
}
// Exec runs an arbitrary SQL statement. args represent the bind parameters.
2024-02-18 10:42:21 +00:00
// This is equivalent to running: Exec() using database/sql
2024-02-18 10:42:21 +00:00
func (m *DbMap) Exec(query string, args ...interface{}) (sql.Result, error) {
2024-02-18 10:42:21 +00:00
if m.ExpandSliceArgs {
2024-02-18 10:42:21 +00:00
expandSliceArgs(&query, args...)
2024-02-18 10:42:21 +00:00
}
if m.logger != nil {
2024-02-18 10:42:21 +00:00
now := time.Now()
2024-02-18 10:42:21 +00:00
defer m.trace(now, query, args...)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return maybeExpandNamedQueryAndExec(m, query, args...)
2024-02-18 10:42:21 +00:00
}
// SelectInt is a convenience wrapper around the gorp.SelectInt function
2024-02-18 10:42:21 +00:00
func (m *DbMap) SelectInt(query string, args ...interface{}) (int64, error) {
2024-02-18 10:42:21 +00:00
if m.ExpandSliceArgs {
2024-02-18 10:42:21 +00:00
expandSliceArgs(&query, args...)
2024-02-18 10:42:21 +00:00
}
return SelectInt(m, query, args...)
2024-02-18 10:42:21 +00:00
}
// SelectNullInt is a convenience wrapper around the gorp.SelectNullInt function
2024-02-18 10:42:21 +00:00
func (m *DbMap) SelectNullInt(query string, args ...interface{}) (sql.NullInt64, error) {
2024-02-18 10:42:21 +00:00
if m.ExpandSliceArgs {
2024-02-18 10:42:21 +00:00
expandSliceArgs(&query, args...)
2024-02-18 10:42:21 +00:00
}
return SelectNullInt(m, query, args...)
2024-02-18 10:42:21 +00:00
}
// SelectFloat is a convenience wrapper around the gorp.SelectFloat function
2024-02-18 10:42:21 +00:00
func (m *DbMap) SelectFloat(query string, args ...interface{}) (float64, error) {
2024-02-18 10:42:21 +00:00
if m.ExpandSliceArgs {
2024-02-18 10:42:21 +00:00
expandSliceArgs(&query, args...)
2024-02-18 10:42:21 +00:00
}
return SelectFloat(m, query, args...)
2024-02-18 10:42:21 +00:00
}
// SelectNullFloat is a convenience wrapper around the gorp.SelectNullFloat function
2024-02-18 10:42:21 +00:00
func (m *DbMap) SelectNullFloat(query string, args ...interface{}) (sql.NullFloat64, error) {
2024-02-18 10:42:21 +00:00
if m.ExpandSliceArgs {
2024-02-18 10:42:21 +00:00
expandSliceArgs(&query, args...)
2024-02-18 10:42:21 +00:00
}
return SelectNullFloat(m, query, args...)
2024-02-18 10:42:21 +00:00
}
// SelectStr is a convenience wrapper around the gorp.SelectStr function
2024-02-18 10:42:21 +00:00
func (m *DbMap) SelectStr(query string, args ...interface{}) (string, error) {
2024-02-18 10:42:21 +00:00
if m.ExpandSliceArgs {
2024-02-18 10:42:21 +00:00
expandSliceArgs(&query, args...)
2024-02-18 10:42:21 +00:00
}
return SelectStr(m, query, args...)
2024-02-18 10:42:21 +00:00
}
// SelectNullStr is a convenience wrapper around the gorp.SelectNullStr function
2024-02-18 10:42:21 +00:00
func (m *DbMap) SelectNullStr(query string, args ...interface{}) (sql.NullString, error) {
2024-02-18 10:42:21 +00:00
if m.ExpandSliceArgs {
2024-02-18 10:42:21 +00:00
expandSliceArgs(&query, args...)
2024-02-18 10:42:21 +00:00
}
return SelectNullStr(m, query, args...)
2024-02-18 10:42:21 +00:00
}
// SelectOne is a convenience wrapper around the gorp.SelectOne function
2024-02-18 10:42:21 +00:00
func (m *DbMap) SelectOne(holder interface{}, query string, args ...interface{}) error {
2024-02-18 10:42:21 +00:00
if m.ExpandSliceArgs {
2024-02-18 10:42:21 +00:00
expandSliceArgs(&query, args...)
2024-02-18 10:42:21 +00:00
}
return SelectOne(m, m, holder, query, args...)
2024-02-18 10:42:21 +00:00
}
// Begin starts a gorp Transaction
2024-02-18 10:42:21 +00:00
func (m *DbMap) Begin() (*Transaction, error) {
2024-02-18 10:42:21 +00:00
if m.logger != nil {
2024-02-18 10:42:21 +00:00
now := time.Now()
2024-02-18 10:42:21 +00:00
defer m.trace(now, "begin;")
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
tx, err := begin(m)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return &Transaction{
dbmap: m,
tx: tx,
2024-02-18 10:42:21 +00:00
closed: false,
}, nil
2024-02-18 10:42:21 +00:00
}
// TableFor returns the *TableMap corresponding to the given Go Type
2024-02-18 10:42:21 +00:00
// If no table is mapped to that type an error is returned.
2024-02-18 10:42:21 +00:00
// If checkPK is true and the mapped table has no registered PKs, an error is returned.
2024-02-18 10:42:21 +00:00
func (m *DbMap) TableFor(t reflect.Type, checkPK bool) (*TableMap, error) {
2024-02-18 10:42:21 +00:00
table := tableOrNil(m, t, "")
2024-02-18 10:42:21 +00:00
if table == nil {
2024-02-18 10:42:21 +00:00
return nil, fmt.Errorf("no table found for type: %v", t.Name())
2024-02-18 10:42:21 +00:00
}
if checkPK && len(table.keys) < 1 {
2024-02-18 10:42:21 +00:00
e := fmt.Sprintf("gorp: no keys defined for table: %s",
2024-02-18 10:42:21 +00:00
table.TableName)
2024-02-18 10:42:21 +00:00
return nil, errors.New(e)
2024-02-18 10:42:21 +00:00
}
return table, nil
2024-02-18 10:42:21 +00:00
}
// DynamicTableFor returns the *TableMap for the dynamic table corresponding
2024-02-18 10:42:21 +00:00
// to the input tablename
2024-02-18 10:42:21 +00:00
// If no table is mapped to that tablename an error is returned.
2024-02-18 10:42:21 +00:00
// If checkPK is true and the mapped table has no registered PKs, an error is returned.
2024-02-18 10:42:21 +00:00
func (m *DbMap) DynamicTableFor(tableName string, checkPK bool) (*TableMap, error) {
2024-02-18 10:42:21 +00:00
table, found := m.dynamicTableFind(tableName)
2024-02-18 10:42:21 +00:00
if !found {
2024-02-18 10:42:21 +00:00
return nil, fmt.Errorf("gorp: no table found for name: %v", tableName)
2024-02-18 10:42:21 +00:00
}
if checkPK && len(table.keys) < 1 {
2024-02-18 10:42:21 +00:00
e := fmt.Sprintf("gorp: no keys defined for table: %s",
2024-02-18 10:42:21 +00:00
table.TableName)
2024-02-18 10:42:21 +00:00
return nil, errors.New(e)
2024-02-18 10:42:21 +00:00
}
return table, nil
2024-02-18 10:42:21 +00:00
}
// Prepare creates a prepared statement for later queries or executions.
2024-02-18 10:42:21 +00:00
// Multiple queries or executions may be run concurrently from the returned statement.
2024-02-18 10:42:21 +00:00
// This is equivalent to running: Prepare() using database/sql
2024-02-18 10:42:21 +00:00
func (m *DbMap) Prepare(query string) (*sql.Stmt, error) {
2024-02-18 10:42:21 +00:00
if m.logger != nil {
2024-02-18 10:42:21 +00:00
now := time.Now()
2024-02-18 10:42:21 +00:00
defer m.trace(now, query, nil)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return prepare(m, query)
2024-02-18 10:42:21 +00:00
}
func tableOrNil(m *DbMap, t reflect.Type, name string) *TableMap {
2024-02-18 10:42:21 +00:00
if name != "" {
2024-02-18 10:42:21 +00:00
// Search by table name (dynamic tables)
2024-02-18 10:42:21 +00:00
if table, found := m.dynamicTableFind(name); found {
2024-02-18 10:42:21 +00:00
return table
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
for i := range m.tables {
2024-02-18 10:42:21 +00:00
table := m.tables[i]
2024-02-18 10:42:21 +00:00
if table.gotype == t {
2024-02-18 10:42:21 +00:00
return table
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
func (m *DbMap) tableForPointer(ptr interface{}, checkPK bool) (*TableMap, reflect.Value, error) {
2024-02-18 10:42:21 +00:00
ptrv := reflect.ValueOf(ptr)
2024-02-18 10:42:21 +00:00
if ptrv.Kind() != reflect.Ptr {
2024-02-18 10:42:21 +00:00
e := fmt.Sprintf("gorp: passed non-pointer: %v (kind=%v)", ptr,
2024-02-18 10:42:21 +00:00
ptrv.Kind())
2024-02-18 10:42:21 +00:00
return nil, reflect.Value{}, errors.New(e)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
elem := ptrv.Elem()
2024-02-18 10:42:21 +00:00
ifc := elem.Interface()
2024-02-18 10:42:21 +00:00
var t *TableMap
2024-02-18 10:42:21 +00:00
var err error
2024-02-18 10:42:21 +00:00
tableName := ""
2024-02-18 10:42:21 +00:00
if dyn, isDyn := ptr.(DynamicTable); isDyn {
2024-02-18 10:42:21 +00:00
tableName = dyn.TableName()
2024-02-18 10:42:21 +00:00
t, err = m.DynamicTableFor(tableName, checkPK)
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
etype := reflect.TypeOf(ifc)
2024-02-18 10:42:21 +00:00
t, err = m.TableFor(etype, checkPK)
2024-02-18 10:42:21 +00:00
}
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, reflect.Value{}, err
2024-02-18 10:42:21 +00:00
}
return t, elem, nil
2024-02-18 10:42:21 +00:00
}
func (m *DbMap) QueryRow(query string, args ...interface{}) *sql.Row {
2024-02-18 10:42:21 +00:00
if m.ExpandSliceArgs {
2024-02-18 10:42:21 +00:00
expandSliceArgs(&query, args...)
2024-02-18 10:42:21 +00:00
}
if m.logger != nil {
2024-02-18 10:42:21 +00:00
now := time.Now()
2024-02-18 10:42:21 +00:00
defer m.trace(now, query, args...)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return queryRow(m, query, args...)
2024-02-18 10:42:21 +00:00
}
func (m *DbMap) Query(q string, args ...interface{}) (*sql.Rows, error) {
2024-02-18 10:42:21 +00:00
if m.ExpandSliceArgs {
2024-02-18 10:42:21 +00:00
expandSliceArgs(&q, args...)
2024-02-18 10:42:21 +00:00
}
if m.logger != nil {
2024-02-18 10:42:21 +00:00
now := time.Now()
2024-02-18 10:42:21 +00:00
defer m.trace(now, q, args...)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return query(m, q, args...)
2024-02-18 10:42:21 +00:00
}
func (m *DbMap) trace(started time.Time, query string, args ...interface{}) {
2024-02-18 10:42:21 +00:00
if m.ExpandSliceArgs {
2024-02-18 10:42:21 +00:00
expandSliceArgs(&query, args...)
2024-02-18 10:42:21 +00:00
}
if m.logger != nil {
2024-02-18 10:42:21 +00:00
var margs = argsString(args...)
2024-02-18 10:42:21 +00:00
m.logger.Printf("%s%s [%s] (%v)", m.logPrefix, query, margs, (time.Now().Sub(started)))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
type stringer interface {
ToStringSlice() []string
}
type numberer interface {
ToInt64Slice() []int64
}
func expandSliceArgs(query *string, args ...interface{}) {
2024-02-18 10:42:21 +00:00
for _, arg := range args {
2024-02-18 10:42:21 +00:00
mapper, ok := arg.(map[string]interface{})
2024-02-18 10:42:21 +00:00
if !ok {
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
for key, value := range mapper {
2024-02-18 10:42:21 +00:00
var replacements []string
// add flexibility for any custom type to be convert to one of the
2024-02-18 10:42:21 +00:00
// acceptable formats.
2024-02-18 10:42:21 +00:00
if v, ok := value.(stringer); ok {
2024-02-18 10:42:21 +00:00
value = v.ToStringSlice()
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if v, ok := value.(numberer); ok {
2024-02-18 10:42:21 +00:00
value = v.ToInt64Slice()
2024-02-18 10:42:21 +00:00
}
switch v := value.(type) {
2024-02-18 10:42:21 +00:00
case []string:
2024-02-18 10:42:21 +00:00
for id, replace := range v {
2024-02-18 10:42:21 +00:00
mapper[fmt.Sprintf("%s%d", key, id)] = replace
2024-02-18 10:42:21 +00:00
replacements = append(replacements, fmt.Sprintf(":%s%d", key, id))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
case []uint:
2024-02-18 10:42:21 +00:00
for id, replace := range v {
2024-02-18 10:42:21 +00:00
mapper[fmt.Sprintf("%s%d", key, id)] = replace
2024-02-18 10:42:21 +00:00
replacements = append(replacements, fmt.Sprintf(":%s%d", key, id))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
case []uint8:
2024-02-18 10:42:21 +00:00
for id, replace := range v {
2024-02-18 10:42:21 +00:00
mapper[fmt.Sprintf("%s%d", key, id)] = replace
2024-02-18 10:42:21 +00:00
replacements = append(replacements, fmt.Sprintf(":%s%d", key, id))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
case []uint16:
2024-02-18 10:42:21 +00:00
for id, replace := range v {
2024-02-18 10:42:21 +00:00
mapper[fmt.Sprintf("%s%d", key, id)] = replace
2024-02-18 10:42:21 +00:00
replacements = append(replacements, fmt.Sprintf(":%s%d", key, id))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
case []uint32:
2024-02-18 10:42:21 +00:00
for id, replace := range v {
2024-02-18 10:42:21 +00:00
mapper[fmt.Sprintf("%s%d", key, id)] = replace
2024-02-18 10:42:21 +00:00
replacements = append(replacements, fmt.Sprintf(":%s%d", key, id))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
case []uint64:
2024-02-18 10:42:21 +00:00
for id, replace := range v {
2024-02-18 10:42:21 +00:00
mapper[fmt.Sprintf("%s%d", key, id)] = replace
2024-02-18 10:42:21 +00:00
replacements = append(replacements, fmt.Sprintf(":%s%d", key, id))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
case []int:
2024-02-18 10:42:21 +00:00
for id, replace := range v {
2024-02-18 10:42:21 +00:00
mapper[fmt.Sprintf("%s%d", key, id)] = replace
2024-02-18 10:42:21 +00:00
replacements = append(replacements, fmt.Sprintf(":%s%d", key, id))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
case []int8:
2024-02-18 10:42:21 +00:00
for id, replace := range v {
2024-02-18 10:42:21 +00:00
mapper[fmt.Sprintf("%s%d", key, id)] = replace
2024-02-18 10:42:21 +00:00
replacements = append(replacements, fmt.Sprintf(":%s%d", key, id))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
case []int16:
2024-02-18 10:42:21 +00:00
for id, replace := range v {
2024-02-18 10:42:21 +00:00
mapper[fmt.Sprintf("%s%d", key, id)] = replace
2024-02-18 10:42:21 +00:00
replacements = append(replacements, fmt.Sprintf(":%s%d", key, id))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
case []int32:
2024-02-18 10:42:21 +00:00
for id, replace := range v {
2024-02-18 10:42:21 +00:00
mapper[fmt.Sprintf("%s%d", key, id)] = replace
2024-02-18 10:42:21 +00:00
replacements = append(replacements, fmt.Sprintf(":%s%d", key, id))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
case []int64:
2024-02-18 10:42:21 +00:00
for id, replace := range v {
2024-02-18 10:42:21 +00:00
mapper[fmt.Sprintf("%s%d", key, id)] = replace
2024-02-18 10:42:21 +00:00
replacements = append(replacements, fmt.Sprintf(":%s%d", key, id))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
case []float32:
2024-02-18 10:42:21 +00:00
for id, replace := range v {
2024-02-18 10:42:21 +00:00
mapper[fmt.Sprintf("%s%d", key, id)] = replace
2024-02-18 10:42:21 +00:00
replacements = append(replacements, fmt.Sprintf(":%s%d", key, id))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
case []float64:
2024-02-18 10:42:21 +00:00
for id, replace := range v {
2024-02-18 10:42:21 +00:00
mapper[fmt.Sprintf("%s%d", key, id)] = replace
2024-02-18 10:42:21 +00:00
replacements = append(replacements, fmt.Sprintf(":%s%d", key, id))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
if len(replacements) == 0 {
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
*query = strings.Replace(*query, fmt.Sprintf(":%s", key), strings.Join(replacements, ","), -1)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}