170 lines
4.1 KiB
Go
170 lines
4.1 KiB
Go
package parser
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/localhots/pretty"
|
|
)
|
|
|
|
// Rows contains a Rows Event.
|
|
type Rows struct {
|
|
EventType EventType
|
|
TableID uint64
|
|
Flags uint16
|
|
ExtraData []byte
|
|
ColumnCount uint64
|
|
ColumnBitmap1 []byte
|
|
ColumnBitmap2 []byte
|
|
Rows [][]interface{}
|
|
TableMap *TableMap
|
|
}
|
|
|
|
type rowsFlag uint16
|
|
|
|
const (
|
|
rowsFlagEndOfStatement rowsFlag = 0x0001
|
|
rowsFlagNoForeignKeyChecks rowsFlag = 0x0002
|
|
rowsFlagNoUniqueKeyChecks rowsFlag = 0x0004
|
|
rowsFlagRowHasColumns rowsFlag = 0x0008
|
|
|
|
freeTableMapID = 0x00FFFFFF
|
|
)
|
|
|
|
func (r *Reader) decodeRowsEvent(data []byte, typ EventType) {
|
|
// pretty.Println(data)
|
|
buf := newReadBuffer(data)
|
|
rows := Rows{EventType: typ}
|
|
idSize := r.format.tableIDSize(typ)
|
|
if idSize == 6 {
|
|
rows.TableID = buf.readUint48()
|
|
} else {
|
|
rows.TableID = uint64(buf.readUint32())
|
|
}
|
|
|
|
rows.Flags = buf.readUint16()
|
|
|
|
if typ.isEither(WriteRowsEventV2, UpdateRowsEventV2, DeleteRowsEventV2) {
|
|
// Extra data length is part of extra data, deduct 2 bytes as they
|
|
// already store its length
|
|
extraLen := buf.readUint16() - 2
|
|
rows.ExtraData = buf.readStringVarLen(int(extraLen))
|
|
}
|
|
|
|
rows.ColumnCount, _ = buf.readUintLenEnc()
|
|
rows.ColumnBitmap1 = buf.readStringVarLen(int(rows.ColumnCount+7) / 8)
|
|
if typ.isEither(UpdateRowsEventV2, UpdateRowsEventV1) {
|
|
rows.ColumnBitmap2 = buf.readStringVarLen(int(rows.ColumnCount+7) / 8)
|
|
}
|
|
|
|
tm, ok := r.tableMap[rows.TableID]
|
|
if !ok {
|
|
panic(fmt.Errorf("Out of sync: no table map definition for ID=%d", rows.TableID))
|
|
}
|
|
rows.TableMap = &tm
|
|
|
|
pretty.Println(typ.String(), rows, tm, buf.cur())
|
|
|
|
rows.decodeRows(buf, rows.ColumnBitmap1)
|
|
}
|
|
|
|
func (r *Rows) decodeRows(buf *buffer, bm []byte) {
|
|
count := 0
|
|
for i := 0; i < int(r.ColumnCount); i++ {
|
|
if isBitSet(bm, i) {
|
|
count++
|
|
}
|
|
}
|
|
count = (count + 7) / 8
|
|
|
|
nullBM := buf.readStringVarLen(count)
|
|
nullCnt := 0
|
|
row := make([]interface{}, r.ColumnCount)
|
|
|
|
pretty.Println(count, nullBM)
|
|
|
|
var err error
|
|
for i := 0; i < int(r.ColumnCount); i++ {
|
|
if !isBitSet(bm, i) {
|
|
continue
|
|
}
|
|
|
|
isNull := (uint32(nullBM[nullCnt/8]) >> uint32(nullCnt%8)) & 0x01
|
|
nullCnt++
|
|
if isNull > 0 {
|
|
row[i] = nil
|
|
continue
|
|
}
|
|
|
|
row[i], err = r.decodeValue(buf, columnType(r.TableMap.ColumnTypes[i]), r.TableMap.ColumnMeta[i])
|
|
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *Rows) decodeValue(buf *buffer, ct columnType, meta uint16) (interface{}, error) {
|
|
switch ct {
|
|
case colTypeDecimal:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeTiny:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeShort:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeLong:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeFloat:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeDouble:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeNull:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeTimestamp:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeLonglong:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeInt24:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeDate:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeTime:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeDatetime:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeYear:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeVarchar:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeBit:
|
|
pretty.Println("Type", ct.String())
|
|
|
|
case colTypeJSON:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeNewDecimal:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeEnum:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeSet:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeTinyblob:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeMediumblob:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeLongblob:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeBlob:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeVarstring:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeString:
|
|
pretty.Println("Type", ct.String())
|
|
case colTypeGeometry:
|
|
pretty.Println("Type", ct.String())
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func isBitSet(bm []byte, i int) bool {
|
|
return bm[i>>3]&(1<<(uint(i)&7)) > 0
|
|
}
|