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 // ModifyColumn modify column's type ModifyColumn(tableName string, columnName string, typ string) error // 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 // BuildKeyName returns a valid key name (foreign key, index key) for the given table, field and reference BuildKeyName(kind, tableName string, fields ...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) } func currentDatabaseAndTable(dialect Dialect, tableName string) (string, string) { if strings.Contains(tableName, ".") { splitStrings := strings.SplitN(tableName, ".", 2) return splitStrings[0], splitStrings[1] } return dialect.CurrentDatabase(), tableName }