Skip to content

Commit be6107b

Browse files
committed
Packet parsing small improvement
* correct packet readLengthEncodedString that was returning byte array to readLengthEncodedBytes * have an readLengthEncodedString that effectively return string * faster column parsing: MariaDB/MySQL have an identifier limitation of 64 characters (https://dev.mysql.com/doc/refman/8.4/en/identifier-length.html) before: BenchmarkReceiveMetadata-16 1846 650394 ns/op 138776 B/op 3024 allocs/op after: BenchmarkReceiveMetadata-16 1772 639809 ns/op 138776 B/op 3024 allocs/op
1 parent 8e1f894 commit be6107b

File tree

3 files changed

+87
-37
lines changed

3 files changed

+87
-37
lines changed

Diff for: benchmark_test.go

+50
Original file line numberDiff line numberDiff line change
@@ -440,3 +440,53 @@ func BenchmarkReceiveMassiveRows(b *testing.B) {
440440
}
441441
})
442442
}
443+
444+
// BenchmarkReceiveMetadata measures performance of receiving more metadata than real data
445+
func BenchmarkReceiveMetadata(b *testing.B) {
446+
tb := (*TB)(b)
447+
b.StopTimer()
448+
b.ReportAllocs()
449+
450+
// Create a table with 1000 integer fields
451+
createTableQuery := "CREATE TABLE large_integer_table ("
452+
for i := 0; i < 1000; i++ {
453+
createTableQuery += fmt.Sprintf("col_%d INT", i)
454+
if i < 999 {
455+
createTableQuery += ", "
456+
}
457+
}
458+
createTableQuery += ")"
459+
460+
// Initialize database
461+
db := initDB(b, false,
462+
"DROP TABLE IF EXISTS large_integer_table",
463+
createTableQuery,
464+
"INSERT INTO large_integer_table VALUES ("+
465+
strings.Repeat("0,", 999)+"0)", // Insert a row of zeros
466+
)
467+
defer db.Close()
468+
469+
// Prepare a SELECT query to retrieve metadata
470+
stmt := tb.checkStmt(db.Prepare("SELECT * FROM large_integer_table LIMIT 1"))
471+
defer stmt.Close()
472+
473+
b.StartTimer()
474+
475+
// Benchmark metadata retrieval
476+
for i := 0; i < b.N; i++ {
477+
rows := tb.checkRows(stmt.Query())
478+
479+
// Create a slice to scan all columns
480+
values := make([]interface{}, 1000)
481+
valuePtrs := make([]interface{}, 1000)
482+
for j := range values {
483+
valuePtrs[j] = &values[j]
484+
}
485+
rows.Next()
486+
// Scan the row
487+
err := rows.Scan(valuePtrs...)
488+
tb.check(err)
489+
490+
rows.Close()
491+
}
492+
}

Diff for: packets.go

+12-27
Original file line numberDiff line numberDiff line change
@@ -724,55 +724,40 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
724724
}
725725

726726
// Catalog
727-
pos, err := skipLengthEncodedString(data)
728-
if err != nil {
729-
return nil, err
730-
}
731-
727+
pos := int(data[0]) + 1
732728
// Database [len coded string]
733-
n, err := skipLengthEncodedString(data[pos:])
734-
if err != nil {
735-
return nil, err
736-
}
737-
pos += n
729+
pos += int(data[pos]) + 1
738730

739-
// Table [len coded string]
731+
// Table alias [len coded string]
732+
// alias length can be up to 256
740733
if mc.cfg.ColumnsWithAlias {
741734
tableName, _, n, err := readLengthEncodedString(data[pos:])
742735
if err != nil {
743736
return nil, err
744737
}
745738
pos += n
746-
columns[i].tableName = string(tableName)
739+
columns[i].tableName = tableName
747740
} else {
748-
n, err = skipLengthEncodedString(data[pos:])
741+
n, err := skipLengthEncodedString(data[pos:])
749742
if err != nil {
750743
return nil, err
751744
}
752745
pos += n
753746
}
754747

755748
// Original table [len coded string]
756-
n, err = skipLengthEncodedString(data[pos:])
757-
if err != nil {
758-
return nil, err
759-
}
760-
pos += n
749+
pos += int(data[pos]) + 1
761750

