2014-04-25 03:20:23 +04:00
package gorm
2013-11-14 14:59:11 +04:00
import (
2016-05-22 01:13:26 +03:00
"crypto/sha1"
2019-10-17 18:22:13 +03:00
"database/sql"
2013-11-14 14:59:11 +04:00
"fmt"
2014-03-16 05:28:43 +04:00
"reflect"
2016-05-22 01:13:26 +03:00
"regexp"
2017-07-03 06:26:31 +03:00
"strconv"
2016-02-14 08:34:32 +03:00
"strings"
2015-02-17 17:55:14 +03:00
"time"
2016-05-22 10:58:37 +03:00
"unicode/utf8"
2013-11-14 14:59:11 +04:00
)
2019-04-15 11:46:50 +03:00
var mysqlIndexRegex = regexp . MustCompile ( ` ^(.+)\((\d+)\)$ ` )
2015-03-17 05:40:42 +03:00
type mysql struct {
commonDialect
2014-09-16 00:03:14 +04:00
}
2016-02-14 13:06:42 +03:00
func init ( ) {
RegisterDialect ( "mysql" , & mysql { } )
}
2016-03-05 16:24:54 +03:00
func ( mysql ) GetName ( ) string {
return "mysql"
}
2016-01-18 15:32:52 +03:00
func ( mysql ) Quote ( key string ) string {
return fmt . Sprintf ( "`%s`" , key )
}
2016-02-14 08:34:32 +03:00
// Get Data Type for MySQL Dialect
2017-01-15 16:24:53 +03:00
func ( s * mysql ) DataTypeOf ( field * StructField ) string {
var dataValue , sqlType , size , additionalType = ParseFieldStructForDialect ( field , s )
2016-02-13 18:51:36 +03:00
2016-06-20 17:00:38 +03:00
// MySQL allows only one auto increment column per table, and it must
// be a KEY column.
2018-09-10 02:11:00 +03:00
if _ , ok := field . TagSettingsGet ( "AUTO_INCREMENT" ) ; ok {
if _ , ok = field . TagSettingsGet ( "INDEX" ) ; ! ok && ! field . IsPrimaryKey {
field . TagSettingsDelete ( "AUTO_INCREMENT" )
2016-06-20 17:00:38 +03:00
}
}
2016-02-14 08:34:32 +03:00
if sqlType == "" {
switch dataValue . Kind ( ) {
case reflect . Bool :
sqlType = "boolean"
2017-04-18 12:16:10 +03:00
case reflect . Int8 :
2017-10-13 10:08:55 +03:00
if s . fieldCanAutoIncrement ( field ) {
2018-09-10 02:11:00 +03:00
field . TagSettingsSet ( "AUTO_INCREMENT" , "AUTO_INCREMENT" )
2017-04-18 12:16:10 +03:00
sqlType = "tinyint AUTO_INCREMENT"
} else {
sqlType = "tinyint"
}
case reflect . Int , reflect . Int16 , reflect . Int32 :
2017-10-13 10:08:55 +03:00
if s . fieldCanAutoIncrement ( field ) {
2018-09-10 02:11:00 +03:00
field . TagSettingsSet ( "AUTO_INCREMENT" , "AUTO_INCREMENT" )
2016-02-14 08:34:32 +03:00
sqlType = "int AUTO_INCREMENT"
} else {
sqlType = "int"
}
2017-04-18 12:16:10 +03:00
case reflect . Uint8 :
2017-10-13 10:08:55 +03:00
if s . fieldCanAutoIncrement ( field ) {
2018-09-10 02:11:00 +03:00
field . TagSettingsSet ( "AUTO_INCREMENT" , "AUTO_INCREMENT" )
2017-04-18 12:16:10 +03:00
sqlType = "tinyint unsigned AUTO_INCREMENT"
} else {
sqlType = "tinyint unsigned"
}
case reflect . Uint , reflect . Uint16 , reflect . Uint32 , reflect . Uintptr :
2017-10-13 10:08:55 +03:00
if s . fieldCanAutoIncrement ( field ) {
2018-09-10 02:11:00 +03:00
field . TagSettingsSet ( "AUTO_INCREMENT" , "AUTO_INCREMENT" )
2016-02-14 08:34:32 +03:00
sqlType = "int unsigned AUTO_INCREMENT"
} else {
sqlType = "int unsigned"
}
case reflect . Int64 :
2017-10-13 10:08:55 +03:00
if s . fieldCanAutoIncrement ( field ) {
2018-09-10 02:11:00 +03:00
field . TagSettingsSet ( "AUTO_INCREMENT" , "AUTO_INCREMENT" )
2016-02-14 08:34:32 +03:00
sqlType = "bigint AUTO_INCREMENT"
} else {
sqlType = "bigint"
}
case reflect . Uint64 :
2017-10-13 10:08:55 +03:00
if s . fieldCanAutoIncrement ( field ) {
2018-09-10 02:11:00 +03:00
field . TagSettingsSet ( "AUTO_INCREMENT" , "AUTO_INCREMENT" )
2016-02-14 08:34:32 +03:00
sqlType = "bigint unsigned AUTO_INCREMENT"
} else {
sqlType = "bigint unsigned"
}
case reflect . Float32 , reflect . Float64 :
sqlType = "double"
case reflect . String :
2014-03-16 05:28:43 +04:00
if size > 0 && size < 65532 {
2016-02-14 08:34:32 +03:00
sqlType = fmt . Sprintf ( "varchar(%d)" , size )
} else {
sqlType = "longtext"
}
case reflect . Struct :
if _ , ok := dataValue . Interface ( ) . ( time . Time ) ; ok {
2018-02-09 18:18:47 +03:00
precision := ""
2018-09-10 02:11:00 +03:00
if p , ok := field . TagSettingsGet ( "PRECISION" ) ; ok {
2018-02-09 18:18:47 +03:00
precision = fmt . Sprintf ( "(%s)" , p )
}
2019-10-17 18:36:06 +03:00
if _ , ok := field . TagSettings [ "NOT NULL" ] ; ok || field . IsPrimaryKey {
sqlType = fmt . Sprintf ( "DATETIME%v" , precision )
2016-02-14 17:42:17 +03:00
} else {
2019-10-17 18:36:06 +03:00
sqlType = fmt . Sprintf ( "DATETIME%v NULL" , precision )
2016-02-14 17:42:17 +03:00
}
2016-02-14 08:34:32 +03:00
}
default :
2017-04-19 10:21:56 +03:00
if IsByteArrayOrSlice ( dataValue ) {
2016-02-14 08:34:32 +03:00
if size > 0 && size < 65532 {
sqlType = fmt . Sprintf ( "varbinary(%d)" , size )
} else {
sqlType = "longblob"
}
2014-03-16 05:28:43 +04:00
}
}
2013-11-14 14:59:11 +04:00
}
2016-02-14 08:34:32 +03:00
if sqlType == "" {
2019-10-17 17:56:19 +03:00
panic ( fmt . Sprintf ( "invalid sql type %s (%s) in field %s for mysql" , dataValue . Type ( ) . Name ( ) , dataValue . Kind ( ) . String ( ) , field . Name ) )
2016-02-14 08:34:32 +03:00
}
if strings . TrimSpace ( additionalType ) == "" {
return sqlType
}
return fmt . Sprintf ( "%v %v" , sqlType , additionalType )
2013-11-14 14:59:11 +04:00
}
2016-02-15 09:09:24 +03:00
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
}
2018-02-09 17:58:34 +03:00
func ( s mysql ) ModifyColumn ( tableName string , columnName string , typ string ) error {
_ , err := s . db . Exec ( fmt . Sprintf ( "ALTER TABLE %v MODIFY COLUMN %v %v" , tableName , columnName , typ ) )
return err
}
2017-07-03 06:26:31 +03: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 )
2017-07-03 06:58:01 +03:00
if offset != nil {
if parsedOffset , err := strconv . ParseInt ( fmt . Sprint ( offset ) , 0 , 0 ) ; err == nil && parsedOffset >= 0 {
sql += fmt . Sprintf ( " OFFSET %d" , parsedOffset )
}
2017-07-03 06:26:31 +03:00
}
}
}
return
}
2016-03-05 17:50:49 +03:00
func ( s mysql ) HasForeignKey ( tableName string , foreignKeyName string ) bool {
var count int
2018-02-03 15:27:19 +03:00
currentDatabase , tableName := currentDatabaseAndTable ( & s , tableName )
s . db . QueryRow ( "SELECT count(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA=? AND TABLE_NAME=? AND CONSTRAINT_NAME=? AND CONSTRAINT_TYPE='FOREIGN KEY'" , currentDatabase , tableName , foreignKeyName ) . Scan ( & count )
2016-03-05 17:50:49 +03:00
return count > 0
}
2019-10-17 18:22:13 +03:00
func ( s mysql ) HasTable ( tableName string ) bool {
currentDatabase , tableName := currentDatabaseAndTable ( & s , tableName )
var name string
2019-10-21 15:20:38 +03:00
if err := s . db . QueryRow ( fmt . Sprintf ( "SHOW TABLES FROM `%s` WHERE `Tables_in_%s` = ?" , currentDatabase , currentDatabase ) , tableName ) . Scan ( & name ) ; err != nil {
2019-10-17 18:22:13 +03:00
if err == sql . ErrNoRows {
return false
}
panic ( err )
} else {
return true
}
}
func ( s mysql ) HasIndex ( tableName string , indexName string ) bool {
currentDatabase , tableName := currentDatabaseAndTable ( & s , tableName )
if rows , err := s . db . Query ( fmt . Sprintf ( "SHOW INDEXES FROM `%s` FROM `%s` WHERE Key_name = ?" , tableName , currentDatabase ) , indexName ) ; err != nil {
panic ( err )
} else {
defer rows . Close ( )
return rows . Next ( )
}
}
func ( s mysql ) HasColumn ( tableName string , columnName string ) bool {
currentDatabase , tableName := currentDatabaseAndTable ( & s , tableName )
if rows , err := s . db . Query ( fmt . Sprintf ( "SHOW COLUMNS FROM `%s` FROM `%s` WHERE Field = ?" , tableName , currentDatabase ) , columnName ) ; err != nil {
panic ( err )
} else {
defer rows . Close ( )
return rows . Next ( )
}
}
2016-07-11 16:37:44 +03:00
func ( s mysql ) CurrentDatabase ( ) ( name string ) {
2016-02-15 09:09:24 +03:00
s . db . QueryRow ( "SELECT DATABASE()" ) . Scan ( & name )
2016-01-18 15:32:52 +03:00
return
2013-11-30 10:52:01 +04:00
}
2014-04-25 03:20:23 +04:00
2015-03-17 05:40:42 +03:00
func ( mysql ) SelectFromDummyTable ( ) string {
return "FROM DUAL"
2014-07-29 08:02:03 +04:00
}
2016-05-22 01:13:26 +03:00
2018-02-02 17:01:31 +03:00
func ( s mysql ) BuildKeyName ( kind , tableName string , fields ... string ) string {
keyName := s . commonDialect . BuildKeyName ( kind , tableName , fields ... )
2016-05-22 10:58:37 +03:00
if utf8 . RuneCountInString ( keyName ) <= 64 {
2016-05-22 01:13:26 +03:00
return keyName
}
h := sha1 . New ( )
h . Write ( [ ] byte ( keyName ) )
bs := h . Sum ( nil )
2018-01-31 11:32:36 +03:00
// sha1 is 40 characters, keep first 24 characters of destination
2019-04-15 11:46:50 +03:00
destRunes := [ ] rune ( keyNameRegex . ReplaceAllString ( fields [ 0 ] , "_" ) )
2016-05-22 10:58:37 +03:00
if len ( destRunes ) > 24 {
destRunes = destRunes [ : 24 ]
}
2016-05-22 01:13:26 +03:00
2016-05-22 10:58:37 +03:00
return fmt . Sprintf ( "%s%x" , string ( destRunes ) , bs )
2016-05-22 01:13:26 +03:00
}
2018-02-12 12:39:34 +03:00
2019-04-15 11:46:50 +03:00
// NormalizeIndexAndColumn returns index name and column name for specify an index prefix length if needed
func ( mysql ) NormalizeIndexAndColumn ( indexName , columnName string ) ( string , string ) {
submatch := mysqlIndexRegex . FindStringSubmatch ( indexName )
if len ( submatch ) != 3 {
return indexName , columnName
}
indexName = submatch [ 1 ]
columnName = fmt . Sprintf ( "%s(%s)" , columnName , submatch [ 2 ] )
return indexName , columnName
}
2018-02-12 12:39:34 +03:00
func ( mysql ) DefaultValueStr ( ) string {
return "VALUES()"
}