2017-05-26 13:07:22 +02:00
package gorm
import (
"crypto/sha1"
"fmt"
"reflect"
"regexp"
2017-07-28 05:46:40 +02:00
"strconv"
2017-05-26 13:07:22 +02:00
"strings"
"time"
"unicode/utf8"
)
type mysql struct {
commonDialect
}
func init ( ) {
RegisterDialect ( "mysql" , & mysql { } )
}
func ( mysql ) GetName ( ) string {
return "mysql"
}
func ( mysql ) Quote ( key string ) string {
return fmt . Sprintf ( "`%s`" , key )
}
// Get Data Type for MySQL Dialect
func ( s * mysql ) DataTypeOf ( field * StructField ) string {
var dataValue , sqlType , size , additionalType = ParseFieldStructForDialect ( field , s )
// MySQL allows only one auto increment column per table, and it must
// be a KEY column.
if _ , ok := field . TagSettings [ "AUTO_INCREMENT" ] ; ok {
if _ , ok = field . TagSettings [ "INDEX" ] ; ! ok && ! field . IsPrimaryKey {
delete ( field . TagSettings , "AUTO_INCREMENT" )
}
}
if sqlType == "" {
switch dataValue . Kind ( ) {
case reflect . Bool :
sqlType = "boolean"
case reflect . Int8 :
if _ , ok := field . TagSettings [ "AUTO_INCREMENT" ] ; ok || field . IsPrimaryKey {
field . TagSettings [ "AUTO_INCREMENT" ] = "AUTO_INCREMENT"
sqlType = "tinyint AUTO_INCREMENT"
} else {
sqlType = "tinyint"
}
case reflect . Int , reflect . Int16 , reflect . Int32 :
if _ , ok := field . TagSettings [ "AUTO_INCREMENT" ] ; ok || field . IsPrimaryKey {
field . TagSettings [ "AUTO_INCREMENT" ] = "AUTO_INCREMENT"
sqlType = "int AUTO_INCREMENT"
} else {
sqlType = "int"
}
case reflect . Uint8 :
if _ , ok := field . TagSettings [ "AUTO_INCREMENT" ] ; ok || field . IsPrimaryKey {
field . TagSettings [ "AUTO_INCREMENT" ] = "AUTO_INCREMENT"
sqlType = "tinyint unsigned AUTO_INCREMENT"
} else {
sqlType = "tinyint unsigned"
}
case reflect . Uint , reflect . Uint16 , reflect . Uint32 , reflect . Uintptr :
if _ , ok := field . TagSettings [ "AUTO_INCREMENT" ] ; ok || field . IsPrimaryKey {
field . TagSettings [ "AUTO_INCREMENT" ] = "AUTO_INCREMENT"
sqlType = "int unsigned AUTO_INCREMENT"
} else {
sqlType = "int unsigned"
}
case reflect . Int64 :
if _ , ok := field . TagSettings [ "AUTO_INCREMENT" ] ; ok || field . IsPrimaryKey {
field . TagSettings [ "AUTO_INCREMENT" ] = "AUTO_INCREMENT"
sqlType = "bigint AUTO_INCREMENT"
} else {
sqlType = "bigint"
}
case reflect . Uint64 :
if _ , ok := field . TagSettings [ "AUTO_INCREMENT" ] ; ok || field . IsPrimaryKey {
field . TagSettings [ "AUTO_INCREMENT" ] = "AUTO_INCREMENT"
sqlType = "bigint unsigned AUTO_INCREMENT"
} else {
sqlType = "bigint unsigned"
}
case reflect . Float32 , reflect . Float64 :
sqlType = "double"
case reflect . String :
if size > 0 && size < 65532 {
sqlType = fmt . Sprintf ( "varchar(%d)" , size )
} else {
sqlType = "longtext"
}
case reflect . Struct :
if _ , ok := dataValue . Interface ( ) . ( time . Time ) ; ok {
if _ , ok := field . TagSettings [ "NOT NULL" ] ; ok {
sqlType = "timestamp"
} else {
sqlType = "timestamp NULL"
}
}
default :
if IsByteArrayOrSlice ( dataValue ) {
if size > 0 && size < 65532 {
sqlType = fmt . Sprintf ( "varbinary(%d)" , size )
} else {
sqlType = "longblob"
}
}
}
}
if sqlType == "" {
panic ( fmt . Sprintf ( "invalid sql type %s (%s) for mysql" , dataValue . Type ( ) . Name ( ) , dataValue . Kind ( ) . String ( ) ) )
}
if strings . TrimSpace ( additionalType ) == "" {
return sqlType
}
return fmt . Sprintf ( "%v %v" , sqlType , additionalType )
}
func ( s mysql ) RemoveIndex ( tableName string , indexName string ) error {
_ , err := s . db . Exec ( fmt . Sprintf ( "DROP INDEX %v ON %v" , indexName , s . Quote ( tableName ) ) )
return err
}
2017-07-28 05:46:40 +02:00
func ( s mysql ) LimitAndOffsetSQL ( limit , offset interface { } ) ( sql string ) {
if limit != nil {
if parsedLimit , err := strconv . ParseInt ( fmt . Sprint ( limit ) , 0 , 0 ) ; err == nil && parsedLimit >= 0 {
sql += fmt . Sprintf ( " LIMIT %d" , parsedLimit )
if offset != nil {
if parsedOffset , err := strconv . ParseInt ( fmt . Sprint ( offset ) , 0 , 0 ) ; err == nil && parsedOffset >= 0 {
sql += fmt . Sprintf ( " OFFSET %d" , parsedOffset )
}
}
}
}
return
}
2017-05-26 13:07:22 +02:00
func ( s mysql ) HasForeignKey ( tableName string , foreignKeyName string ) bool {
var count int
s . db . QueryRow ( "SELECT count(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA=? AND TABLE_NAME=? AND CONSTRAINT_NAME=? AND CONSTRAINT_TYPE='FOREIGN KEY'" , s . CurrentDatabase ( ) , tableName , foreignKeyName ) . Scan ( & count )
return count > 0
}
func ( s mysql ) CurrentDatabase ( ) ( name string ) {
s . db . QueryRow ( "SELECT DATABASE()" ) . Scan ( & name )
return
}
func ( mysql ) SelectFromDummyTable ( ) string {
return "FROM DUAL"
}
func ( s mysql ) BuildForeignKeyName ( tableName , field , dest string ) string {
keyName := s . commonDialect . BuildForeignKeyName ( tableName , field , dest )
if utf8 . RuneCountInString ( keyName ) <= 64 {
return keyName
}
h := sha1 . New ( )
h . Write ( [ ] byte ( keyName ) )
bs := h . Sum ( nil )
// sha1 is 40 digits, keep first 24 characters of destination
destRunes := [ ] rune ( regexp . MustCompile ( "(_*[^a-zA-Z]+_*|_+)" ) . ReplaceAllString ( dest , "_" ) )
if len ( destRunes ) > 24 {
destRunes = destRunes [ : 24 ]
}
return fmt . Sprintf ( "%s%x" , string ( destRunes ) , bs )
}