mysql.go 18 KB

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