
Exporting sqlCommon as SQLCommon. This allows passing alternate implementations of the database connection, or wrapping the connection with middleware. This change didn't change any usages of the database variables. All usages were already only using the functions defined in SQLCommon. This does cause a breaking change in Dialect, since *sql.DB was referenced in the interface.
117 lines
3.6 KiB
Go
117 lines
3.6 KiB
Go
package gorm
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// Dialect interface contains behaviors that differ across SQL database
|
|
type Dialect interface {
|
|
// GetName get dialect's name
|
|
GetName() string
|
|
|
|
// SetDB set db for dialect
|
|
SetDB(db SQLCommon)
|
|
|
|
// BindVar return the placeholder for actual values in SQL statements, in many dbs it is "?", Postgres using $1
|
|
BindVar(i int) string
|
|
// Quote quotes field name to avoid SQL parsing exceptions by using a reserved word as a field name
|
|
Quote(key string) string
|
|
// DataTypeOf return data's sql type
|
|
DataTypeOf(field *StructField) string
|
|
|
|
// HasIndex check has index or not
|
|
HasIndex(tableName string, indexName string) bool
|
|
// HasForeignKey check has foreign key or not
|
|
HasForeignKey(tableName string, foreignKeyName string) bool
|
|
// RemoveIndex remove index
|
|
RemoveIndex(tableName string, indexName string) error
|
|
// HasTable check has table or not
|
|
HasTable(tableName string) bool
|
|
// HasColumn check has column or not
|
|
HasColumn(tableName string, columnName string) bool
|
|
|
|
// LimitAndOffsetSQL return generated SQL with Limit and Offset, as mssql has special case
|
|
LimitAndOffsetSQL(limit, offset interface{}) string
|
|
// SelectFromDummyTable return select values, for most dbs, `SELECT values` just works, mysql needs `SELECT value FROM DUAL`
|
|
SelectFromDummyTable() string
|
|
// LastInsertIdReturningSuffix most dbs support LastInsertId, but postgres needs to use `RETURNING`
|
|
LastInsertIDReturningSuffix(tableName, columnName string) string
|
|
|
|
// BuildForeignKeyName returns a foreign key name for the given table, field and reference
|
|
BuildForeignKeyName(tableName, field, dest string) string
|
|
|
|
// CurrentDatabase return current database name
|
|
CurrentDatabase() string
|
|
}
|
|
|
|
var dialectsMap = map[string]Dialect{}
|
|
|
|
func newDialect(name string, db SQLCommon) Dialect {
|
|
if value, ok := dialectsMap[name]; ok {
|
|
dialect := reflect.New(reflect.TypeOf(value).Elem()).Interface().(Dialect)
|
|
dialect.SetDB(db)
|
|
return dialect
|
|
}
|
|
|
|
fmt.Printf("`%v` is not officially supported, running under compatibility mode.\n", name)
|
|
commontDialect := &commonDialect{}
|
|
commontDialect.SetDB(db)
|
|
return commontDialect
|
|
}
|
|
|
|
// RegisterDialect register new dialect
|
|
func RegisterDialect(name string, dialect Dialect) {
|
|
dialectsMap[name] = dialect
|
|
}
|
|
|
|
// ParseFieldStructForDialect get field's sql data type
|
|
var ParseFieldStructForDialect = func(field *StructField, dialect Dialect) (fieldValue reflect.Value, sqlType string, size int, additionalType string) {
|
|
// Get redirected field type
|
|
var (
|
|
reflectType = field.Struct.Type
|
|
dataType = field.TagSettings["TYPE"]
|
|
)
|
|
|
|
for reflectType.Kind() == reflect.Ptr {
|
|
reflectType = reflectType.Elem()
|
|
}
|
|
|
|
// Get redirected field value
|
|
fieldValue = reflect.Indirect(reflect.New(reflectType))
|
|
|
|
if gormDataType, ok := fieldValue.Interface().(interface {
|
|
GormDataType(Dialect) string
|
|
}); ok {
|
|
dataType = gormDataType.GormDataType(dialect)
|
|
}
|
|
|
|
// Get scanner's real value
|
|
var getScannerValue func(reflect.Value)
|
|
getScannerValue = func(value reflect.Value) {
|
|
fieldValue = value
|
|
if _, isScanner := reflect.New(fieldValue.Type()).Interface().(sql.Scanner); isScanner && fieldValue.Kind() == reflect.Struct {
|
|
getScannerValue(fieldValue.Field(0))
|
|
}
|
|
}
|
|
getScannerValue(fieldValue)
|
|
|
|
// Default Size
|
|
if num, ok := field.TagSettings["SIZE"]; ok {
|
|
size, _ = strconv.Atoi(num)
|
|
} else {
|
|
size = 255
|
|
}
|
|
|
|
// Default type from tag setting
|
|
additionalType = field.TagSettings["NOT NULL"] + " " + field.TagSettings["UNIQUE"]
|
|
if value, ok := field.TagSettings["DEFAULT"]; ok {
|
|
additionalType = additionalType + " DEFAULT " + value
|
|
}
|
|
|
|
return fieldValue, dataType, size, strings.TrimSpace(additionalType)
|
|
}
|