762-
// Name [len coded string]
751+
// Name alias [len coded string]
763752
name, _, n, err := readLengthEncodedString(data[pos:])
764753
if err != nil {
765754
return nil, err
766755
}
767-
columns[i].name = string(name)
756+
columns[i].name = name
768757
pos += n
769758

770759
// Original name [len coded string]
771-
n, err = skipLengthEncodedString(data[pos:])
772-
if err != nil {
773-
return nil, err
774-
}
775-
pos += n
760+
pos += int(data[pos]) + 1
776761

777762
// Filler [uint8]
778763
pos++
@@ -843,7 +828,7 @@ func (rows *textRows) readRow(dest []driver.Value) error {
843828
for i := range dest {
844829
// Read bytes and convert to string
845830
var buf []byte
846-
buf, isNull, n, err = readLengthEncodedString(data[pos:])
831+
buf, isNull, n, err = readLengthEncodedBytes(data[pos:])
847832
pos += n
848833

849834
if err != nil {
@@ -1322,7 +1307,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
13221307
fieldTypeVector:
13231308
var isNull bool
13241309
var n int
1325-
dest[i], isNull, n, err = readLengthEncodedString(data[pos:])
1310+
dest[i], isNull, n, err = readLengthEncodedBytes(data[pos:])
13261311
pos += n
13271312
if err == nil {
13281313
if !isNull {

Diff for: utils.go

+25-10
Original file line numberDiff line numberDiff line change
@@ -524,10 +524,7 @@ func uint64ToString(n uint64) []byte {
524524
return a[i:]
525525
}
526526

527-
// returns the string read as a bytes slice, whether the value is NULL,
528-
// the number of bytes read and an error, in case the string is longer than
529-
// the input slice
530-
func readLengthEncodedString(b []byte) ([]byte, bool, int, error) {
527+
func readLengthEncodedBytes(b []byte) ([]byte, bool, int, error) {
531528
// Get length
532529
num, isNull, n := readLengthEncodedInteger(b)
533530
if num < 1 {
@@ -543,6 +540,25 @@ func readLengthEncodedString(b []byte) ([]byte, bool, int, error) {
543540
return nil, false, n, io.EOF
544541
}
545542

543+
// returns the string read as a bytes slice, whether the value is NULL,
544+
// the number of bytes read and an error, in case the string is longer than
545+
// the input slice
546+
func readLengthEncodedString(b []byte) (string, bool, int, error) {
547+
// Get length
548+
num, isNull, n := readLengthEncodedInteger(b)
549+
if num < 1 {
550+
return "", isNull, n, nil
551+
}
552+
553+
n += int(num)
554+
555+
// Check data length
556+
if len(b) >= n {
557+
return string(b[n-int(num) : n : n]), false, n, nil
558+
}
559+
return "", false, n, io.EOF
560+
}
561+
546562
// returns the number of bytes skipped and an error, in case the string is
547563
// longer than the input slice
548564
func skipLengthEncodedString(b []byte) (int, error) {
@@ -567,7 +583,9 @@ func readLengthEncodedInteger(b []byte) (uint64, bool, int) {
567583
if len(b) == 0 {
568584
return 0, true, 1
569585
}
570-
586+
if b[0] < 251 {
587+
return uint64(b[0]), false, 1
588+
}
571589
switch b[0] {
572590
// 251: NULL
573591
case 0xfb:
@@ -582,12 +600,9 @@ func readLengthEncodedInteger(b []byte) (uint64, bool, int) {
582600
return uint64(getUint24(b[1:])), false, 4
583601

584602
// 254: value of following 8
585-
case 0xfe:
586-
return uint64(binary.LittleEndian.Uint64(b[1:])), false, 9
603+
default:
604+
return binary.LittleEndian.Uint64(b[1:]), false, 9
587605
}
588-
589-
// 0-250: value of first byte
590-
return uint64(b[0]), false, 1
591606
}
592607

593608
// encodes a uint64 value and appends it to the given bytes slice

0 commit comments

Comments
 (0)