Move parser code to it's own package
This commit is contained in:
		
							parent
							
								
									93f9ee8b52
								
							
						
					
					
						commit
						3a5271bca3
					
				@ -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)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -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())
 | 
			
		||||
}
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
package blt
 | 
			
		||||
package parser
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
package blt
 | 
			
		||||
package parser
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
package blt
 | 
			
		||||
package parser
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
package blt
 | 
			
		||||
package parser
 | 
			
		||||
 | 
			
		||||
import "testing"
 | 
			
		||||
 | 
			
		||||
@ -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),
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
package blt
 | 
			
		||||
package parser
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
@ -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
									
								
							
							
						
						
									
										169
									
								
								parser/event_rows.go
									
									
									
									
									
										Normal 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
 | 
			
		||||
}
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
package blt
 | 
			
		||||
package parser
 | 
			
		||||
 | 
			
		||||
// TableMap ...
 | 
			
		||||
type TableMap struct {
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
package blt
 | 
			
		||||
package parser
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
package blt
 | 
			
		||||
package parser
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user