dsn_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. // Go MySQL Driver - A MySQL-Driver for Go's database/sql package
  2. //
  3. // Copyright 2016 The Go-MySQL-Driver Authors. All rights reserved.
  4. //
  5. // This Source Code Form is subject to the terms of the Mozilla Public
  6. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  7. // You can obtain one at http://mozilla.org/MPL/2.0/.
  8. package mysql
  9. import (
  10. "crypto/tls"
  11. "fmt"
  12. "net/url"
  13. "testing"
  14. )
  15. var testDSNs = []struct {
  16. in string
  17. out string
  18. }{
  19. {"username:password@protocol(address)/dbname?param=value", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
  20. {"username:password@protocol(address)/dbname?param=value&connattrs=(foo=bar)", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[foo:bar]}"},
  21. {"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:true InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
  22. {"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true&multiStatements=true", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:true InterpolateParams:false MultiStatements:true ParseTime:false Strict:false ConnAttrs:map[]}"},
  23. {"user@unix(/path/to/socket)/dbname?charset=utf8", "&{User:user Passwd: Net:unix Addr:/path/to/socket DBName:dbname Params:map[charset:utf8] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
  24. {"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true", "&{User:user Passwd:password Net:tcp Addr:localhost:5555 DBName:dbname Params:map[charset:utf8] Collation:utf8_general_ci Loc:UTC TLSConfig:true tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
  25. {"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify", "&{User:user Passwd:password Net:tcp Addr:localhost:5555 DBName:dbname Params:map[charset:utf8mb4,utf8] Collation:utf8_general_ci Loc:UTC TLSConfig:skip-verify tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
  26. {"user:password@/dbname?loc=UTC&timeout=30s&readTimeout=1s&writeTimeout=1s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci", "&{User:user Passwd:password Net:tcp Addr:127.0.0.1:3306 DBName:dbname Params:map[] Collation:utf8mb4_unicode_ci Loc:UTC TLSConfig: tls:<nil> Timeout:30s ReadTimeout:1s WriteTimeout:1s AllowAllFiles:true AllowCleartextPasswords:false AllowOldPasswords:true ClientFoundRows:true ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
  27. {"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local", "&{User:user Passwd:p@ss(word) Net:tcp Addr:[de:ad:be:ef::ca:fe]:80 DBName:dbname Params:map[] Collation:utf8_general_ci Loc:Local TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
  28. {"/dbname", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName:dbname Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
  29. {"@/", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
  30. {"/", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
  31. {"", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
  32. {"user:p@/ssword@/", "&{User:user Passwd:p@/ssword Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
  33. {"unix/?arg=%2Fsome%2Fpath.ext", "&{User: Passwd: Net:unix Addr:/tmp/mysql.sock DBName: Params:map[arg:/some/path.ext] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
  34. }
  35. func TestDSNParser(t *testing.T) {
  36. var cfg *Config
  37. var err error
  38. var res string
  39. for i, tst := range testDSNs {
  40. cfg, err = ParseDSN(tst.in)
  41. if err != nil {
  42. t.Error(err.Error())
  43. }
  44. // pointer not static
  45. cfg.tls = nil
  46. res = fmt.Sprintf("%+v", cfg)
  47. if res != tst.out {
  48. t.Errorf("%d. ParseDSN(%q) => %q, want %q", i, tst.in, res, tst.out)
  49. }
  50. }
  51. }
  52. func TestDSNParserInvalid(t *testing.T) {
  53. var invalidDSNs = []string{
  54. "@net(addr/", // no closing brace
  55. "@tcp(/", // no closing brace
  56. "tcp(/", // no closing brace
  57. "(/", // no closing brace
  58. "net(addr)//", // unescaped
  59. "User:pass@tcp(1.2.3.4:3306)", // no trailing slash
  60. //"/dbname?arg=/some/unescaped/path",
  61. }
  62. for i, tst := range invalidDSNs {
  63. if _, err := ParseDSN(tst); err == nil {
  64. t.Errorf("invalid DSN #%d. (%s) didn't error!", i, tst)
  65. }
  66. }
  67. }
  68. func TestDSNReformat(t *testing.T) {
  69. for i, tst := range testDSNs {
  70. dsn1 := tst.in
  71. cfg1, err := ParseDSN(dsn1)
  72. if err != nil {
  73. t.Error(err.Error())
  74. continue
  75. }
  76. cfg1.tls = nil // pointer not static
  77. res1 := fmt.Sprintf("%+v", cfg1)
  78. dsn2 := cfg1.FormatDSN()
  79. cfg2, err := ParseDSN(dsn2)
  80. if err != nil {
  81. t.Error(err.Error())
  82. continue
  83. }
  84. cfg2.tls = nil // pointer not static
  85. res2 := fmt.Sprintf("%+v", cfg2)
  86. if res1 != res2 {
  87. t.Errorf("%d. %q does not match %q", i, res2, res1)
  88. }
  89. }
  90. }
  91. func TestDSNWithCustomTLS(t *testing.T) {
  92. baseDSN := "User:password@tcp(localhost:5555)/dbname?tls="
  93. tlsCfg := tls.Config{}
  94. RegisterTLSConfig("utils_test", &tlsCfg)
  95. // Custom TLS is missing
  96. tst := baseDSN + "invalid_tls"
  97. cfg, err := ParseDSN(tst)
  98. if err == nil {
  99. t.Errorf("invalid custom TLS in DSN (%s) but did not error. Got config: %#v", tst, cfg)
  100. }
  101. tst = baseDSN + "utils_test"
  102. // Custom TLS with a server name
  103. name := "foohost"
  104. tlsCfg.ServerName = name
  105. cfg, err = ParseDSN(tst)
  106. if err != nil {
  107. t.Error(err.Error())
  108. } else if cfg.tls.ServerName != name {
  109. t.Errorf("did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, tst)
  110. }
  111. // Custom TLS without a server name
  112. name = "localhost"
  113. tlsCfg.ServerName = ""
  114. cfg, err = ParseDSN(tst)
  115. if err != nil {
  116. t.Error(err.Error())
  117. } else if cfg.tls.ServerName != name {
  118. t.Errorf("did not get the correct ServerName (%s) parsing DSN (%s).", name, tst)
  119. }
  120. DeregisterTLSConfig("utils_test")
  121. }
  122. func TestDSNWithCustomTLSQueryEscape(t *testing.T) {
  123. const configKey = "&%!:"
  124. dsn := "User:password@tcp(localhost:5555)/dbname?tls=" + url.QueryEscape(configKey)
  125. name := "foohost"
  126. tlsCfg := tls.Config{ServerName: name}
  127. RegisterTLSConfig(configKey, &tlsCfg)
  128. cfg, err := ParseDSN(dsn)
  129. if err != nil {
  130. t.Error(err.Error())
  131. } else if cfg.tls.ServerName != name {
  132. t.Errorf("did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, dsn)
  133. }
  134. }
  135. func TestDSNUnsafeCollation(t *testing.T) {
  136. _, err := ParseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=true")
  137. if err != errInvalidDSNUnsafeCollation {
  138. t.Errorf("expected %v, got %v", errInvalidDSNUnsafeCollation, err)
  139. }
  140. _, err = ParseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=false")
  141. if err != nil {
  142. t.Errorf("expected %v, got %v", nil, err)
  143. }
  144. _, err = ParseDSN("/dbname?collation=gbk_chinese_ci")
  145. if err != nil {
  146. t.Errorf("expected %v, got %v", nil, err)
  147. }
  148. _, err = ParseDSN("/dbname?collation=ascii_bin&interpolateParams=true")
  149. if err != nil {
  150. t.Errorf("expected %v, got %v", nil, err)
  151. }
  152. _, err = ParseDSN("/dbname?collation=latin1_german1_ci&interpolateParams=true")
  153. if err != nil {
  154. t.Errorf("expected %v, got %v", nil, err)
  155. }
  156. _, err = ParseDSN("/dbname?collation=utf8_general_ci&interpolateParams=true")
  157. if err != nil {
  158. t.Errorf("expected %v, got %v", nil, err)
  159. }
  160. _, err = ParseDSN("/dbname?collation=utf8mb4_general_ci&interpolateParams=true")
  161. if err != nil {
  162. t.Errorf("expected %v, got %v", nil, err)
  163. }
  164. }
  165. func BenchmarkParseDSN(b *testing.B) {
  166. b.ReportAllocs()
  167. for i := 0; i < b.N; i++ {
  168. for _, tst := range testDSNs {
  169. if _, err := ParseDSN(tst.in); err != nil {
  170. b.Error(err.Error())
  171. }
  172. }
  173. }
  174. }