dialect_mysql.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  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 xorm
  5. import (
  6. "crypto/tls"
  7. "errors"
  8. "fmt"
  9. "regexp"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "github.com/go-xorm/core"
  14. )
  15. var (
  16. mysqlReservedWords = map[string]bool{
  17. "ADD": true,
  18. "ALL": true,
  19. "ALTER": true,
  20. "ANALYZE": true,
  21. "AND": true,
  22. "AS": true,
  23. "ASC": true,
  24. "ASENSITIVE": true,
  25. "BEFORE": true,
  26. "BETWEEN": true,
  27. "BIGINT": true,
  28. "BINARY": true,
  29. "BLOB": true,
  30. "BOTH": true,
  31. "BY": true,
  32. "CALL": true,
  33. "CASCADE": true,
  34. "CASE": true,
  35. "CHANGE": true,
  36. "CHAR": true,
  37. "CHARACTER": true,
  38. "CHECK": true,
  39. "COLLATE": true,
  40. "COLUMN": true,
  41. "CONDITION": true,
  42. "CONNECTION": true,
  43. "CONSTRAINT": true,
  44. "CONTINUE": true,
  45. "CONVERT": true,
  46. "CREATE": true,
  47. "CROSS": true,
  48. "CURRENT_DATE": true,
  49. "CURRENT_TIME": true,
  50. "CURRENT_TIMESTAMP": true,
  51. "CURRENT_USER": true,
  52. "CURSOR": true,
  53. "DATABASE": true,
  54. "DATABASES": true,
  55. "DAY_HOUR": true,
  56. "DAY_MICROSECOND": true,
  57. "DAY_MINUTE": true,
  58. "DAY_SECOND": true,
  59. "DEC": true,
  60. "DECIMAL": true,
  61. "DECLARE": true,
  62. "DEFAULT": true,
  63. "DELAYED": true,
  64. "DELETE": true,
  65. "DESC": true,
  66. "DESCRIBE": true,
  67. "DETERMINISTIC": true,
  68. "DISTINCT": true,
  69. "DISTINCTROW": true,
  70. "DIV": true,
  71. "DOUBLE": true,
  72. "DROP": true,
  73. "DUAL": true,
  74. "EACH": true,
  75. "ELSE": true,
  76. "ELSEIF": true,
  77. "ENCLOSED": true,
  78. "ESCAPED": true,
  79. "EXISTS": true,
  80. "EXIT": true,
  81. "EXPLAIN": true,
  82. "FALSE": true,
  83. "FETCH": true,
  84. "FLOAT": true,
  85. "FLOAT4": true,
  86. "FLOAT8": true,
  87. "FOR": true,
  88. "FORCE": true,
  89. "FOREIGN": true,
  90. "FROM": true,
  91. "FULLTEXT": true,
  92. "GOTO": true,
  93. "GRANT": true,
  94. "GROUP": true,
  95. "HAVING": true,
  96. "HIGH_PRIORITY": true,
  97. "HOUR_MICROSECOND": true,
  98. "HOUR_MINUTE": true,
  99. "HOUR_SECOND": true,
  100. "IF": true,
  101. "IGNORE": true,
  102. "IN": true, "INDEX": true,
  103. "INFILE": true, "INNER": true, "INOUT": true,
  104. "INSENSITIVE": true, "INSERT": true, "INT": true,
  105. "INT1": true, "INT2": true, "INT3": true,
  106. "INT4": true, "INT8": true, "INTEGER": true,
  107. "INTERVAL": true, "INTO": true, "IS": true,
  108. "ITERATE": true, "JOIN": true, "KEY": true,
  109. "KEYS": true, "KILL": true, "LABEL": true,
  110. "LEADING": true, "LEAVE": true, "LEFT": true,
  111. "LIKE": true, "LIMIT": true, "LINEAR": true,
  112. "LINES": true, "LOAD": true, "LOCALTIME": true,
  113. "LOCALTIMESTAMP": true, "LOCK": true, "LONG": true,
  114. "LONGBLOB": true, "LONGTEXT": true, "LOOP": true,
  115. "LOW_PRIORITY": true, "MATCH": true, "MEDIUMBLOB": true,
  116. "MEDIUMINT": true, "MEDIUMTEXT": true, "MIDDLEINT": true,
  117. "MINUTE_MICROSECOND": true, "MINUTE_SECOND": true, "MOD": true,
  118. "MODIFIES": true, "NATURAL": true, "NOT": true,
  119. "NO_WRITE_TO_BINLOG": true, "NULL": true, "NUMERIC": true,
  120. "ON OPTIMIZE": true, "OPTION": true,
  121. "OPTIONALLY": true, "OR": true, "ORDER": true,
  122. "OUT": true, "OUTER": true, "OUTFILE": true,
  123. "PRECISION": true, "PRIMARY": true, "PROCEDURE": true,
  124. "PURGE": true, "RAID0": true, "RANGE": true,
  125. "READ": true, "READS": true, "REAL": true,
  126. "REFERENCES": true, "REGEXP": true, "RELEASE": true,
  127. "RENAME": true, "REPEAT": true, "REPLACE": true,
  128. "REQUIRE": true, "RESTRICT": true, "RETURN": true,
  129. "REVOKE": true, "RIGHT": true, "RLIKE": true,
  130. "SCHEMA": true, "SCHEMAS": true, "SECOND_MICROSECOND": true,
  131. "SELECT": true, "SENSITIVE": true, "SEPARATOR": true,
  132. "SET": true, "SHOW": true, "SMALLINT": true,
  133. "SPATIAL": true, "SPECIFIC": true, "SQL": true,
  134. "SQLEXCEPTION": true, "SQLSTATE": true, "SQLWARNING": true,
  135. "SQL_BIG_RESULT": true, "SQL_CALC_FOUND_ROWS": true, "SQL_SMALL_RESULT": true,
  136. "SSL": true, "STARTING": true, "STRAIGHT_JOIN": true,
  137. "TABLE": true, "TERMINATED": true, "THEN": true,
  138. "TINYBLOB": true, "TINYINT": true, "TINYTEXT": true,
  139. "TO": true, "TRAILING": true, "TRIGGER": true,
  140. "TRUE": true, "UNDO": true, "UNION": true,
  141. "UNIQUE": true, "UNLOCK": true, "UNSIGNED": true,
  142. "UPDATE": true, "USAGE": true, "USE": true,
  143. "USING": true, "UTC_DATE": true, "UTC_TIME": true,
  144. "UTC_TIMESTAMP": true, "VALUES": true, "VARBINARY": true,
  145. "VARCHAR": true,
  146. "VARCHARACTER": true,
  147. "VARYING": true,
  148. "WHEN": true,
  149. "WHERE": true,
  150. "WHILE": true,
  151. "WITH": true,
  152. "WRITE": true,
  153. "X509": true,
  154. "XOR": true,
  155. "YEAR_MONTH": true,
  156. "ZEROFILL": true,
  157. }
  158. )
  159. type mysql struct {
  160. core.Base
  161. net string
  162. addr string
  163. params map[string]string
  164. loc *time.Location
  165. timeout time.Duration
  166. tls *tls.Config
  167. allowAllFiles bool
  168. allowOldPasswords bool
  169. clientFoundRows bool
  170. rowFormat string
  171. }
  172. func (db *mysql) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error {
  173. return db.Base.Init(d, db, uri, drivername, dataSourceName)
  174. }
  175. func (db *mysql) SetParams(params map[string]string) {
  176. rowFormat, ok := params["rowFormat"]
  177. if ok {
  178. var t = strings.ToUpper(rowFormat)
  179. switch t {
  180. case "COMPACT":
  181. fallthrough
  182. case "REDUNDANT":
  183. fallthrough
  184. case "DYNAMIC":
  185. fallthrough
  186. case "COMPRESSED":
  187. db.rowFormat = t
  188. break
  189. default:
  190. break
  191. }
  192. }
  193. }
  194. func (db *mysql) SqlType(c *core.Column) string {
  195. var res string
  196. switch t := c.SQLType.Name; t {
  197. case core.Bool:
  198. res = core.TinyInt
  199. c.Length = 1
  200. case core.Serial:
  201. c.IsAutoIncrement = true
  202. c.IsPrimaryKey = true
  203. c.Nullable = false
  204. res = core.Int
  205. case core.BigSerial:
  206. c.IsAutoIncrement = true
  207. c.IsPrimaryKey = true
  208. c.Nullable = false
  209. res = core.BigInt
  210. case core.Bytea:
  211. res = core.Blob
  212. case core.TimeStampz:
  213. res = core.Char
  214. c.Length = 64
  215. case core.Enum: //mysql enum
  216. res = core.Enum
  217. res += "("
  218. opts := ""
  219. for v := range c.EnumOptions {
  220. opts += fmt.Sprintf(",'%v'", v)
  221. }
  222. res += strings.TrimLeft(opts, ",")
  223. res += ")"
  224. case core.Set: //mysql set
  225. res = core.Set
  226. res += "("
  227. opts := ""
  228. for v := range c.SetOptions {
  229. opts += fmt.Sprintf(",'%v'", v)
  230. }
  231. res += strings.TrimLeft(opts, ",")
  232. res += ")"
  233. case core.NVarchar:
  234. res = core.Varchar
  235. case core.Uuid:
  236. res = core.Varchar
  237. c.Length = 40
  238. case core.Json:
  239. res = core.Text
  240. default:
  241. res = t
  242. }
  243. hasLen1 := (c.Length > 0)
  244. hasLen2 := (c.Length2 > 0)
  245. if res == core.BigInt && !hasLen1 && !hasLen2 {
  246. c.Length = 20
  247. hasLen1 = true
  248. }
  249. if hasLen2 {
  250. res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")"
  251. } else if hasLen1 {
  252. res += "(" + strconv.Itoa(c.Length) + ")"
  253. }
  254. return res
  255. }
  256. func (db *mysql) SupportInsertMany() bool {
  257. return true
  258. }
  259. func (db *mysql) IsReserved(name string) bool {
  260. _, ok := mysqlReservedWords[name]
  261. return ok
  262. }
  263. func (db *mysql) Quote(name string) string {
  264. return "`" + name + "`"
  265. }
  266. func (db *mysql) QuoteStr() string {
  267. return "`"
  268. }
  269. func (db *mysql) SupportEngine() bool {
  270. return true
  271. }
  272. func (db *mysql) AutoIncrStr() string {
  273. return "AUTO_INCREMENT"
  274. }
  275. func (db *mysql) SupportCharset() bool {
  276. return true
  277. }
  278. func (db *mysql) IndexOnTable() bool {
  279. return true
  280. }
  281. func (db *mysql) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
  282. args := []interface{}{db.DbName, tableName, idxName}
  283. sql := "SELECT `INDEX_NAME` FROM `INFORMATION_SCHEMA`.`STATISTICS`"
  284. sql += " WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `INDEX_NAME`=?"
  285. return sql, args
  286. }
  287. /*func (db *mysql) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
  288. args := []interface{}{db.DbName, tableName, colName}
  289. sql := "SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `COLUMN_NAME` = ?"
  290. return sql, args
  291. }*/
  292. func (db *mysql) TableCheckSql(tableName string) (string, []interface{}) {
  293. args := []interface{}{db.DbName, tableName}
  294. sql := "SELECT `TABLE_NAME` from `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? and `TABLE_NAME`=?"
  295. return sql, args
  296. }
  297. func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
  298. args := []interface{}{db.DbName, tableName}
  299. s := "SELECT `COLUMN_NAME`, `IS_NULLABLE`, `COLUMN_DEFAULT`, `COLUMN_TYPE`," +
  300. " `COLUMN_KEY`, `EXTRA`,`COLUMN_COMMENT` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?"
  301. db.LogSQL(s, args)
  302. rows, err := db.DB().Query(s, args...)
  303. if err != nil {
  304. return nil, nil, err
  305. }
  306. defer rows.Close()
  307. cols := make(map[string]*core.Column)
  308. colSeq := make([]string, 0)
  309. for rows.Next() {
  310. col := new(core.Column)
  311. col.Indexes = make(map[string]int)
  312. var columnName, isNullable, colType, colKey, extra, comment string
  313. var colDefault *string
  314. err = rows.Scan(&columnName, &isNullable, &colDefault, &colType, &colKey, &extra, &comment)
  315. if err != nil {
  316. return nil, nil, err
  317. }
  318. col.Name = strings.Trim(columnName, "` ")
  319. col.Comment = comment
  320. if "YES" == isNullable {
  321. col.Nullable = true
  322. }
  323. if colDefault != nil {
  324. col.Default = *colDefault
  325. if col.Default == "" {
  326. col.DefaultIsEmpty = true
  327. }
  328. }
  329. cts := strings.Split(colType, "(")
  330. colName := cts[0]
  331. colType = strings.ToUpper(colName)
  332. var len1, len2 int
  333. if len(cts) == 2 {
  334. idx := strings.Index(cts[1], ")")
  335. if colType == core.Enum && cts[1][0] == '\'' { //enum
  336. options := strings.Split(cts[1][0:idx], ",")
  337. col.EnumOptions = make(map[string]int)
  338. for k, v := range options {
  339. v = strings.TrimSpace(v)
  340. v = strings.Trim(v, "'")
  341. col.EnumOptions[v] = k
  342. }
  343. } else if colType == core.Set && cts[1][0] == '\'' {
  344. options := strings.Split(cts[1][0:idx], ",")
  345. col.SetOptions = make(map[string]int)
  346. for k, v := range options {
  347. v = strings.TrimSpace(v)
  348. v = strings.Trim(v, "'")
  349. col.SetOptions[v] = k
  350. }
  351. } else {
  352. lens := strings.Split(cts[1][0:idx], ",")
  353. len1, err = strconv.Atoi(strings.TrimSpace(lens[0]))
  354. if err != nil {
  355. return nil, nil, err
  356. }
  357. if len(lens) == 2 {
  358. len2, err = strconv.Atoi(lens[1])
  359. if err != nil {
  360. return nil, nil, err
  361. }
  362. }
  363. }
  364. }
  365. if colType == "FLOAT UNSIGNED" {
  366. colType = "FLOAT"
  367. }
  368. col.Length = len1
  369. col.Length2 = len2
  370. if _, ok := core.SqlTypes[colType]; ok {
  371. col.SQLType = core.SQLType{Name: colType, DefaultLength: len1, DefaultLength2: len2}
  372. } else {
  373. return nil, nil, fmt.Errorf("Unknown colType %v", colType)
  374. }
  375. if colKey == "PRI" {
  376. col.IsPrimaryKey = true
  377. }
  378. if colKey == "UNI" {
  379. //col.is
  380. }
  381. if extra == "auto_increment" {
  382. col.IsAutoIncrement = true
  383. }
  384. if col.SQLType.IsText() || col.SQLType.IsTime() {
  385. if col.Default != "" {
  386. col.Default = "'" + col.Default + "'"
  387. } else {
  388. if col.DefaultIsEmpty {
  389. col.Default = "''"
  390. }
  391. }
  392. }
  393. cols[col.Name] = col
  394. colSeq = append(colSeq, col.Name)
  395. }
  396. return colSeq, cols, nil
  397. }
  398. func (db *mysql) GetTables() ([]*core.Table, error) {
  399. args := []interface{}{db.DbName}
  400. s := "SELECT `TABLE_NAME`, `ENGINE`, `TABLE_ROWS`, `AUTO_INCREMENT`, `TABLE_COMMENT` from " +
  401. "`INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? AND (`ENGINE`='MyISAM' OR `ENGINE` = 'InnoDB' OR `ENGINE` = 'TokuDB')"
  402. db.LogSQL(s, args)
  403. rows, err := db.DB().Query(s, args...)
  404. if err != nil {
  405. return nil, err
  406. }
  407. defer rows.Close()
  408. tables := make([]*core.Table, 0)
  409. for rows.Next() {
  410. table := core.NewEmptyTable()
  411. var name, engine, tableRows, comment string
  412. var autoIncr *string
  413. err = rows.Scan(&name, &engine, &tableRows, &autoIncr, &comment)
  414. if err != nil {
  415. return nil, err
  416. }
  417. table.Name = name
  418. table.Comment = comment
  419. table.StoreEngine = engine
  420. tables = append(tables, table)
  421. }
  422. return tables, nil
  423. }
  424. func (db *mysql) GetIndexes(tableName string) (map[string]*core.Index, error) {
  425. args := []interface{}{db.DbName, tableName}
  426. s := "SELECT `INDEX_NAME`, `NON_UNIQUE`, `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?"
  427. db.LogSQL(s, args)
  428. rows, err := db.DB().Query(s, args...)
  429. if err != nil {
  430. return nil, err
  431. }
  432. defer rows.Close()
  433. indexes := make(map[string]*core.Index, 0)
  434. for rows.Next() {
  435. var indexType int
  436. var indexName, colName, nonUnique string
  437. err = rows.Scan(&indexName, &nonUnique, &colName)
  438. if err != nil {
  439. return nil, err
  440. }
  441. if indexName == "PRIMARY" {
  442. continue
  443. }
  444. if "YES" == nonUnique || nonUnique == "1" {
  445. indexType = core.IndexType
  446. } else {
  447. indexType = core.UniqueType
  448. }
  449. colName = strings.Trim(colName, "` ")
  450. var isRegular bool
  451. if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
  452. indexName = indexName[5+len(tableName):]
  453. isRegular = true
  454. }
  455. var index *core.Index
  456. var ok bool
  457. if index, ok = indexes[indexName]; !ok {
  458. index = new(core.Index)
  459. index.IsRegular = isRegular
  460. index.Type = indexType
  461. index.Name = indexName
  462. indexes[indexName] = index
  463. }
  464. index.AddColumn(colName)
  465. }
  466. return indexes, nil
  467. }
  468. func (db *mysql) CreateTableSql(table *core.Table, tableName, storeEngine, charset string) string {
  469. var sql string
  470. sql = "CREATE TABLE IF NOT EXISTS "
  471. if tableName == "" {
  472. tableName = table.Name
  473. }
  474. sql += db.Quote(tableName)
  475. sql += " ("
  476. if len(table.ColumnsSeq()) > 0 {
  477. pkList := table.PrimaryKeys
  478. for _, colName := range table.ColumnsSeq() {
  479. col := table.GetColumn(colName)
  480. if col.IsPrimaryKey && len(pkList) == 1 {
  481. sql += col.String(db)
  482. } else {
  483. sql += col.StringNoPk(db)
  484. }
  485. sql = strings.TrimSpace(sql)
  486. if len(col.Comment) > 0 {
  487. sql += " COMMENT '" + col.Comment + "'"
  488. }
  489. sql += ", "
  490. }
  491. if len(pkList) > 1 {
  492. sql += "PRIMARY KEY ( "
  493. sql += db.Quote(strings.Join(pkList, db.Quote(",")))
  494. sql += " ), "
  495. }
  496. sql = sql[:len(sql)-2]
  497. }
  498. sql += ")"
  499. if storeEngine != "" {
  500. sql += " ENGINE=" + storeEngine
  501. }
  502. if len(charset) == 0 {
  503. charset = db.URI().Charset
  504. } else if len(charset) > 0 {
  505. sql += " DEFAULT CHARSET " + charset
  506. }
  507. if db.rowFormat != "" {
  508. sql += " ROW_FORMAT=" + db.rowFormat
  509. }
  510. return sql
  511. }
  512. func (db *mysql) Filters() []core.Filter {
  513. return []core.Filter{&core.IdFilter{}}
  514. }
  515. type mymysqlDriver struct {
  516. }
  517. func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
  518. db := &core.Uri{DbType: core.MYSQL}
  519. pd := strings.SplitN(dataSourceName, "*", 2)
  520. if len(pd) == 2 {
  521. // Parse protocol part of URI
  522. p := strings.SplitN(pd[0], ":", 2)
  523. if len(p) != 2 {
  524. return nil, errors.New("Wrong protocol part of URI")
  525. }
  526. db.Proto = p[0]
  527. options := strings.Split(p[1], ",")
  528. db.Raddr = options[0]
  529. for _, o := range options[1:] {
  530. kv := strings.SplitN(o, "=", 2)
  531. var k, v string
  532. if len(kv) == 2 {
  533. k, v = kv[0], kv[1]
  534. } else {
  535. k, v = o, "true"
  536. }
  537. switch k {
  538. case "laddr":
  539. db.Laddr = v
  540. case "timeout":
  541. to, err := time.ParseDuration(v)
  542. if err != nil {
  543. return nil, err
  544. }
  545. db.Timeout = to
  546. default:
  547. return nil, errors.New("Unknown option: " + k)
  548. }
  549. }
  550. // Remove protocol part
  551. pd = pd[1:]
  552. }
  553. // Parse database part of URI
  554. dup := strings.SplitN(pd[0], "/", 3)
  555. if len(dup) != 3 {
  556. return nil, errors.New("Wrong database part of URI")
  557. }
  558. db.DbName = dup[0]
  559. db.User = dup[1]
  560. db.Passwd = dup[2]
  561. return db, nil
  562. }
  563. type mysqlDriver struct {
  564. }
  565. func (p *mysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
  566. dsnPattern := regexp.MustCompile(
  567. `^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
  568. `(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
  569. `\/(?P<dbname>.*?)` + // /dbname
  570. `(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1&paramN=valueN]
  571. matches := dsnPattern.FindStringSubmatch(dataSourceName)
  572. //tlsConfigRegister := make(map[string]*tls.Config)
  573. names := dsnPattern.SubexpNames()
  574. uri := &core.Uri{DbType: core.MYSQL}
  575. for i, match := range matches {
  576. switch names[i] {
  577. case "dbname":
  578. uri.DbName = match
  579. case "params":
  580. if len(match) > 0 {
  581. kvs := strings.Split(match, "&")
  582. for _, kv := range kvs {
  583. splits := strings.Split(kv, "=")
  584. if len(splits) == 2 {
  585. switch splits[0] {
  586. case "charset":
  587. uri.Charset = splits[1]
  588. }
  589. }
  590. }
  591. }
  592. }
  593. }
  594. return uri, nil
  595. }