1
0
Fork 0

Move parser code to it's own package

This commit is contained in:
Gregory Eremin 2018-11-05 18:15:59 +01:00
parent 93f9ee8b52
commit 3a5271bca3
13 changed files with 182 additions and 80 deletions

View File

@ -7,7 +7,7 @@ import (
"os"
"time"
"github.com/localhots/blt"
"github.com/localhots/blt/parser"
"github.com/localhots/gobelt/log"
)
@ -22,13 +22,13 @@ func main() {
validate((*dsn != ""), "Database source name is not set")
validate((*id != 0), "Server ID is not set")
validate((*file != ""), "Binary log file is not set")
conf := blt.Config{
conf := parser.Config{
ServerID: uint32(*id),
File: *file,
Offset: uint32(*offset),
}
reader, err := blt.Connect(*dsn, conf)
reader, err := parser.Connect(*dsn, conf)
if err != nil {
log.Fatalf(ctx, "Failed to establish connection: %v", err)
}

View File

@ -1,64 +0,0 @@
package blt
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{}
}
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))
}
pretty.Println(typ.String(), rows, tm, buf.cur())
}

View File

@ -1,4 +1,4 @@
package blt
package parser
import (
"encoding/binary"

View File

@ -1,4 +1,4 @@
package blt
package parser
import (
"fmt"

View File

@ -1,4 +1,4 @@
package blt
package parser
import (
"encoding/binary"

View File

@ -1,4 +1,4 @@
package blt
package parser
import "testing"

View File

@ -1,11 +1,9 @@
package blt
package parser
import (
"fmt"
"strconv"
"strings"
"github.com/localhots/pretty"
)
// FormatDescription is a description of binary log format.
@ -55,7 +53,6 @@ func decodeFormatDescription(data []byte) FormatDescription {
EventHeaderLength: buf.readUint8(),
EventTypeHeaderLengths: buf.readStringEOF(),
}
pretty.Println(fd)
fd.ServerDetails = ServerDetails{
Flavor: FlavorMySQL,
Version: parseVersionNumber(fd.ServerVersion),

View File

@ -1,4 +1,4 @@
package blt
package parser
import (
"errors"

View File

@ -1,4 +1,4 @@
package blt
package parser
func (r *Reader) decodeRotateEvent(data []byte) Position {
buf := newReadBuffer(data)

169
parser/event_rows.go Normal file
View File

@ -0,0 +1,169 @@
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
}

View File

@ -1,4 +1,4 @@
package blt
package parser
// TableMap ...
type TableMap struct {

View File

@ -1,4 +1,4 @@
package blt
package parser
import (
"fmt"

View File

@ -1,4 +1,4 @@
package blt
package parser
import (
"context"