dsn_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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}"},
  20. {"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}"},
  21. {"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}"},
  22. {"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}"},
  23. {"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}"},
  24. {"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}"},
  25. {"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}"},
  26. {"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}"},
  27. {"/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}"},
  28. {"@/", "&{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}"},
  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}"},
  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}"},
  31. {"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}"},
  32. {"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}"},
  33. }
  34. func TestDSNParser(t *testing.T) {
  35. var cfg *Config
  36. var err error
  37. var res string
  38. for i, tst := range testDSNs {
  39. cfg, err = ParseDSN(tst.in)
  40. if err != nil {
  41. t.Error(err.Error())
  42. }
  43. // pointer not static
  44. cfg.tls = nil
  45. res = fmt.Sprintf("%+v", cfg)
  46. if res != tst.out {
  47. t.Errorf("%d. ParseDSN(%q) => %q, want %q", i, tst.in, res, tst.out)
  48. }
  49. }
  50. }
  51. func TestDSNParserInvalid(t *testing.T) {
  52. var invalidDSNs = []string{
  53. "@net(addr/", // no closing brace
  54. "@tcp(/", // no closing brace
  55. "tcp(/", // no closing brace
  56. "(/", // no closing brace
  57. "net(addr)//", // unescaped
  58. "User:pass@tcp(1.2.3.4:3306)", // no trailing slash
  59. //"/dbname?arg=/some/unescaped/path",
  60. }
  61. for i, tst := range invalidDSNs {
  62. if _, err := ParseDSN(tst); err == nil {
  63. t.Errorf("invalid DSN #%d. (%s) didn't error!", i, tst)
  64. }
  65. }
  66. }
  67. func TestDSNReformat(t *testing.T) {
  68. for i, tst := range testDSNs {
  69. dsn1 := tst.in
  70. cfg1, err := ParseDSN(dsn1)
  71. if err != nil {
  72. t.Error(err.Error())
  73. continue
  74. }
  75. cfg1.tls = nil // pointer not static
  76. res1 := fmt.Sprintf("%+v", cfg1)
  77. dsn2 := cfg1.FormatDSN()
  78. cfg2, err := ParseDSN(dsn2)
  79. if err != nil {
  80. t.Error(err.Error())
  81. continue
  82. }
  83. cfg2.tls = nil // pointer not static
  84. res2 := fmt.Sprintf("%+v", cfg2)
  85. if res1 != res2 {
  86. t.Errorf("%d. %q does not match %q", i, res2, res1)
  87. }
  88. }
  89. }
  90. func TestDSNWithCustomTLS(t *testing.T) {
  91. baseDSN := "User:password@tcp(localhost:5555)/dbname?tls="
  92. tlsCfg := tls.Config{}
  93. RegisterTLSConfig("utils_test", &tlsCfg)
  94. // Custom TLS is missing
  95. tst := baseDSN + "invalid_tls"
  96. cfg, err := ParseDSN(tst)
  97. if err == nil {
  98. t.Errorf("invalid custom TLS in DSN (%s) but did not error. Got config: %#v", tst, cfg)
  99. }
  100. tst = baseDSN + "utils_test"
  101. // Custom TLS with a server name
  102. name := "foohost"
  103. tlsCfg.ServerName = name
  104. cfg, err = ParseDSN(tst)
  105. if err != nil {
  106. t.Error(err.Error())
  107. } else if cfg.tls.ServerName != name {
  108. t.Errorf("did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, tst)
  109. }
  110. // Custom TLS without a server name
  111. name = "localhost"
  112. tlsCfg.ServerName = ""
  113. cfg, err = ParseDSN(tst)
  114. if err != nil {
  115. t.Error(err.Error())
  116. } else if cfg.tls.ServerName != name {
  117. t.Errorf("did not get the correct ServerName (%s) parsing DSN (%s).", name, tst)
  118. }
  119. DeregisterTLSConfig("utils_test")
  120. }
  121. func TestDSNWithCustomTLSQueryEscape(t *testing.T) {
  122. const configKey = "&%!:"
  123. dsn := "User:password@tcp(localhost:5555)/dbname?tls=" + url.QueryEscape(configKey)
  124. name := "foohost"
  125. tlsCfg := tls.Config{ServerName: name}
  126. RegisterTLSConfig(configKey, &tlsCfg)
  127. cfg, err := ParseDSN(dsn)
  128. if err != nil {
  129. t.Error(err.Error())
  130. } else if cfg.tls.ServerName != name {
  131. t.Errorf("did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, dsn)
  132. }
  133. }
  134. func TestDSNUnsafeCollation(t *testing.T) {
  135. _, err := ParseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=true")
  136. if err != errInvalidDSNUnsafeCollation {
  137. t.Errorf("expected %v, got %v", errInvalidDSNUnsafeCollation, err)
  138. }
  139. _, err = ParseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=false")
  140. if err != nil {
  141. t.Errorf("expected %v, got %v", nil, err)
  142. }
  143. _, err = ParseDSN("/dbname?collation=gbk_chinese_ci")
  144. if err != nil {
  145. t.Errorf("expected %v, got %v", nil, err)
  146. }
  147. _, err = ParseDSN("/dbname?collation=ascii_bin&interpolateParams=true")
  148. if err != nil {
  149. t.Errorf("expected %v, got %v", nil, err)
  150. }
  151. _, err = ParseDSN("/dbname?collation=latin1_german1_ci&interpolateParams=true")
  152. if err != nil {
  153. t.Errorf("expected %v, got %v", nil, err)
  154. }
  155. _, err = ParseDSN("/dbname?collation=utf8_general_ci&interpolateParams=true")
  156. if err != nil {
  157. t.Errorf("expected %v, got %v", nil, err)
  158. }
  159. _, err = ParseDSN("/dbname?collation=utf8mb4_general_ci&interpolateParams=true")
  160. if err != nil {
  161. t.Errorf("expected %v, got %v", nil, err)
  162. }
  163. }
  164. func BenchmarkParseDSN(b *testing.B) {
  165. b.ReportAllocs()
  166. for i := 0; i < b.N; i++ {
  167. for _, tst := range testDSNs {
  168. if _, err := ParseDSN(tst.in); err != nil {
  169. b.Error(err.Error())
  170. }
  171. }
  172. }
  173. }