dsn_test.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 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] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 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] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 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] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 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] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 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] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 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[] Loc:UTC TLS:<nil> Timeout:30s ReadTimeout:1s WriteTimeout:1s Collation:224 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[] Loc:Local TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 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[] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 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[] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 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[] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 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[] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 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[] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 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] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 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 TestDSNWithCustomTLS(t *testing.T) {
  68. baseDSN := "User:password@tcp(localhost:5555)/dbname?tls="
  69. tlsCfg := tls.Config{}
  70. RegisterTLSConfig("utils_test", &tlsCfg)
  71. // Custom TLS is missing
  72. tst := baseDSN + "invalid_tls"
  73. cfg, err := ParseDSN(tst)
  74. if err == nil {
  75. t.Errorf("invalid custom TLS in DSN (%s) but did not error. Got config: %#v", tst, cfg)
  76. }
  77. tst = baseDSN + "utils_test"
  78. // Custom TLS with a server name
  79. name := "foohost"
  80. tlsCfg.ServerName = name
  81. cfg, err = ParseDSN(tst)
  82. if err != nil {
  83. t.Error(err.Error())
  84. } else if cfg.TLS.ServerName != name {
  85. t.Errorf("did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, tst)
  86. }
  87. // Custom TLS without a server name
  88. name = "localhost"
  89. tlsCfg.ServerName = ""
  90. cfg, err = ParseDSN(tst)
  91. if err != nil {
  92. t.Error(err.Error())
  93. } else if cfg.TLS.ServerName != name {
  94. t.Errorf("did not get the correct ServerName (%s) parsing DSN (%s).", name, tst)
  95. }
  96. DeregisterTLSConfig("utils_test")
  97. }
  98. func TestDSNWithCustomTLS_queryEscape(t *testing.T) {
  99. const configKey = "&%!:"
  100. dsn := "User:password@tcp(localhost:5555)/dbname?tls=" + url.QueryEscape(configKey)
  101. name := "foohost"
  102. tlsCfg := tls.Config{ServerName: name}
  103. RegisterTLSConfig(configKey, &tlsCfg)
  104. cfg, err := ParseDSN(dsn)
  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, dsn)
  109. }
  110. }
  111. func TestDSNUnsafeCollation(t *testing.T) {
  112. _, err := ParseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=true")
  113. if err != errInvalidDSNUnsafeCollation {
  114. t.Errorf("expected %v, got %v", errInvalidDSNUnsafeCollation, err)
  115. }
  116. _, err = ParseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=false")
  117. if err != nil {
  118. t.Errorf("expected %v, got %v", nil, err)
  119. }
  120. _, err = ParseDSN("/dbname?collation=gbk_chinese_ci")
  121. if err != nil {
  122. t.Errorf("expected %v, got %v", nil, err)
  123. }
  124. _, err = ParseDSN("/dbname?collation=ascii_bin&interpolateParams=true")
  125. if err != nil {
  126. t.Errorf("expected %v, got %v", nil, err)
  127. }
  128. _, err = ParseDSN("/dbname?collation=latin1_german1_ci&interpolateParams=true")
  129. if err != nil {
  130. t.Errorf("expected %v, got %v", nil, err)
  131. }
  132. _, err = ParseDSN("/dbname?collation=utf8_general_ci&interpolateParams=true")
  133. if err != nil {
  134. t.Errorf("expected %v, got %v", nil, err)
  135. }
  136. _, err = ParseDSN("/dbname?collation=utf8mb4_general_ci&interpolateParams=true")
  137. if err != nil {
  138. t.Errorf("expected %v, got %v", nil, err)
  139. }
  140. }
  141. func BenchmarkParseDSN(b *testing.B) {
  142. b.ReportAllocs()
  143. for i := 0; i < b.N; i++ {
  144. for _, tst := range testDSNs {
  145. if _, err := ParseDSN(tst.in); err != nil {
  146. b.Error(err.Error())
  147. }
  148. }
  149. }
  150. }