You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

mysql.go 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. // Copyright 2015 The Xorm Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package dialects
  5. import (
  6. "context"
  7. "crypto/tls"
  8. "database/sql"
  9. "errors"
  10. "fmt"
  11. "regexp"
  12. "strconv"
  13. "strings"
  14. "time"
  15. "xorm.io/xorm/core"
  16. "xorm.io/xorm/schemas"
  17. )
  18. var (
  19. mysqlReservedWords = map[string]bool{
  20. "ADD": true,
  21. "ALL": true,
  22. "ALTER": true,
  23. "ANALYZE": true,
  24. "AND": true,
  25. "AS": true,
  26. "ASC": true,
  27. "ASENSITIVE": true,
  28. "BEFORE": true,
  29. "BETWEEN": true,
  30. "BIGINT": true,
  31. "BINARY": true,
  32. "BLOB": true,
  33. "BOTH": true,
  34. "BY": true,
  35. "CALL": true,
  36. "CASCADE": true,
  37. "CASE": true,
  38. "CHANGE": true,
  39. "CHAR": true,
  40. "CHARACTER": true,
  41. "CHECK": true,
  42. "COLLATE": true,
  43. "COLUMN": true,
  44. "CONDITION": true,
  45. "CONNECTION": true,
  46. "CONSTRAINT": true,
  47. "CONTINUE": true,
  48. "CONVERT": true,
  49. "CREATE": true,
  50. "CROSS": true,
  51. "CURRENT_DATE": true,
  52. "CURRENT_TIME": true,
  53. "CURRENT_TIMESTAMP": true,
  54. "CURRENT_USER": true,
  55. "CURSOR": true,
  56. "DATABASE": true,
  57. "DATABASES": true,
  58. "DAY_HOUR": true,
  59. "DAY_MICROSECOND": true,
  60. "DAY_MINUTE": true,
  61. "DAY_SECOND": true,
  62. "DEC": true,
  63. "DECIMAL": true,
  64. "DECLARE": true,
  65. "DEFAULT": true,
  66. "DELAYED": true,
  67. "DELETE": true,
  68. "DESC": true,
  69. "DESCRIBE": true,
  70. "DETERMINISTIC": true,
  71. "DISTINCT": true,
  72. "DISTINCTROW": true,
  73. "DIV": true,
  74. "DOUBLE": true,
  75. "DROP": true,
  76. "DUAL": true,
  77. "EACH": true,
  78. "ELSE": true,
  79. "ELSEIF": true,
  80. "ENCLOSED": true,
  81. "ESCAPED": true,
  82. "EXISTS": true,
  83. "EXIT": true,
  84. "EXPLAIN": true,
  85. "FALSE": true,
  86. "FETCH": true,
  87. "FLOAT": true,
  88. "FLOAT4": true,
  89. "FLOAT8": true,
  90. "FOR": true,
  91. "FORCE": true,
  92. "FOREIGN": true,
  93. "FROM": true,
  94. "FULLTEXT": true,
  95. "GOTO": true,
  96. "GRANT": true,
  97. "GROUP": true,
  98. "HAVING": true,
  99. "HIGH_PRIORITY": true,
  100. "HOUR_MICROSECOND": true,
  101. "HOUR_MINUTE": true,
  102. "HOUR_SECOND": true,
  103. "IF": true,
  104. "IGNORE": true,
  105. "IN": true, "INDEX": true,
  106. "INFILE": true, "INNER": true, "INOUT": true,
  107. "INSENSITIVE": true, "INSERT": true, "INT": true,
  108. "INT1": true, "INT2": true, "INT3": true,
  109. "INT4": true, "INT8": true, "INTEGER": true,
  110. "INTERVAL": true, "INTO": true, "IS": true,
  111. "ITERATE": true, "JOIN": true, "KEY": true,
  112. "KEYS": true, "KILL": true, "LABEL": true,
  113. "LEADING": true, "LEAVE": true, "LEFT": true,
  114. "LIKE": true, "LIMIT": true, "LINEAR": true,
  115. "LINES": true, "LOAD": true, "LOCALTIME": true,
  116. "LOCALTIMESTAMP": true, "LOCK": true, "LONG": true,
  117. "LONGBLOB": true, "LONGTEXT": true, "LOOP": true,
  118. "LOW_PRIORITY": true, "MATCH": true, "MEDIUMBLOB": true,
  119. "MEDIUMINT": true, "MEDIUMTEXT": true, "MIDDLEINT": true,
  120. "MINUTE_MICROSECOND": true, "MINUTE_SECOND": true, "MOD": true,
  121. "MODIFIES": true, "NATURAL": true, "NOT": true,
  122. "NO_WRITE_TO_BINLOG": true, "NULL": true, "NUMERIC": true,
  123. "ON OPTIMIZE": true, "OPTION": true,
  124. "OPTIONALLY": true, "OR": true, "ORDER": true,
  125. "OUT": true, "OUTER": true, "OUTFILE": true,
  126. "PRECISION": true, "PRIMARY": true, "PROCEDURE": true,
  127. "PURGE": true, "RAID0": true, "RANGE": true,
  128. "READ": true, "READS": true, "REAL": true,
  129. "REFERENCES": true, "REGEXP": true, "RELEASE": true,
  130. "RENAME": true, "REPEAT": true, "REPLACE": true,
  131. "REQUIRE": true, "RESTRICT": true, "RETURN": true,
  132. "REVOKE": true, "RIGHT": true, "RLIKE": true,
  133. "SCHEMA": true, "SCHEMAS": true, "SECOND_MICROSECOND": true,
  134. "SELECT": true, "SENSITIVE": true, "SEPARATOR": true,
  135. "SET": true, "SHOW": true, "SMALLINT": true,
  136. "SPATIAL": true, "SPECIFIC": true, "SQL": true,
  137. "SQLEXCEPTION": true, "SQLSTATE": true, "SQLWARNING": true,
  138. "SQL_BIG_RESULT": true, "SQL_CALC_FOUND_ROWS": true, "SQL_SMALL_RESULT": true,
  139. "SSL": true, "STARTING": true, "STRAIGHT_JOIN": true,
  140. "TABLE": true, "TERMINATED": true, "THEN": true,
  141. "TINYBLOB": true, "TINYINT": true, "TINYTEXT": true,
  142. "TO": true, "TRAILING": true, "TRIGGER": true,
  143. "TRUE": true, "UNDO": true, "UNION": true,
  144. "UNIQUE": true, "UNLOCK": true, "UNSIGNED": true,
  145. "UPDATE": true, "USAGE": true, "USE": true,
  146. "USING": true, "UTC_DATE": true, "UTC_TIME": true,
  147. "UTC_TIMESTAMP": true, "VALUES": true, "VARBINARY": true,
  148. "VARCHAR": true,
  149. "VARCHARACTER": true,
  150. "VARYING": true,
  151. "WHEN": true,
  152. "WHERE": true,
  153. "WHILE": true,
  154. "WITH": true,
  155. "WRITE": true,
  156. "X509": true,
  157. "XOR": true,
  158. "YEAR_MONTH": true,
  159. "ZEROFILL": true,
  160. }
  161. mysqlQuoter = schemas.Quoter{
  162. Prefix: '`',
  163. Suffix: '`',
  164. IsReserved: schemas.AlwaysReserve,
  165. }
  166. )
  167. type mysql struct {
  168. Base
  169. net string
  170. addr string
  171. params map[string]string
  172. loc *time.Location
  173. timeout time.Duration
  174. tls *tls.Config
  175. allowAllFiles bool
  176. allowOldPasswords bool
  177. clientFoundRows bool
  178. rowFormat string
  179. }
  180. func (db *mysql) Init(uri *URI) error {
  181. db.quoter = mysqlQuoter
  182. return db.Base.Init(db, uri)
  183. }
  184. var (
  185. mysqlColAliases = map[string]string{
  186. "numeric": "decimal",
  187. }
  188. )
  189. // Alias returns a alias of column
  190. func (db *mysql) Alias(col string) string {
  191. v, ok := mysqlColAliases[strings.ToLower(col)]
  192. if ok {
  193. return v
  194. }
  195. return col
  196. }
  197. func (db *mysql) Version(ctx context.Context, queryer core.Queryer) (*schemas.Version, error) {
  198. rows, err := queryer.QueryContext(ctx, "SELECT @@VERSION")
  199. if err != nil {
  200. return nil, err
  201. }
  202. defer rows.Close()
  203. var version string
  204. if !rows.Next() {
  205. if rows.Err() != nil {
  206. return nil, rows.Err()
  207. }
  208. return nil, errors.New("unknow version")
  209. }
  210. if err := rows.Scan(&version); err != nil {
  211. return nil, err
  212. }
  213. fields := strings.Split(version, "-")
  214. if len(fields) == 3 && fields[1] == "TiDB" {
  215. // 5.7.25-TiDB-v3.0.3
  216. return &schemas.Version{
  217. Number: strings.TrimPrefix(fields[2], "v"),
  218. Level: fields[0],
  219. Edition: fields[1],
  220. }, nil
  221. }
  222. var edition string
  223. if len(fields) == 2 {
  224. edition = fields[1]
  225. }
  226. return &schemas.Version{
  227. Number: fields[0],
  228. Edition: edition,
  229. }, nil
  230. }
  231. func (db *mysql) SetParams(params map[string]string) {
  232. rowFormat, ok := params["rowFormat"]
  233. if ok {
  234. var t = strings.ToUpper(rowFormat)
  235. switch t {
  236. case "COMPACT":
  237. fallthrough
  238. case "REDUNDANT":
  239. fallthrough
  240. case "DYNAMIC":
  241. fallthrough
  242. case "COMPRESSED":
  243. db.rowFormat = t
  244. }
  245. }
  246. }
  247. func (db *mysql) SQLType(c *schemas.Column) string {
  248. var res string
  249. var isUnsigned bool
  250. switch t := c.SQLType.Name; t {
  251. case schemas.Bool:
  252. res = schemas.TinyInt
  253. c.Length = 1
  254. case schemas.Serial:
  255. c.IsAutoIncrement = true
  256. c.IsPrimaryKey = true
  257. c.Nullable = false
  258. res = schemas.Int
  259. case schemas.BigSerial:
  260. c.IsAutoIncrement = true
  261. c.IsPrimaryKey = true
  262. c.Nullable = false
  263. res = schemas.BigInt
  264. case schemas.Bytea:
  265. res = schemas.Blob
  266. case schemas.TimeStampz:
  267. res = schemas.Char
  268. c.Length = 64
  269. case schemas.Enum: // mysql enum
  270. res = schemas.Enum
  271. res += "("
  272. opts := ""
  273. for v := range c.EnumOptions {
  274. opts += fmt.Sprintf(",'%v'", v)
  275. }
  276. res += strings.TrimLeft(opts, ",")
  277. res += ")"
  278. case schemas.Set: // mysql set
  279. res = schemas.Set
  280. res += "("
  281. opts := ""
  282. for v := range c.SetOptions {
  283. opts += fmt.Sprintf(",'%v'", v)
  284. }
  285. res += strings.TrimLeft(opts, ",")
  286. res += ")"
  287. case schemas.NVarchar:
  288. res = schemas.Varchar
  289. case schemas.Uuid:
  290. res = schemas.Varchar
  291. c.Length = 40
  292. case schemas.Json:
  293. res = schemas.Text
  294. case schemas.UnsignedInt:
  295. res = schemas.Int
  296. isUnsigned = true
  297. case schemas.UnsignedBigInt:
  298. res = schemas.BigInt
  299. isUnsigned = true
  300. case schemas.UnsignedMediumInt:
  301. res = schemas.MediumInt
  302. isUnsigned = true
  303. case schemas.UnsignedSmallInt:
  304. res = schemas.SmallInt
  305. isUnsigned = true
  306. case schemas.UnsignedTinyInt:
  307. res = schemas.TinyInt
  308. isUnsigned = true
  309. default:
  310. res = t
  311. }
  312. hasLen1 := (c.Length > 0)
  313. hasLen2 := (c.Length2 > 0)
  314. if res == schemas.BigInt && !hasLen1 && !hasLen2 {
  315. c.Length = 20
  316. hasLen1 = true
  317. }
  318. if hasLen2 {
  319. res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")"
  320. } else if hasLen1 {
  321. res += "(" + strconv.Itoa(c.Length) + ")"
  322. }
  323. if isUnsigned {
  324. res += " UNSIGNED"
  325. }
  326. return res
  327. }
  328. func (db *mysql) ColumnTypeKind(t string) int {
  329. switch strings.ToUpper(t) {
  330. case "DATETIME":
  331. return schemas.TIME_TYPE
  332. case "CHAR", "VARCHAR", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "ENUM", "SET":
  333. return schemas.TEXT_TYPE
  334. case "BIGINT", "TINYINT", "SMALLINT", "MEDIUMINT", "INT", "FLOAT", "REAL", "DOUBLE PRECISION", "DECIMAL", "NUMERIC", "BIT":
  335. return schemas.NUMERIC_TYPE
  336. case "BINARY", "VARBINARY", "TINYBLOB", "BLOB", "MEDIUMBLOB", "LONGBLOB":
  337. return schemas.BLOB_TYPE
  338. default:
  339. return schemas.UNKNOW_TYPE
  340. }
  341. }
  342. func (db *mysql) IsReserved(name string) bool {
  343. _, ok := mysqlReservedWords[strings.ToUpper(name)]
  344. return ok
  345. }
  346. func (db *mysql) AutoIncrStr() string {
  347. return "AUTO_INCREMENT"
  348. }
  349. func (db *mysql) IndexCheckSQL(tableName, idxName string) (string, []interface{}) {
  350. args := []interface{}{db.uri.DBName, tableName, idxName}
  351. sql := "SELECT `INDEX_NAME` FROM `INFORMATION_SCHEMA`.`STATISTICS`"
  352. sql += " WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `INDEX_NAME`=?"
  353. return sql, args
  354. }
  355. func (db *mysql) IsTableExist(queryer core.Queryer, ctx context.Context, tableName string) (bool, error) {
  356. sql := "SELECT `TABLE_NAME` from `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? and `TABLE_NAME`=?"
  357. return db.HasRecords(queryer, ctx, sql, db.uri.DBName, tableName)
  358. }
  359. func (db *mysql) AddColumnSQL(tableName string, col *schemas.Column) string {
  360. quoter := db.dialect.Quoter()
  361. s, _ := ColumnString(db, col, true)
  362. sql := fmt.Sprintf("ALTER TABLE %v ADD %v", quoter.Quote(tableName), s)
  363. if len(col.Comment) > 0 {
  364. sql += " COMMENT '" + col.Comment + "'"
  365. }
  366. return sql
  367. }
  368. func (db *mysql) GetColumns(queryer core.Queryer, ctx context.Context, tableName string) ([]string, map[string]*schemas.Column, error) {
  369. args := []interface{}{db.uri.DBName, tableName}
  370. alreadyQuoted := "(INSTR(VERSION(), 'maria') > 0 && " +
  371. "(SUBSTRING_INDEX(VERSION(), '.', 1) > 10 || " +
  372. "(SUBSTRING_INDEX(VERSION(), '.', 1) = 10 && " +
  373. "(SUBSTRING_INDEX(SUBSTRING(VERSION(), 4), '.', 1) > 2 || " +
  374. "(SUBSTRING_INDEX(SUBSTRING(VERSION(), 4), '.', 1) = 2 && " +
  375. "SUBSTRING_INDEX(SUBSTRING(VERSION(), 6), '-', 1) >= 7)))))"
  376. s := "SELECT `COLUMN_NAME`, `IS_NULLABLE`, `COLUMN_DEFAULT`, `COLUMN_TYPE`," +
  377. " `COLUMN_KEY`, `EXTRA`, `COLUMN_COMMENT`, " +
  378. alreadyQuoted + " AS NEEDS_QUOTE " +
  379. "FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?" +
  380. " ORDER BY `COLUMNS`.ORDINAL_POSITION"
  381. rows, err := queryer.QueryContext(ctx, s, args...)
  382. if err != nil {
  383. return nil, nil, err
  384. }
  385. defer rows.Close()
  386. cols := make(map[string]*schemas.Column)
  387. colSeq := make([]string, 0)
  388. for rows.Next() {
  389. col := new(schemas.Column)
  390. col.Indexes = make(map[string]int)
  391. var columnName, nullableStr, colType, colKey, extra, comment string
  392. var alreadyQuoted, isUnsigned bool
  393. var colDefault *string
  394. err = rows.Scan(&columnName, &nullableStr, &colDefault, &colType, &colKey, &extra, &comment, &alreadyQuoted)
  395. if err != nil {
  396. return nil, nil, err
  397. }
  398. col.Name = strings.Trim(columnName, "` ")
  399. col.Comment = comment
  400. if nullableStr == "YES" {
  401. col.Nullable = true
  402. }
  403. if colDefault != nil && (!alreadyQuoted || *colDefault != "NULL") {
  404. col.Default = *colDefault
  405. col.DefaultIsEmpty = false
  406. } else {
  407. col.DefaultIsEmpty = true
  408. }
  409. fields := strings.Fields(colType)
  410. if len(fields) == 2 && fields[1] == "unsigned" {
  411. isUnsigned = true
  412. }
  413. colType = fields[0]
  414. cts := strings.Split(colType, "(")
  415. colName := cts[0]
  416. // Remove the /* mariadb-5.3 */ suffix from coltypes
  417. colName = strings.TrimSuffix(colName, "/* mariadb-5.3 */")
  418. colType = strings.ToUpper(colName)
  419. var len1, len2 int
  420. if len(cts) == 2 {
  421. idx := strings.Index(cts[1], ")")
  422. if colType == schemas.Enum && cts[1][0] == '\'' { // enum
  423. options := strings.Split(cts[1][0:idx], ",")
  424. col.EnumOptions = make(map[string]int)
  425. for k, v := range options {
  426. v = strings.TrimSpace(v)
  427. v = strings.Trim(v, "'")
  428. col.EnumOptions[v] = k
  429. }
  430. } else if colType == schemas.Set && cts[1][0] == '\'' {
  431. options := strings.Split(cts[1][0:idx], ",")
  432. col.SetOptions = make(map[string]int)
  433. for k, v := range options {
  434. v = strings.TrimSpace(v)
  435. v = strings.Trim(v, "'")
  436. col.SetOptions[v] = k
  437. }
  438. } else {
  439. lens := strings.Split(cts[1][0:idx], ",")
  440. len1, err = strconv.Atoi(strings.TrimSpace(lens[0]))
  441. if err != nil {
  442. return nil, nil, err
  443. }
  444. if len(lens) == 2 {
  445. len2, err = strconv.Atoi(lens[1])
  446. if err != nil {
  447. return nil, nil, err
  448. }
  449. }
  450. }
  451. }
  452. if isUnsigned {
  453. colType = "UNSIGNED " + colType
  454. }
  455. col.Length = len1
  456. col.Length2 = len2
  457. if _, ok := schemas.SqlTypes[colType]; ok {
  458. col.SQLType = schemas.SQLType{Name: colType, DefaultLength: len1, DefaultLength2: len2}
  459. } else {
  460. return nil, nil, fmt.Errorf("Unknown colType %v", colType)
  461. }
  462. if colKey == "PRI" {
  463. col.IsPrimaryKey = true
  464. }
  465. if colKey == "UNI" {
  466. // col.is
  467. }
  468. if extra == "auto_increment" {
  469. col.IsAutoIncrement = true
  470. }
  471. if !col.DefaultIsEmpty {
  472. if !alreadyQuoted && col.SQLType.IsText() {
  473. col.Default = "'" + col.Default + "'"
  474. } else if col.SQLType.IsTime() && !alreadyQuoted && col.Default != "CURRENT_TIMESTAMP" {
  475. col.Default = "'" + col.Default + "'"
  476. }
  477. }
  478. cols[col.Name] = col
  479. colSeq = append(colSeq, col.Name)
  480. }
  481. if rows.Err() != nil {
  482. return nil, nil, rows.Err()
  483. }
  484. return colSeq, cols, nil
  485. }
  486. func (db *mysql) GetTables(queryer core.Queryer, ctx context.Context) ([]*schemas.Table, error) {
  487. args := []interface{}{db.uri.DBName}
  488. s := "SELECT `TABLE_NAME`, `ENGINE`, `AUTO_INCREMENT`, `TABLE_COMMENT` from " +
  489. "`INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? AND (`ENGINE`='MyISAM' OR `ENGINE` = 'InnoDB' OR `ENGINE` = 'TokuDB')"
  490. rows, err := queryer.QueryContext(ctx, s, args...)
  491. if err != nil {
  492. return nil, err
  493. }
  494. defer rows.Close()
  495. tables := make([]*schemas.Table, 0)
  496. for rows.Next() {
  497. table := schemas.NewEmptyTable()
  498. var name, engine string
  499. var autoIncr, comment *string
  500. err = rows.Scan(&name, &engine, &autoIncr, &comment)
  501. if err != nil {
  502. return nil, err
  503. }
  504. table.Name = name
  505. if comment != nil {
  506. table.Comment = *comment
  507. }
  508. table.StoreEngine = engine
  509. tables = append(tables, table)
  510. }
  511. if rows.Err() != nil {
  512. return nil, rows.Err()
  513. }
  514. return tables, nil
  515. }
  516. func (db *mysql) SetQuotePolicy(quotePolicy QuotePolicy) {
  517. switch quotePolicy {
  518. case QuotePolicyNone:
  519. var q = mysqlQuoter
  520. q.IsReserved = schemas.AlwaysNoReserve
  521. db.quoter = q
  522. case QuotePolicyReserved:
  523. var q = mysqlQuoter
  524. q.IsReserved = db.IsReserved
  525. db.quoter = q
  526. case QuotePolicyAlways:
  527. fallthrough
  528. default:
  529. db.quoter = mysqlQuoter
  530. }
  531. }
  532. func (db *mysql) GetIndexes(queryer core.Queryer, ctx context.Context, tableName string) (map[string]*schemas.Index, error) {
  533. args := []interface{}{db.uri.DBName, tableName}
  534. s := "SELECT `INDEX_NAME`, `NON_UNIQUE`, `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?"
  535. rows, err := queryer.QueryContext(ctx, s, args...)
  536. if err != nil {
  537. return nil, err
  538. }
  539. defer rows.Close()
  540. indexes := make(map[string]*schemas.Index)
  541. for rows.Next() {
  542. var indexType int
  543. var indexName, colName, nonUnique string
  544. err = rows.Scan(&indexName, &nonUnique, &colName)
  545. if err != nil {
  546. return nil, err
  547. }
  548. if indexName == "PRIMARY" {
  549. continue
  550. }
  551. if nonUnique == "YES" || nonUnique == "1" {
  552. indexType = schemas.IndexType
  553. } else {
  554. indexType = schemas.UniqueType
  555. }
  556. colName = strings.Trim(colName, "` ")
  557. var isRegular bool
  558. if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
  559. indexName = indexName[5+len(tableName):]
  560. isRegular = true
  561. }
  562. var index *schemas.Index
  563. var ok bool
  564. if index, ok = indexes[indexName]; !ok {
  565. index = new(schemas.Index)
  566. index.IsRegular = isRegular
  567. index.Type = indexType
  568. index.Name = indexName
  569. indexes[indexName] = index
  570. }
  571. index.AddColumn(colName)
  572. }
  573. if rows.Err() != nil {
  574. return nil, rows.Err()
  575. }
  576. return indexes, nil
  577. }
  578. func (db *mysql) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) {
  579. if tableName == "" {
  580. tableName = table.Name
  581. }
  582. quoter := db.dialect.Quoter()
  583. var b strings.Builder
  584. b.WriteString("CREATE TABLE IF NOT EXISTS ")
  585. quoter.QuoteTo(&b, tableName)
  586. b.WriteString(" (")
  587. for i, colName := range table.ColumnsSeq() {
  588. col := table.GetColumn(colName)
  589. s, _ := ColumnString(db.dialect, col, col.IsPrimaryKey && len(table.PrimaryKeys) == 1)
  590. b.WriteString(s)
  591. if len(col.Comment) > 0 {
  592. b.WriteString(" COMMENT '")
  593. b.WriteString(col.Comment)
  594. b.WriteString("'")
  595. }
  596. if i != len(table.ColumnsSeq())-1 {
  597. b.WriteString(", ")
  598. }
  599. }
  600. if len(table.PrimaryKeys) > 1 {
  601. b.WriteString(", PRIMARY KEY (")
  602. b.WriteString(quoter.Join(table.PrimaryKeys, ","))
  603. b.WriteString(")")
  604. }
  605. b.WriteString(")")
  606. if table.StoreEngine != "" {
  607. b.WriteString(" ENGINE=")
  608. b.WriteString(table.StoreEngine)
  609. }
  610. var charset = table.Charset
  611. if len(charset) == 0 {
  612. charset = db.URI().Charset
  613. }
  614. if len(charset) != 0 {
  615. b.WriteString(" DEFAULT CHARSET ")
  616. b.WriteString(charset)
  617. }
  618. if db.rowFormat != "" {
  619. b.WriteString(" ROW_FORMAT=")
  620. b.WriteString(db.rowFormat)
  621. }
  622. return []string{b.String()}, true
  623. }
  624. func (db *mysql) Filters() []Filter {
  625. return []Filter{}
  626. }
  627. type mysqlDriver struct {
  628. baseDriver
  629. }
  630. func (p *mysqlDriver) Features() *DriverFeatures {
  631. return &DriverFeatures{
  632. SupportReturnInsertedID: true,
  633. }
  634. }
  635. func (p *mysqlDriver) Parse(driverName, dataSourceName string) (*URI, error) {
  636. dsnPattern := regexp.MustCompile(
  637. `^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
  638. `(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
  639. `\/(?P<dbname>.*?)` + // /dbname
  640. `(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1&paramN=valueN]
  641. matches := dsnPattern.FindStringSubmatch(dataSourceName)
  642. // tlsConfigRegister := make(map[string]*tls.Config)
  643. names := dsnPattern.SubexpNames()
  644. uri := &URI{DBType: schemas.MYSQL}
  645. for i, match := range matches {
  646. switch names[i] {
  647. case "dbname":
  648. uri.DBName = match
  649. case "params":
  650. if len(match) > 0 {
  651. kvs := strings.Split(match, "&")
  652. for _, kv := range kvs {
  653. splits := strings.Split(kv, "=")
  654. if len(splits) == 2 {
  655. if splits[0] == "charset" {
  656. uri.Charset = splits[1]
  657. }
  658. }
  659. }
  660. }
  661. }
  662. }
  663. return uri, nil
  664. }
  665. func (p *mysqlDriver) GenScanResult(colType string) (interface{}, error) {
  666. switch colType {
  667. case "CHAR", "VARCHAR", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "ENUM", "SET":
  668. var s sql.NullString
  669. return &s, nil
  670. case "BIGINT":
  671. var s sql.NullInt64
  672. return &s, nil
  673. case "TINYINT", "SMALLINT", "MEDIUMINT", "INT":
  674. var s sql.NullInt32
  675. return &s, nil
  676. case "FLOAT", "REAL", "DOUBLE PRECISION", "DOUBLE":
  677. var s sql.NullFloat64
  678. return &s, nil
  679. case "DECIMAL", "NUMERIC":
  680. var s sql.NullString
  681. return &s, nil
  682. case "DATETIME", "TIMESTAMP":
  683. var s sql.NullTime
  684. return &s, nil
  685. case "BIT":
  686. var s sql.RawBytes
  687. return &s, nil
  688. case "BINARY", "VARBINARY", "TINYBLOB", "BLOB", "MEDIUMBLOB", "LONGBLOB":
  689. var r sql.RawBytes
  690. return &r, nil
  691. default:
  692. var r sql.RawBytes
  693. return &r, nil
  694. }
  695. }
  696. type mymysqlDriver struct {
  697. mysqlDriver
  698. }
  699. func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*URI, error) {
  700. uri := &URI{DBType: schemas.MYSQL}
  701. pd := strings.SplitN(dataSourceName, "*", 2)
  702. if len(pd) == 2 {
  703. // Parse protocol part of URI
  704. p := strings.SplitN(pd[0], ":", 2)
  705. if len(p) != 2 {
  706. return nil, errors.New("Wrong protocol part of URI")
  707. }
  708. uri.Proto = p[0]
  709. options := strings.Split(p[1], ",")
  710. uri.Raddr = options[0]
  711. for _, o := range options[1:] {
  712. kv := strings.SplitN(o, "=", 2)
  713. var k, v string
  714. if len(kv) == 2 {
  715. k, v = kv[0], kv[1]
  716. } else {
  717. k, v = o, "true"
  718. }
  719. switch k {
  720. case "laddr":
  721. uri.Laddr = v
  722. case "timeout":
  723. to, err := time.ParseDuration(v)
  724. if err != nil {
  725. return nil, err
  726. }
  727. uri.Timeout = to
  728. default:
  729. return nil, errors.New("Unknown option: " + k)
  730. }
  731. }
  732. // Remove protocol part
  733. pd = pd[1:]
  734. }
  735. // Parse database part of URI
  736. dup := strings.SplitN(pd[0], "/", 3)
  737. if len(dup) != 3 {
  738. return nil, errors.New("Wrong database part of URI")
  739. }
  740. uri.DBName = dup[0]
  741. uri.User = dup[1]
  742. uri.Passwd = dup[2]
  743. return uri, nil
  744. }