Skip to content

Commit 8636b6c

Browse files
committed
Finish collation implementation
1 parent 13fb928 commit 8636b6c

7 files changed

+53
-63
lines changed

collations.go

+4-6
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@
88

99
package mysql
1010

11-
const collationUtf8GeneralCi = 33
11+
const defaultCollation byte = 33 // utf8_general_ci
1212

13-
const defaultCollation byte = collationUtf8GeneralCi
14-
15-
// A list of available collations and associated charsets to update this map
16-
// is available in MySQL with the query
13+
// A list of available collations mapped to the internal ID.
14+
// To update this map use the following MySQL query:
1715
// SELECT COLLATION_NAME, ID FROM information_schema.COLLATIONS
1816
var collations = map[string]byte{
1917
"big5_chinese_ci": 1,
@@ -47,7 +45,7 @@ var collations = map[string]byte{
4745
"latin5_turkish_ci": 30,
4846
"latin1_german2_ci": 31,
4947
"armscii8_general_ci": 32,
50-
"utf8_general_ci": collationUtf8GeneralCi,
48+
"utf8_general_ci": 33,
5149
"cp1250_czech_cs": 34,
5250
"ucs2_general_ci": 35,
5351
"cp866_general_ci": 36,

connection.go

+3-17
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ type mysqlConn struct {
2727
maxWriteSize int
2828
flags clientFlag
2929
sequence uint8
30-
collation byte
3130
parseTime bool
3231
strict bool
3332
}
@@ -40,31 +39,18 @@ type config struct {
4039
dbname string
4140
params map[string]string
4241
loc *time.Location
43-
timeout time.Duration
4442
tls *tls.Config
43+
timeout time.Duration
44+
collation uint8
4545
allowAllFiles bool
4646
allowOldPasswords bool
4747
clientFoundRows bool
4848
}
4949

50-
// Handles parameters set in DSN
50+
// Handles parameters set in DSN after the connection is established
5151
func (mc *mysqlConn) handleParams() (err error) {
5252
for param, val := range mc.cfg.params {
5353
switch param {
54-
// Collation
55-
case "collation":
56-
collation, ok := collations[val]
57-
if !ok {
58-
// Note possibility for false negatives:
59-
// could be caused although the collation is valid
60-
// if the collations map does not contain entries
61-
// the server supports.
62-
err = errors.New("unknown collation")
63-
return
64-
}
65-
mc.collation = collation
66-
break
67-
6854
// Charset
6955
case "charset":
7056
charsets := strings.Split(val, ",")

driver.go

-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ func (d *MySQLDriver) Open(dsn string) (driver.Conn, error) {
4040
mc := &mysqlConn{
4141
maxPacketAllowed: maxPacketSize,
4242
maxWriteSize: maxPacketSize - 1,
43-
collation: defaultCollation,
4443
}
4544
mc.cfg, err = parseDSN(dsn)
4645
if err != nil {

driver_test.go

+9-14
Original file line numberDiff line numberDiff line change
@@ -944,33 +944,28 @@ func TestCollation(t *testing.T) {
944944
}
945945

946946
defaultCollation := "utf8_general_ci"
947-
tests := []string{
947+
testCollations := []string{
948948
"", // do not set
949949
defaultCollation, // driver default
950950
"latin1_general_ci",
951951
"binary",
952+
"utf8_unicode_ci",
952953
"utf8mb4_general_ci",
953954
}
954-
cdsn := dsn
955-
for _, collation := range tests {
956-
var expected string
955+
956+
for _, collation := range testCollations {
957+
var expected, tdsn string
957958
if collation != "" {
958-
cdsn += "&collation=" + collation
959+
tdsn = dsn + "&collation=" + collation
959960
expected = collation
960961
} else {
962+
tdsn = dsn
961963
expected = defaultCollation
962964
}
963-
runTests(t, cdsn, func(dbt *DBTest) {
964-
rows := dbt.mustQuery("SELECT @@collation_connection")
965-
defer rows.Close()
966-
967-
if !rows.Next() {
968-
dbt.Fatalf("Error getting connection collation: %s", rows.Err())
969-
}
970965

966+
runTests(t, tdsn, func(dbt *DBTest) {
971967
var got string
972-
err := rows.Scan(&got)
973-
if err != nil {
968+
if err := dbt.db.QueryRow("SELECT @@collation_connection").Scan(&got); err != nil {
974969
dbt.Fatal(err)
975970
}
976971

packets.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
257257
data[11] = 0x00
258258

259259
// Charset [1 byte]
260-
data[12] = mc.collation
260+
data[12] = mc.cfg.collation
261261

262262
// SSL Connection Request Packet
263263
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest

utils.go

+24-12
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,11 @@ func DeregisterTLSConfig(key string) {
7272

7373
// parseDSN parses the DSN string to a config
7474
func parseDSN(dsn string) (cfg *config, err error) {
75-
cfg = new(config)
75+
// New config with some default values
76+
cfg = &config{
77+
loc: time.UTC,
78+
collation: defaultCollation,
79+
}
7680

7781
// TODO: use strings.IndexByte when we can depend on Go 1.2
7882

@@ -160,11 +164,6 @@ func parseDSN(dsn string) (cfg *config, err error) {
160164

161165
}
162166

163-
// Set default location if empty
164-
if cfg.loc == nil {
165-
cfg.loc = time.UTC
166-
}
167-
168167
return
169168
}
170169

@@ -188,22 +187,35 @@ func parseDSNParams(cfg *config, params string) (err error) {
188187
return fmt.Errorf("Invalid Bool value: %s", value)
189188
}
190189

191-
// Switch "rowsAffected" mode
192-
case "clientFoundRows":
190+
// Use old authentication mode (pre MySQL 4.1)
191+
case "allowOldPasswords":
193192
var isBool bool
194-
cfg.clientFoundRows, isBool = readBool(value)
193+
cfg.allowOldPasswords, isBool = readBool(value)
195194
if !isBool {
196195
return fmt.Errorf("Invalid Bool value: %s", value)
197196
}
198197

199-
// Use old authentication mode (pre MySQL 4.1)
200-
case "allowOldPasswords":
198+
// Switch "rowsAffected" mode
199+
case "clientFoundRows":
201200
var isBool bool
202-
cfg.allowOldPasswords, isBool = readBool(value)
201+
cfg.clientFoundRows, isBool = readBool(value)
203202
if !isBool {
204203
return fmt.Errorf("Invalid Bool value: %s", value)
205204
}
206205

206+
// Collation
207+
case "collation":
208+
collation, ok := collations[value]
209+
if !ok {
210+
// Note possibility for false negatives:
211+
// could be triggered although the collation is valid if the
212+
// collations map does not contain entries the server supports.
213+
err = errors.New("unknown collation")
214+
return
215+
}
216+
cfg.collation = collation
217+
break
218+
207219
// Time Location
208220
case "loc":
209221
if value, err = url.QueryUnescape(value); err != nil {

utils_test.go

+12-12
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,18 @@ var testDSNs = []struct {
2121
out string
2222
loc *time.Location
2323
}{
24-
{"username:password@protocol(address)/dbname?param=value", "&{user:username passwd:password net:protocol addr:address dbname:dbname params:map[param:value] loc:%p timeout:0 tls:<nil> allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
25-
{"user@unix(/path/to/socket)/dbname?charset=utf8", "&{user:user passwd: net:unix addr:/path/to/socket dbname:dbname params:map[charset:utf8] loc:%p timeout:0 tls:<nil> allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
26-
{"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:%p timeout:0 tls:<nil> allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
27-
{"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:%p timeout:0 tls:<nil> allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
28-
{"user:password@/dbname?loc=UTC&timeout=30s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE", "&{user:user passwd:password net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p timeout:30000000000 tls:<nil> allowAllFiles:true allowOldPasswords:true clientFoundRows:true}", time.UTC},
29-
{"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:%p timeout:0 tls:<nil> allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.Local},
30-
{"/dbname", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p timeout:0 tls:<nil> allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
31-
{"@/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p timeout:0 tls:<nil> allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
32-
{"/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p timeout:0 tls:<nil> allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
33-
{"", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p timeout:0 tls:<nil> allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
34-
{"user:p@/ssword@/", "&{user:user passwd:p@/ssword net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p timeout:0 tls:<nil> allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
35-
{"unix/?arg=%2Fsome%2Fpath.ext", "&{user: passwd: net:unix addr:/tmp/mysql.sock dbname: params:map[arg:/some/path.ext] loc:%p timeout:0 tls:<nil> allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
24+
{"username:password@protocol(address)/dbname?param=value", "&{user:username passwd:password net:protocol addr:address dbname:dbname params:map[param:value] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
25+
{"user@unix(/path/to/socket)/dbname?charset=utf8", "&{user:user passwd: net:unix addr:/path/to/socket dbname:dbname params:map[charset:utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
26+
{"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:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
27+
{"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:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
28+
{"user:password@/dbname?loc=UTC&timeout=30s&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:%p tls:<nil> timeout:30000000000 collation:224 allowAllFiles:true allowOldPasswords:true clientFoundRows:true}", time.UTC},
29+
{"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:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.Local},
30+
{"/dbname", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
31+
{"@/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
32+
{"/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
33+
{"", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
34+
{"user:p@/ssword@/", "&{user:user passwd:p@/ssword net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
35+
{"unix/?arg=%2Fsome%2Fpath.ext", "&{user: passwd: net:unix addr:/tmp/mysql.sock dbname: params:map[arg:/some/path.ext] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
3636
}
3737

3838
func TestDSNParser(t *testing.T) {

0 commit comments

Comments
 (0)