Skip to content

Commit 27d6b7e

Browse files
committed
Extract charset/collation info from TableMapEvent
1 parent a624aa6 commit 27d6b7e

File tree

4 files changed

+129
-24
lines changed

4 files changed

+129
-24
lines changed

mysql/type.go

Lines changed: 0 additions & 21 deletions
This file was deleted.

replication/event.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ func (e *GTIDEvent) Decode(data []byte) error {
385385
func (e *GTIDEvent) Dump(w io.Writer) {
386386
fmtTime := func(t time.Time) string {
387387
if t.IsZero() {
388-
return "N/A"
388+
return "<n/a>"
389389
}
390390
return t.Format(time.RFC3339Nano)
391391
}

replication/row_event.go

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ type TableMapEvent struct {
4040

4141
SignednessBitmap []byte
4242

43+
// DefaultCharset[0] is the default collation;
44+
// For character columns that have different charset,
45+
// (character column index, column collation) pairs follows
46+
DefaultCharset []uint64
47+
// ColumnCharset contains collation sequence for all character columns
48+
ColumnCharset []uint64
49+
4350
ColumnName [][]byte
4451
PrimaryKey []uint64 // A sequence of column indexes
4552
PrimaryKeyPrefix []uint64 // Prefix length 0 means that the whole column value is used
@@ -219,6 +226,22 @@ func (e *TableMapEvent) decodeOptionalMeta(data []byte) error {
219226
case TABLE_MAP_OPT_META_SIGNEDNESS:
220227
e.SignednessBitmap = v
221228

229+
case TABLE_MAP_OPT_META_DEFAULT_CHARSET:
230+
p := 0
231+
for p < len(v) {
232+
c, _, n := LengthEncodedInt(v[p:])
233+
p += n
234+
e.DefaultCharset = append(e.DefaultCharset, c)
235+
}
236+
237+
case TABLE_MAP_OPT_META_COLUMN_CHARSET:
238+
p := 0
239+
for p < len(v) {
240+
c, _, n := LengthEncodedInt(v[p:])
241+
p += n
242+
e.ColumnCharset = append(e.ColumnCharset, c)
243+
}
244+
222245
case TABLE_MAP_OPT_META_COLUMN_NAME:
223246
p := 0
224247
e.ColumnName = make([][]byte, 0, e.ColumnCount)
@@ -273,10 +296,15 @@ func (e *TableMapEvent) Dump(w io.Writer) {
273296
fmt.Fprintf(w, "NULL bitmap: \n%s", hex.Dump(e.NullBitmap))
274297

275298
fmt.Fprintf(w, "Signedness bitmap: \n%s", hex.Dump(e.SignednessBitmap))
299+
fmt.Fprintf(w, "Default charset: %v\n", e.DefaultCharset)
300+
fmt.Fprintf(w, "Column charset: %v\n", e.ColumnCharset)
276301
fmt.Fprintf(w, "Primary key: %v\n", e.PrimaryKey)
277302
fmt.Fprintf(w, "Primary key prefix: %v\n", e.PrimaryKeyPrefix)
278303

279304
unsignedMap := e.UnsignedMap()
305+
fmt.Fprintf(w, "UnsignedMap: %#v\n", unsignedMap)
306+
collationMap := e.CollationMap()
307+
fmt.Fprintf(w, "CollationMap: %#v\n", collationMap)
280308

281309
nameMaxLen := 0
282310
for _, name := range e.ColumnName {
@@ -305,13 +333,19 @@ func (e *TableMapEvent) Dump(w io.Writer) {
305333
fmt.Fprintf(w, " type=%-3d", e.ColumnType[i])
306334

307335
if IsNumericType(e.ColumnType[i]) {
308-
if unsignedMap == nil {
336+
if len(unsignedMap) == 0 {
309337
fmt.Fprintf(w, " unsigned=<n/a>")
310338
} else if unsignedMap[i] {
311339
fmt.Fprintf(w, " unsigned=yes")
312340
} else {
313341
fmt.Fprintf(w, " unsigned=no ")
314342
}
343+
} else if IsCharacterType(e.ColumnType[i]) {
344+
if len(collationMap) == 0 {
345+
fmt.Fprintf(w, " collation=<n/a>")
346+
} else {
347+
fmt.Fprintf(w, " collation=%d ", collationMap[i])
348+
}
315349
}
316350

317351
available, nullable := e.Nullable(i)
@@ -344,7 +378,7 @@ func (e *TableMapEvent) Nullable(i int) (available, nullable bool) {
344378

345379
// UnsignedMap returns a map: column index -> unsigned.
346380
// Note that only numeric columns will be returned.
347-
// If signedness bits are not available, nil is returned.
381+
// nil is returned if not available or no numeric columns at all.
348382
func (e *TableMapEvent) UnsignedMap() map[int]bool {
349383
if len(e.SignednessBitmap) == 0 {
350384
return nil
@@ -355,12 +389,64 @@ func (e *TableMapEvent) UnsignedMap() map[int]bool {
355389
if !IsNumericType(e.ColumnType[i]) {
356390
continue
357391
}
392+
358393
ret[i] = e.SignednessBitmap[p/8]&(1<<uint(7-p%8)) != 0
359394
p++
360395
}
361396
return ret
362397
}
363398

399+
// CollationMap returns a map: column index -> collation id.
400+
// Note that only character columns will be returned.
401+
// nil is returned if not available or no character columns at all.
402+
func (e *TableMapEvent) CollationMap() map[int]uint64 {
403+
404+
ret := make(map[int]uint64)
405+
406+
if len(e.DefaultCharset) != 0 {
407+
defaultCollation := e.DefaultCharset[0]
408+
409+
// character column index -> collation
410+
collations := make(map[int]uint64)
411+
for i := 1; i < len(e.DefaultCharset); i += 2 {
412+
collations[int(e.DefaultCharset[i])] = e.DefaultCharset[i+1]
413+
}
414+
415+
p := 0
416+
for i := 0; i < int(e.ColumnCount); i++ {
417+
if !IsCharacterType(e.ColumnType[i]) {
418+
continue
419+
}
420+
421+
if collation, ok := collations[p]; ok {
422+
ret[i] = collation
423+
} else {
424+
ret[i] = defaultCollation
425+
}
426+
p++
427+
}
428+
429+
return ret
430+
}
431+
432+
if len(e.ColumnCharset) != 0 {
433+
434+
p := 0
435+
for i := 0; i < int(e.ColumnCount); i++ {
436+
if !IsCharacterType(e.ColumnType[i]) {
437+
continue
438+
}
439+
440+
ret[i] = e.ColumnCharset[p]
441+
p++
442+
}
443+
444+
return ret
445+
}
446+
447+
return nil
448+
}
449+
364450
// RowsEventStmtEndFlag is set in the end of the statement.
365451
const RowsEventStmtEndFlag = 0x01
366452

replication/type.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package replication
2+
3+
import (
4+
. "github.com/siddontang/go-mysql/mysql"
5+
)
6+
7+
// IsNumericType returns true if the given type is numeric type. From: sql/log_event.cc and sql/field.h
8+
func IsNumericType(typ byte) bool {
9+
switch typ {
10+
case MYSQL_TYPE_TINY,
11+
MYSQL_TYPE_SHORT,
12+
MYSQL_TYPE_INT24,
13+
MYSQL_TYPE_LONG,
14+
MYSQL_TYPE_LONGLONG,
15+
MYSQL_TYPE_FLOAT,
16+
MYSQL_TYPE_DOUBLE,
17+
MYSQL_TYPE_DECIMAL,
18+
MYSQL_TYPE_NEWDECIMAL:
19+
return true
20+
21+
default:
22+
return false
23+
}
24+
25+
}
26+
27+
// IsCharacterType returns true if the given type is character type. From: sql/log_event.cc
28+
func IsCharacterType(typ byte) bool {
29+
switch typ {
30+
case MYSQL_TYPE_STRING,
31+
MYSQL_TYPE_VAR_STRING,
32+
MYSQL_TYPE_VARCHAR,
33+
MYSQL_TYPE_BLOB:
34+
return true
35+
36+
default:
37+
return false
38+
}
39+
40+
}

0 commit comments

Comments
 (0)