2015-04-28 08:25:04 +00:00
// Copyright 2015 The Xorm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
2020-02-24 08:53:18 +00:00
package dialects
2014-01-07 09:33:27 +00:00
import (
2020-02-27 15:31:05 +00:00
"context"
2021-07-04 13:23:17 +00:00
"database/sql"
2017-03-23 06:05:32 +00:00
"errors"
2014-01-07 09:33:27 +00:00
"fmt"
2017-03-23 06:05:32 +00:00
"regexp"
2014-01-07 09:33:27 +00:00
"strconv"
"strings"
2023-10-27 16:27:33 +00:00
"xorm.io/xorm/v2/internal/core"
2023-10-27 14:27:46 +00:00
"xorm.io/xorm/v2/schemas"
2014-01-07 09:33:27 +00:00
)
2014-09-05 02:30:41 +00:00
var (
2014-09-06 15:25:46 +00:00
mysqlReservedWords = map [ string ] bool {
"ADD" : true ,
"ALL" : true ,
"ALTER" : true ,
"ANALYZE" : true ,
"AND" : true ,
"AS" : true ,
"ASC" : true ,
"ASENSITIVE" : true ,
"BEFORE" : true ,
"BETWEEN" : true ,
"BIGINT" : true ,
"BINARY" : true ,
"BLOB" : true ,
"BOTH" : true ,
"BY" : true ,
"CALL" : true ,
"CASCADE" : true ,
"CASE" : true ,
2023-07-12 08:52:23 +00:00
"CHAIN" : true ,
2014-09-06 15:25:46 +00:00
"CHANGE" : true ,
"CHAR" : true ,
"CHARACTER" : true ,
"CHECK" : true ,
"COLLATE" : true ,
"COLUMN" : true ,
"CONDITION" : true ,
"CONNECTION" : true ,
"CONSTRAINT" : true ,
"CONTINUE" : true ,
"CONVERT" : true ,
"CREATE" : true ,
"CROSS" : true ,
"CURRENT_DATE" : true ,
"CURRENT_TIME" : true ,
"CURRENT_TIMESTAMP" : true ,
"CURRENT_USER" : true ,
"CURSOR" : true ,
"DATABASE" : true ,
"DATABASES" : true ,
"DAY_HOUR" : true ,
"DAY_MICROSECOND" : true ,
"DAY_MINUTE" : true ,
"DAY_SECOND" : true ,
"DEC" : true ,
"DECIMAL" : true ,
"DECLARE" : true ,
"DEFAULT" : true ,
"DELAYED" : true ,
"DELETE" : true ,
"DESC" : true ,
"DESCRIBE" : true ,
"DETERMINISTIC" : true ,
"DISTINCT" : true ,
"DISTINCTROW" : true ,
"DIV" : true ,
"DOUBLE" : true ,
"DROP" : true ,
"DUAL" : true ,
"EACH" : true ,
"ELSE" : true ,
"ELSEIF" : true ,
"ENCLOSED" : true ,
"ESCAPED" : true ,
"EXISTS" : true ,
"EXIT" : true ,
"EXPLAIN" : true ,
"FALSE" : true ,
"FETCH" : true ,
"FLOAT" : true ,
"FLOAT4" : true ,
"FLOAT8" : true ,
"FOR" : true ,
"FORCE" : true ,
"FOREIGN" : true ,
"FROM" : true ,
"FULLTEXT" : true ,
"GOTO" : true ,
"GRANT" : true ,
"GROUP" : true ,
"HAVING" : true ,
"HIGH_PRIORITY" : true ,
"HOUR_MICROSECOND" : true ,
"HOUR_MINUTE" : true ,
"HOUR_SECOND" : true ,
"IF" : true ,
"IGNORE" : true ,
"IN" : true , "INDEX" : true ,
"INFILE" : true , "INNER" : true , "INOUT" : true ,
"INSENSITIVE" : true , "INSERT" : true , "INT" : true ,
"INT1" : true , "INT2" : true , "INT3" : true ,
"INT4" : true , "INT8" : true , "INTEGER" : true ,
"INTERVAL" : true , "INTO" : true , "IS" : true ,
"ITERATE" : true , "JOIN" : true , "KEY" : true ,
"KEYS" : true , "KILL" : true , "LABEL" : true ,
"LEADING" : true , "LEAVE" : true , "LEFT" : true ,
"LIKE" : true , "LIMIT" : true , "LINEAR" : true ,
"LINES" : true , "LOAD" : true , "LOCALTIME" : true ,
"LOCALTIMESTAMP" : true , "LOCK" : true , "LONG" : true ,
"LONGBLOB" : true , "LONGTEXT" : true , "LOOP" : true ,
"LOW_PRIORITY" : true , "MATCH" : true , "MEDIUMBLOB" : true ,
"MEDIUMINT" : true , "MEDIUMTEXT" : true , "MIDDLEINT" : true ,
"MINUTE_MICROSECOND" : true , "MINUTE_SECOND" : true , "MOD" : true ,
"MODIFIES" : true , "NATURAL" : true , "NOT" : true ,
"NO_WRITE_TO_BINLOG" : true , "NULL" : true , "NUMERIC" : true ,
"ON OPTIMIZE" : true , "OPTION" : true ,
"OPTIONALLY" : true , "OR" : true , "ORDER" : true ,
"OUT" : true , "OUTER" : true , "OUTFILE" : true ,
"PRECISION" : true , "PRIMARY" : true , "PROCEDURE" : true ,
"PURGE" : true , "RAID0" : true , "RANGE" : true ,
2023-07-12 08:52:23 +00:00
"RANK" : true ,
2014-09-06 15:25:46 +00:00
"READ" : true , "READS" : true , "REAL" : true ,
"REFERENCES" : true , "REGEXP" : true , "RELEASE" : true ,
"RENAME" : true , "REPEAT" : true , "REPLACE" : true ,
"REQUIRE" : true , "RESTRICT" : true , "RETURN" : true ,
"REVOKE" : true , "RIGHT" : true , "RLIKE" : true ,
"SCHEMA" : true , "SCHEMAS" : true , "SECOND_MICROSECOND" : true ,
"SELECT" : true , "SENSITIVE" : true , "SEPARATOR" : true ,
"SET" : true , "SHOW" : true , "SMALLINT" : true ,
"SPATIAL" : true , "SPECIFIC" : true , "SQL" : true ,
"SQLEXCEPTION" : true , "SQLSTATE" : true , "SQLWARNING" : true ,
"SQL_BIG_RESULT" : true , "SQL_CALC_FOUND_ROWS" : true , "SQL_SMALL_RESULT" : true ,
"SSL" : true , "STARTING" : true , "STRAIGHT_JOIN" : true ,
"TABLE" : true , "TERMINATED" : true , "THEN" : true ,
"TINYBLOB" : true , "TINYINT" : true , "TINYTEXT" : true ,
"TO" : true , "TRAILING" : true , "TRIGGER" : true ,
"TRUE" : true , "UNDO" : true , "UNION" : true ,
"UNIQUE" : true , "UNLOCK" : true , "UNSIGNED" : true ,
"UPDATE" : true , "USAGE" : true , "USE" : true ,
"USING" : true , "UTC_DATE" : true , "UTC_TIME" : true ,
"UTC_TIMESTAMP" : true , "VALUES" : true , "VARBINARY" : true ,
"VARCHAR" : true ,
"VARCHARACTER" : true ,
"VARYING" : true ,
"WHEN" : true ,
"WHERE" : true ,
"WHILE" : true ,
"WITH" : true ,
"WRITE" : true ,
"X509" : true ,
"XOR" : true ,
"YEAR_MONTH" : true ,
"ZEROFILL" : true ,
2014-09-05 02:30:41 +00:00
}
2020-03-06 07:48:32 +00:00
2020-03-27 07:13:04 +00:00
mysqlQuoter = schemas . Quoter {
Prefix : '`' ,
Suffix : '`' ,
IsReserved : schemas . AlwaysReserve ,
}
2014-09-05 02:30:41 +00:00
)
2014-01-07 09:33:27 +00:00
type mysql struct {
2020-02-24 08:53:18 +00:00
Base
2021-12-14 01:00:35 +00:00
rowFormat string
2014-01-07 09:33:27 +00:00
}
2020-03-24 02:19:24 +00:00
func ( db * mysql ) Init ( uri * URI ) error {
2020-03-06 07:48:32 +00:00
db . quoter = mysqlQuoter
2020-03-24 02:19:24 +00:00
return db . Base . Init ( db , uri )
2014-01-07 09:33:27 +00:00
}
2022-05-30 10:36:23 +00:00
var mysqlColAliases = map [ string ] string {
"numeric" : "decimal" ,
}
2021-07-07 09:09:40 +00:00
// Alias returns a alias of column
func ( db * mysql ) Alias ( col string ) string {
v , ok := mysqlColAliases [ strings . ToLower ( col ) ]
if ok {
return v
}
return col
}
2021-06-12 07:06:05 +00:00
func ( db * mysql ) Version ( ctx context . Context , queryer core . Queryer ) ( * schemas . Version , error ) {
rows , err := queryer . QueryContext ( ctx , "SELECT @@VERSION" )
if err != nil {
return nil , err
}
defer rows . Close ( )
var version string
if ! rows . Next ( ) {
2021-07-20 05:46:24 +00:00
if rows . Err ( ) != nil {
return nil , rows . Err ( )
}
return nil , errors . New ( "unknow version" )
2021-06-12 07:06:05 +00:00
}
if err := rows . Scan ( & version ) ; err != nil {
return nil , err
}
fields := strings . Split ( version , "-" )
if len ( fields ) == 3 && fields [ 1 ] == "TiDB" {
// 5.7.25-TiDB-v3.0.3
return & schemas . Version {
Number : strings . TrimPrefix ( fields [ 2 ] , "v" ) ,
Level : fields [ 0 ] ,
Edition : fields [ 1 ] ,
} , nil
}
var edition string
if len ( fields ) == 2 {
edition = fields [ 1 ]
}
return & schemas . Version {
Number : fields [ 0 ] ,
Edition : edition ,
} , nil
}
2021-08-24 05:46:08 +00:00
func ( db * mysql ) Features ( ) * DialectFeatures {
return & DialectFeatures {
AutoincrMode : IncrAutoincrMode ,
}
}
2018-03-08 01:30:38 +00:00
func ( db * mysql ) SetParams ( params map [ string ] string ) {
rowFormat , ok := params [ "rowFormat" ]
if ok {
2022-05-30 10:36:23 +00:00
t := strings . ToUpper ( rowFormat )
2018-03-08 01:30:38 +00:00
switch t {
case "COMPACT" :
fallthrough
case "REDUNDANT" :
fallthrough
case "DYNAMIC" :
fallthrough
case "COMPRESSED" :
db . rowFormat = t
}
}
}
2020-02-24 08:53:18 +00:00
func ( db * mysql ) SQLType ( c * schemas . Column ) string {
2014-01-07 09:33:27 +00:00
var res string
2021-07-29 03:04:43 +00:00
var isUnsigned bool
2014-01-07 09:33:27 +00:00
switch t := c . SQLType . Name ; t {
2020-02-24 08:53:18 +00:00
case schemas . Bool :
res = schemas . TinyInt
2014-04-11 07:37:27 +00:00
c . Length = 1
2020-02-24 08:53:18 +00:00
case schemas . Serial :
2014-01-07 09:33:27 +00:00
c . IsAutoIncrement = true
c . IsPrimaryKey = true
c . Nullable = false
2020-02-24 08:53:18 +00:00
res = schemas . Int
case schemas . BigSerial :
2014-01-07 09:33:27 +00:00
c . IsAutoIncrement = true
c . IsPrimaryKey = true
c . Nullable = false
2020-02-24 08:53:18 +00:00
res = schemas . BigInt
case schemas . Bytea :
res = schemas . Blob
case schemas . TimeStampz :
res = schemas . Char
2014-01-07 09:33:27 +00:00
c . Length = 64
2020-02-24 08:53:18 +00:00
case schemas . Enum : // mysql enum
res = schemas . Enum
2014-05-05 14:26:17 +00:00
res += "("
2014-08-19 16:26:28 +00:00
opts := ""
2016-11-09 09:37:59 +00:00
for v := range c . EnumOptions {
2014-08-19 16:26:28 +00:00
opts += fmt . Sprintf ( ",'%v'" , v )
2014-05-05 14:26:17 +00:00
}
2014-08-28 14:48:01 +00:00
res += strings . TrimLeft ( opts , "," )
2014-05-05 14:26:17 +00:00
res += ")"
2020-02-24 08:53:18 +00:00
case schemas . Set : // mysql set
res = schemas . Set
2014-08-07 14:18:15 +00:00
res += "("
2014-08-19 16:26:28 +00:00
opts := ""
2016-11-09 09:37:59 +00:00
for v := range c . SetOptions {
2014-08-19 16:26:28 +00:00
opts += fmt . Sprintf ( ",'%v'" , v )
2014-08-07 14:18:15 +00:00
}
2014-08-28 14:48:01 +00:00
res += strings . TrimLeft ( opts , "," )
2014-08-07 14:18:15 +00:00
res += ")"
2020-02-24 08:53:18 +00:00
case schemas . NVarchar :
res = schemas . Varchar
case schemas . Uuid :
res = schemas . Varchar
2015-02-22 15:52:53 +00:00
c . Length = 40
2020-02-24 08:53:18 +00:00
case schemas . Json :
res = schemas . Text
2021-04-11 08:47:04 +00:00
case schemas . UnsignedInt :
res = schemas . Int
2021-07-29 03:04:43 +00:00
isUnsigned = true
2021-04-11 08:47:04 +00:00
case schemas . UnsignedBigInt :
res = schemas . BigInt
2021-07-29 03:04:43 +00:00
isUnsigned = true
case schemas . UnsignedMediumInt :
res = schemas . MediumInt
isUnsigned = true
case schemas . UnsignedSmallInt :
res = schemas . SmallInt
isUnsigned = true
case schemas . UnsignedTinyInt :
res = schemas . TinyInt
isUnsigned = true
2023-09-16 15:43:12 +00:00
case schemas . UnsignedFloat :
res = schemas . Float
isUnsigned = true
2014-01-07 09:33:27 +00:00
default :
res = t
}
2016-11-09 09:37:59 +00:00
hasLen1 := ( c . Length > 0 )
hasLen2 := ( c . Length2 > 0 )
2014-05-29 08:53:23 +00:00
2020-02-24 08:53:18 +00:00
if res == schemas . BigInt && ! hasLen1 && ! hasLen2 {
2014-08-28 14:48:01 +00:00
c . Length = 20
hasLen1 = true
}
2014-05-29 08:53:23 +00:00
if hasLen2 {
2022-07-14 05:55:24 +00:00
res += "(" + strconv . FormatInt ( c . Length , 10 ) + "," + strconv . FormatInt ( c . Length2 , 10 ) + ")"
2014-05-29 08:53:23 +00:00
} else if hasLen1 {
2022-07-14 05:55:24 +00:00
res += "(" + strconv . FormatInt ( c . Length , 10 ) + ")"
2014-01-07 09:33:27 +00:00
}
2021-04-11 08:47:04 +00:00
2021-07-29 03:04:43 +00:00
if isUnsigned {
2021-04-11 08:47:04 +00:00
res += " UNSIGNED"
}
2014-01-07 09:33:27 +00:00
return res
}
2021-07-14 04:20:26 +00:00
func ( db * mysql ) ColumnTypeKind ( t string ) int {
switch strings . ToUpper ( t ) {
case "DATETIME" :
return schemas . TIME_TYPE
case "CHAR" , "VARCHAR" , "TINYTEXT" , "TEXT" , "MEDIUMTEXT" , "LONGTEXT" , "ENUM" , "SET" :
return schemas . TEXT_TYPE
case "BIGINT" , "TINYINT" , "SMALLINT" , "MEDIUMINT" , "INT" , "FLOAT" , "REAL" , "DOUBLE PRECISION" , "DECIMAL" , "NUMERIC" , "BIT" :
return schemas . NUMERIC_TYPE
case "BINARY" , "VARBINARY" , "TINYBLOB" , "BLOB" , "MEDIUMBLOB" , "LONGBLOB" :
return schemas . BLOB_TYPE
default :
return schemas . UNKNOW_TYPE
}
}
2014-09-06 15:25:46 +00:00
func ( db * mysql ) IsReserved ( name string ) bool {
2020-03-06 07:48:32 +00:00
_ , ok := mysqlReservedWords [ strings . ToUpper ( name ) ]
2014-09-06 15:25:46 +00:00
return ok
}
2014-01-07 09:33:27 +00:00
func ( db * mysql ) AutoIncrStr ( ) string {
return "AUTO_INCREMENT"
}
2020-02-24 08:53:18 +00:00
func ( db * mysql ) IndexCheckSQL ( tableName , idxName string ) ( string , [ ] interface { } ) {
args := [ ] interface { } { db . uri . DBName , tableName , idxName }
2014-01-07 09:33:27 +00:00
sql := "SELECT `INDEX_NAME` FROM `INFORMATION_SCHEMA`.`STATISTICS`"
sql += " WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `INDEX_NAME`=?"
return sql , args
}
2020-03-24 02:19:24 +00:00
func ( db * mysql ) IsTableExist ( queryer core . Queryer , ctx context . Context , tableName string ) ( bool , error ) {
2014-01-07 09:33:27 +00:00
sql := "SELECT `TABLE_NAME` from `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? and `TABLE_NAME`=?"
2020-03-24 02:19:24 +00:00
return db . HasRecords ( queryer , ctx , sql , db . uri . DBName , tableName )
2014-01-07 09:33:27 +00:00
}
2020-02-29 08:59:59 +00:00
func ( db * mysql ) AddColumnSQL ( tableName string , col * schemas . Column ) string {
quoter := db . dialect . Quoter ( )
2023-07-01 03:40:09 +00:00
s , _ := ColumnString ( db , col , true , true )
2023-06-02 14:16:30 +00:00
var b strings . Builder
b . WriteString ( "ALTER TABLE " )
quoter . QuoteTo ( & b , tableName )
b . WriteString ( " ADD " )
b . WriteString ( s )
2020-02-29 08:59:59 +00:00
if len ( col . Comment ) > 0 {
2023-06-02 14:16:30 +00:00
b . WriteString ( " COMMENT '" )
b . WriteString ( col . Comment )
b . WriteString ( "'" )
2020-02-29 08:59:59 +00:00
}
2023-06-02 14:16:30 +00:00
return b . String ( )
2020-02-29 08:59:59 +00:00
}
2023-07-01 03:40:09 +00:00
// ModifyColumnSQL returns a SQL to modify SQL
func ( db * mysql ) ModifyColumnSQL ( tableName string , col * schemas . Column ) string {
s , _ := ColumnString ( db . dialect , col , false , true )
2023-07-11 17:10:36 +00:00
if col . Comment != "" {
s += fmt . Sprintf ( " COMMENT '%s'" , col . Comment )
}
2023-07-01 03:40:09 +00:00
return fmt . Sprintf ( "ALTER TABLE %s MODIFY COLUMN %s" , db . quoter . Quote ( tableName ) , s )
}
2020-03-24 02:19:24 +00:00
func ( db * mysql ) GetColumns ( queryer core . Queryer , ctx context . Context , tableName string ) ( [ ] string , map [ string ] * schemas . Column , error ) {
2020-02-24 08:53:18 +00:00
args := [ ] interface { } { db . uri . DBName , tableName }
2020-09-02 12:37:16 +00:00
alreadyQuoted := "(INSTR(VERSION(), 'maria') > 0 && " +
"(SUBSTRING_INDEX(VERSION(), '.', 1) > 10 || " +
"(SUBSTRING_INDEX(VERSION(), '.', 1) = 10 && " +
"(SUBSTRING_INDEX(SUBSTRING(VERSION(), 4), '.', 1) > 2 || " +
"(SUBSTRING_INDEX(SUBSTRING(VERSION(), 4), '.', 1) = 2 && " +
"SUBSTRING_INDEX(SUBSTRING(VERSION(), 6), '-', 1) >= 7)))))"
2014-01-07 09:33:27 +00:00
s := "SELECT `COLUMN_NAME`, `IS_NULLABLE`, `COLUMN_DEFAULT`, `COLUMN_TYPE`," +
2022-04-22 02:48:53 +00:00
" `COLUMN_KEY`, `EXTRA`, `COLUMN_COMMENT`, `CHARACTER_MAXIMUM_LENGTH`, " +
2023-07-01 03:40:09 +00:00
alreadyQuoted + " AS NEEDS_QUOTE, `COLLATION_NAME` " +
2020-09-02 12:37:16 +00:00
"FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?" +
2022-05-31 03:00:28 +00:00
" ORDER BY `COLUMNS`.ORDINAL_POSITION ASC"
2014-04-11 15:33:56 +00:00
2020-03-24 02:19:24 +00:00
rows , err := queryer . QueryContext ( ctx , s , args ... )
2014-01-07 09:33:27 +00:00
if err != nil {
return nil , nil , err
}
2014-04-11 15:33:56 +00:00
defer rows . Close ( )
2020-02-24 08:53:18 +00:00
cols := make ( map [ string ] * schemas . Column )
2014-01-07 09:33:27 +00:00
colSeq := make ( [ ] string , 0 )
for rows . Next ( ) {
2020-02-24 08:53:18 +00:00
col := new ( schemas . Column )
2016-06-30 08:35:04 +00:00
col . Indexes = make ( map [ string ] int )
2014-01-07 09:33:27 +00:00
2021-04-11 08:47:04 +00:00
var columnName , nullableStr , colType , colKey , extra , comment string
var alreadyQuoted , isUnsigned bool
2023-07-01 03:40:09 +00:00
var colDefault , maxLength , collation * string
err = rows . Scan ( & columnName , & nullableStr , & colDefault , & colType , & colKey , & extra , & comment , & maxLength , & alreadyQuoted , & collation )
2014-01-07 09:33:27 +00:00
if err != nil {
return nil , nil , err
}
col . Name = strings . Trim ( columnName , "` " )
2017-05-04 13:12:02 +00:00
col . Comment = comment
2021-04-11 08:47:04 +00:00
if nullableStr == "YES" {
2014-01-07 09:33:27 +00:00
col . Nullable = true
}
2020-09-02 12:37:16 +00:00
if colDefault != nil && ( ! alreadyQuoted || * colDefault != "NULL" ) {
2014-01-07 09:33:27 +00:00
col . Default = * colDefault
2019-09-29 04:31:06 +00:00
col . DefaultIsEmpty = false
} else {
col . DefaultIsEmpty = true
2014-01-07 09:33:27 +00:00
}
2023-07-01 03:40:09 +00:00
if collation != nil {
col . Collation = * collation
}
2014-01-07 09:33:27 +00:00
2021-04-11 08:47:04 +00:00
fields := strings . Fields ( colType )
if len ( fields ) == 2 && fields [ 1 ] == "unsigned" {
isUnsigned = true
}
colType = fields [ 0 ]
2014-01-07 09:33:27 +00:00
cts := strings . Split ( colType , "(" )
2014-05-05 14:26:17 +00:00
colName := cts [ 0 ]
2021-04-08 06:12:40 +00:00
// Remove the /* mariadb-5.3 */ suffix from coltypes
colName = strings . TrimSuffix ( colName , "/* mariadb-5.3 */" )
2014-05-05 14:26:17 +00:00
colType = strings . ToUpper ( colName )
2022-07-14 05:55:24 +00:00
var len1 , len2 int64
2014-01-07 09:33:27 +00:00
if len ( cts ) == 2 {
idx := strings . Index ( cts [ 1 ] , ")" )
2020-02-24 08:53:18 +00:00
if colType == schemas . Enum && cts [ 1 ] [ 0 ] == '\'' { // enum
2014-05-05 14:26:17 +00:00
options := strings . Split ( cts [ 1 ] [ 0 : idx ] , "," )
col . EnumOptions = make ( map [ string ] int )
for k , v := range options {
v = strings . TrimSpace ( v )
v = strings . Trim ( v , "'" )
col . EnumOptions [ v ] = k
}
2020-02-24 08:53:18 +00:00
} else if colType == schemas . Set && cts [ 1 ] [ 0 ] == '\'' {
2014-08-07 14:18:15 +00:00
options := strings . Split ( cts [ 1 ] [ 0 : idx ] , "," )
col . SetOptions = make ( map [ string ] int )
for k , v := range options {
v = strings . TrimSpace ( v )
v = strings . Trim ( v , "'" )
col . SetOptions [ v ] = k
}
2014-05-05 14:26:17 +00:00
} else {
lens := strings . Split ( cts [ 1 ] [ 0 : idx ] , "," )
2022-07-14 05:55:24 +00:00
len1 , err = strconv . ParseInt ( strings . TrimSpace ( lens [ 0 ] ) , 10 , 64 )
2014-01-07 09:33:27 +00:00
if err != nil {
return nil , nil , err
}
2014-05-05 14:26:17 +00:00
if len ( lens ) == 2 {
2022-07-14 05:55:24 +00:00
len2 , err = strconv . ParseInt ( lens [ 1 ] , 10 , 64 )
2014-05-05 14:26:17 +00:00
if err != nil {
return nil , nil , err
}
}
2014-01-07 09:33:27 +00:00
}
2022-04-22 02:48:53 +00:00
} else {
switch colType {
case "MEDIUMTEXT" , "LONGTEXT" , "TEXT" :
2022-07-14 05:55:24 +00:00
len1 , err = strconv . ParseInt ( * maxLength , 10 , 64 )
2022-04-22 02:48:53 +00:00
if err != nil {
return nil , nil , err
}
}
2014-01-07 09:33:27 +00:00
}
2021-04-11 08:47:04 +00:00
if isUnsigned {
colType = "UNSIGNED " + colType
2019-04-19 08:28:27 +00:00
}
2014-01-07 09:33:27 +00:00
col . Length = len1
col . Length2 = len2
2023-09-16 15:43:12 +00:00
if _ , ok := schemas . SqlTypes [ colType ] ; ! ok {
2021-12-14 01:00:35 +00:00
return nil , nil , fmt . Errorf ( "unknown colType %v" , colType )
2014-01-07 09:33:27 +00:00
}
2023-09-16 15:43:12 +00:00
col . SQLType = schemas . SQLType { Name : colType , DefaultLength : len1 , DefaultLength2 : len2 }
2014-01-07 09:33:27 +00:00
if colKey == "PRI" {
col . IsPrimaryKey = true
}
2021-12-14 01:00:35 +00:00
// if colKey == "UNI" {
// col.is
// }
2014-01-07 09:33:27 +00:00
if extra == "auto_increment" {
col . IsAutoIncrement = true
}
2019-09-29 04:31:06 +00:00
if ! col . DefaultIsEmpty {
2020-09-02 12:37:16 +00:00
if ! alreadyQuoted && col . SQLType . IsText ( ) {
2019-09-29 04:31:06 +00:00
col . Default = "'" + col . Default + "'"
2020-09-02 12:37:16 +00:00
} else if col . SQLType . IsTime ( ) && ! alreadyQuoted && col . Default != "CURRENT_TIMESTAMP" {
2014-01-07 09:33:27 +00:00
col . Default = "'" + col . Default + "'"
}
}
cols [ col . Name ] = col
colSeq = append ( colSeq , col . Name )
}
2021-07-20 05:46:24 +00:00
if rows . Err ( ) != nil {
return nil , nil , rows . Err ( )
}
2014-01-07 09:33:27 +00:00
return colSeq , cols , nil
}
2020-03-24 02:19:24 +00:00
func ( db * mysql ) GetTables ( queryer core . Queryer , ctx context . Context ) ( [ ] * schemas . Table , error ) {
2020-02-24 08:53:18 +00:00
args := [ ] interface { } { db . uri . DBName }
2023-07-01 03:40:09 +00:00
s := "SELECT `TABLE_NAME`, `ENGINE`, `AUTO_INCREMENT`, `TABLE_COMMENT`, `TABLE_COLLATION` from " +
2016-02-19 23:34:40 +00:00
"`INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? AND (`ENGINE`='MyISAM' OR `ENGINE` = 'InnoDB' OR `ENGINE` = 'TokuDB')"
2014-04-18 10:39:07 +00:00
2020-03-24 02:19:24 +00:00
rows , err := queryer . QueryContext ( ctx , s , args ... )
2014-01-07 09:33:27 +00:00
if err != nil {
return nil , err
}
2014-04-18 10:39:07 +00:00
defer rows . Close ( )
2014-01-07 09:33:27 +00:00
2020-02-24 08:53:18 +00:00
tables := make ( [ ] * schemas . Table , 0 )
2014-01-07 09:33:27 +00:00
for rows . Next ( ) {
2020-02-24 08:53:18 +00:00
table := schemas . NewEmptyTable ( )
2023-07-01 03:40:09 +00:00
var name , engine , collation string
2020-03-09 06:37:03 +00:00
var autoIncr , comment * string
2023-07-01 03:40:09 +00:00
err = rows . Scan ( & name , & engine , & autoIncr , & comment , & collation )
2014-01-07 09:33:27 +00:00
if err != nil {
return nil , err
}
table . Name = name
2020-03-09 06:37:03 +00:00
if comment != nil {
table . Comment = * comment
}
2014-08-30 14:17:59 +00:00
table . StoreEngine = engine
2023-07-01 03:40:09 +00:00
table . Collation = collation
2014-01-07 09:33:27 +00:00
tables = append ( tables , table )
}
2021-07-20 05:46:24 +00:00
if rows . Err ( ) != nil {
return nil , rows . Err ( )
}
2014-01-07 09:33:27 +00:00
return tables , nil
}
2020-03-06 07:48:32 +00:00
func ( db * mysql ) SetQuotePolicy ( quotePolicy QuotePolicy ) {
switch quotePolicy {
case QuotePolicyNone :
2022-05-30 10:36:23 +00:00
q := mysqlQuoter
2020-03-06 07:48:32 +00:00
q . IsReserved = schemas . AlwaysNoReserve
db . quoter = q
case QuotePolicyReserved :
2022-05-30 10:36:23 +00:00
q := mysqlQuoter
2020-03-06 07:48:32 +00:00
q . IsReserved = db . IsReserved
db . quoter = q
case QuotePolicyAlways :
fallthrough
default :
db . quoter = mysqlQuoter
}
}
2020-03-24 02:19:24 +00:00
func ( db * mysql ) GetIndexes ( queryer core . Queryer , ctx context . Context , tableName string ) ( map [ string ] * schemas . Index , error ) {
2020-02-24 08:53:18 +00:00
args := [ ] interface { } { db . uri . DBName , tableName }
2022-05-30 10:36:23 +00:00
s := "SELECT `INDEX_NAME`, `NON_UNIQUE`, `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? ORDER BY `SEQ_IN_INDEX`"
2014-04-18 10:39:07 +00:00
2020-03-24 02:19:24 +00:00
rows , err := queryer . QueryContext ( ctx , s , args ... )
2014-01-07 09:33:27 +00:00
if err != nil {
return nil , err
}
2014-04-18 10:39:07 +00:00
defer rows . Close ( )
2014-01-07 09:33:27 +00:00
2021-07-20 05:46:24 +00:00
indexes := make ( map [ string ] * schemas . Index )
2014-01-07 09:33:27 +00:00
for rows . Next ( ) {
var indexType int
var indexName , colName , nonUnique string
err = rows . Scan ( & indexName , & nonUnique , & colName )
if err != nil {
return nil , err
}
if indexName == "PRIMARY" {
continue
}
2021-07-20 05:46:24 +00:00
if nonUnique == "YES" || nonUnique == "1" {
2020-02-24 08:53:18 +00:00
indexType = schemas . IndexType
2014-01-07 09:33:27 +00:00
} else {
2020-02-24 08:53:18 +00:00
indexType = schemas . UniqueType
2014-01-07 09:33:27 +00:00
}
colName = strings . Trim ( colName , "` " )
2015-02-17 07:02:28 +00:00
var isRegular bool
2014-01-07 09:33:27 +00:00
if strings . HasPrefix ( indexName , "IDX_" + tableName ) || strings . HasPrefix ( indexName , "UQE_" + tableName ) {
2017-01-09 01:52:23 +00:00
indexName = indexName [ 5 + len ( tableName ) : ]
2015-02-17 07:02:28 +00:00
isRegular = true
2014-01-07 09:33:27 +00:00
}
2020-02-24 08:53:18 +00:00
var index * schemas . Index
2014-01-07 09:33:27 +00:00
var ok bool
if index , ok = indexes [ indexName ] ; ! ok {
2020-02-24 08:53:18 +00:00
index = new ( schemas . Index )
2015-02-17 07:02:28 +00:00
index . IsRegular = isRegular
2014-01-07 09:33:27 +00:00
index . Type = indexType
index . Name = indexName
indexes [ indexName ] = index
}
index . AddColumn ( colName )
}
2021-07-20 05:46:24 +00:00
if rows . Err ( ) != nil {
return nil , rows . Err ( )
}
2014-01-07 09:33:27 +00:00
return indexes , nil
}
2021-08-24 05:46:08 +00:00
func ( db * mysql ) CreateTableSQL ( ctx context . Context , queryer core . Queryer , table * schemas . Table , tableName string ) ( string , bool , error ) {
2018-03-08 01:30:38 +00:00
if tableName == "" {
tableName = table . Name
}
2021-08-14 02:57:47 +00:00
quoter := db . dialect . Quoter ( )
var b strings . Builder
b . WriteString ( "CREATE TABLE IF NOT EXISTS " )
quoter . QuoteTo ( & b , tableName )
b . WriteString ( " (" )
for i , colName := range table . ColumnsSeq ( ) {
col := table . GetColumn ( colName )
2023-07-01 03:40:09 +00:00
s , _ := ColumnString ( db . dialect , col , col . IsPrimaryKey && len ( table . PrimaryKeys ) == 1 , true )
2021-08-14 02:57:47 +00:00
b . WriteString ( s )
if len ( col . Comment ) > 0 {
b . WriteString ( " COMMENT '" )
b . WriteString ( col . Comment )
b . WriteString ( "'" )
2018-03-08 01:30:38 +00:00
}
2021-08-14 02:57:47 +00:00
if i != len ( table . ColumnsSeq ( ) ) - 1 {
b . WriteString ( ", " )
2018-03-08 01:30:38 +00:00
}
2021-08-14 02:57:47 +00:00
}
2018-03-08 01:30:38 +00:00
2021-08-14 02:57:47 +00:00
if len ( table . PrimaryKeys ) > 1 {
b . WriteString ( ", PRIMARY KEY (" )
b . WriteString ( quoter . Join ( table . PrimaryKeys , "," ) )
b . WriteString ( ")" )
2018-03-08 01:30:38 +00:00
}
2021-08-14 02:57:47 +00:00
b . WriteString ( ")" )
2018-03-08 01:30:38 +00:00
2020-03-07 08:51:30 +00:00
if table . StoreEngine != "" {
2021-08-14 02:57:47 +00:00
b . WriteString ( " ENGINE=" )
b . WriteString ( table . StoreEngine )
2018-03-08 01:30:38 +00:00
}
2022-05-30 10:36:23 +00:00
charset := table . Charset
2018-03-08 01:30:38 +00:00
if len ( charset ) == 0 {
charset = db . URI ( ) . Charset
2019-07-24 06:38:05 +00:00
}
2018-08-25 12:34:45 +00:00
if len ( charset ) != 0 {
2021-08-14 02:57:47 +00:00
b . WriteString ( " DEFAULT CHARSET " )
b . WriteString ( charset )
2018-03-08 01:30:38 +00:00
}
if db . rowFormat != "" {
2021-08-14 02:57:47 +00:00
b . WriteString ( " ROW_FORMAT=" )
b . WriteString ( db . rowFormat )
2018-03-08 01:30:38 +00:00
}
2021-08-24 05:46:08 +00:00
2021-11-12 12:58:05 +00:00
if table . Comment != "" {
b . WriteString ( " COMMENT='" )
b . WriteString ( table . Comment )
b . WriteString ( "'" )
}
2021-08-24 05:46:08 +00:00
return b . String ( ) , true , nil
2018-03-08 01:30:38 +00:00
}
2020-02-24 08:53:18 +00:00
func ( db * mysql ) Filters ( ) [ ] Filter {
2020-02-25 00:01:36 +00:00
return [ ] Filter { }
2014-01-07 09:33:27 +00:00
}
2017-03-23 06:05:32 +00:00
2021-07-04 13:23:17 +00:00
type mysqlDriver struct {
2021-07-06 08:06:04 +00:00
baseDriver
2021-07-04 13:23:17 +00:00
}
2021-07-20 16:12:20 +00:00
func ( p * mysqlDriver ) Features ( ) * DriverFeatures {
return & DriverFeatures {
SupportReturnInsertedID : true ,
}
}
2021-07-04 13:23:17 +00:00
func ( p * mysqlDriver ) Parse ( driverName , dataSourceName string ) ( * URI , error ) {
dsnPattern := regexp . MustCompile (
` ^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)? ` + // [user[:password]@]
` (?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)? ` + // [net[(addr)]]
` \/(?P<dbname>.*?) ` + // /dbname
` (?:\?(?P<params>[^\?]*))?$ ` ) // [?param1=value1¶mN=valueN]
matches := dsnPattern . FindStringSubmatch ( dataSourceName )
// tlsConfigRegister := make(map[string]*tls.Config)
names := dsnPattern . SubexpNames ( )
uri := & URI { DBType : schemas . MYSQL }
for i , match := range matches {
switch names [ i ] {
case "dbname" :
uri . DBName = match
case "params" :
if len ( match ) > 0 {
kvs := strings . Split ( match , "&" )
for _ , kv := range kvs {
splits := strings . Split ( kv , "=" )
if len ( splits ) == 2 {
2021-07-20 05:46:24 +00:00
if splits [ 0 ] == "charset" {
2021-07-04 13:23:17 +00:00
uri . Charset = splits [ 1 ]
}
}
}
}
}
}
return uri , nil
}
func ( p * mysqlDriver ) GenScanResult ( colType string ) ( interface { } , error ) {
2023-02-28 15:42:42 +00:00
colType = strings . Replace ( colType , "UNSIGNED " , "" , - 1 )
2021-07-04 13:23:17 +00:00
switch colType {
2023-02-28 15:42:42 +00:00
case "CHAR" , "VARCHAR" , "TINYTEXT" , "TEXT" , "MEDIUMTEXT" , "LONGTEXT" , "ENUM" , "SET" , "JSON" :
2021-07-04 13:23:17 +00:00
var s sql . NullString
return & s , nil
case "BIGINT" :
var s sql . NullInt64
return & s , nil
case "TINYINT" , "SMALLINT" , "MEDIUMINT" , "INT" :
var s sql . NullInt32
return & s , nil
2021-07-20 05:46:24 +00:00
case "FLOAT" , "REAL" , "DOUBLE PRECISION" , "DOUBLE" :
2021-07-04 13:23:17 +00:00
var s sql . NullFloat64
return & s , nil
case "DECIMAL" , "NUMERIC" :
var s sql . NullString
return & s , nil
2021-07-20 05:46:24 +00:00
case "DATETIME" , "TIMESTAMP" :
2021-07-04 13:23:17 +00:00
var s sql . NullTime
return & s , nil
case "BIT" :
var s sql . RawBytes
return & s , nil
case "BINARY" , "VARBINARY" , "TINYBLOB" , "BLOB" , "MEDIUMBLOB" , "LONGBLOB" :
var r sql . RawBytes
return & r , nil
default :
var r sql . RawBytes
return & r , nil
}
}