Skip to content

Commit 2356566

Browse files
authored
Optimization: statements reuse previous column name (#1711)
#1708 added `[]mysqlField` cache to stmt. It was used only for MariaDB cached metadata. This commit allows MySQL to also benefit from the metadata cache. If the column names are the same as the cached metadata, it reuses them instead of allocating new strings. goos: darwin goarch: arm64 pkg: github.com/go-sql-driver/mysql cpu: Apple M1 Pro │ master.txt │ reuse.txt │ │ sec/op │ sec/op vs base │ ReceiveMetadata-8 1.273m ± 2% 1.269m ± 2% ~ (p=1.000 n=10) │ master.txt │ reuse.txt │ │ B/op │ B/op vs base │ ReceiveMetadata-8 88.17Ki ± 0% 80.39Ki ± 0% -8.82% (p=0.000 n=10) │ master.txt │ reuse.txt │ │ allocs/op │ allocs/op vs base │ ReceiveMetadata-8 1015.00 ± 0% 16.00 ± 0% -98.42% (p=0.000 n=10)
1 parent 6e944e1 commit 2356566

File tree

5 files changed

+24
-12
lines changed

5 files changed

+24
-12
lines changed

benchmark_test.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -490,14 +490,13 @@ func BenchmarkReceiveMetadata(b *testing.B) {
490490
valuePtrs[j] = &values[j]
491491
}
492492

493-
b.ReportAllocs()
494-
b.ResetTimer()
495-
496493
// Prepare a SELECT query to retrieve metadata
497494
stmt := tb.checkStmt(db.Prepare("SELECT * FROM large_integer_table LIMIT 1"))
498495
defer stmt.Close()
499496

500497
// Benchmark metadata retrieval
498+
b.ReportAllocs()
499+
b.ResetTimer()
501500
for range b.N {
502501
rows := tb.checkRows(stmt.Query())
503502

connection.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
231231

232232
if columnCount > 0 {
233233
if mc.extCapabilities&clientCacheMetadata != 0 {
234-
if stmt.columns, err = mc.readColumns(int(columnCount)); err != nil {
234+
if stmt.columns, err = mc.readColumns(int(columnCount), nil); err != nil {
235235
return nil, err
236236
}
237237
} else {
@@ -448,7 +448,7 @@ func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error)
448448
}
449449

450450
// Columns
451-
rows.rs.columns, err = mc.readColumns(resLen)
451+
rows.rs.columns, err = mc.readColumns(resLen, nil)
452452
return rows, err
453453
}
454454

packets.go

+16-3
Original file line numberDiff line numberDiff line change
@@ -702,8 +702,11 @@ func (mc *okHandler) handleOkPacket(data []byte) error {
702702

703703
// Read Packets as Field Packets until EOF-Packet or an Error appears
704704
// http://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnDefinition41
705-
func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
705+
func (mc *mysqlConn) readColumns(count int, old []mysqlField) ([]mysqlField, error) {
706706
columns := make([]mysqlField, count)
707+
if len(old) != count {
708+
old = nil
709+
}
707710

708711
for i := range count {
709712
data, err := mc.readPacket()
@@ -731,7 +734,12 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
731734
return nil, err
732735
}
733736
pos += n
734-
columns[i].tableName = string(tableName)
737+
if old != nil && old[i].tableName == string(tableName) {
738+
// avoid allocating new string
739+
columns[i].tableName = old[i].tableName
740+
} else {
741+
columns[i].tableName = string(tableName)
742+
}
735743
} else {
736744
n, err = skipLengthEncodedString(data[pos:])
737745
if err != nil {
@@ -752,7 +760,12 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
752760
if err != nil {
753761
return nil, err
754762
}
755-
columns[i].name = string(name)
763+
if old != nil && old[i].name == string(name) {
764+
// avoid allocating new string
765+
columns[i].name = old[i].name
766+
} else {
767+
columns[i].name = string(name)
768+
}
756769
pos += n
757770

758771
// Original name [len coded string]

rows.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ func (rows *binaryRows) NextResultSet() error {
186186
return err
187187
}
188188

189-
rows.rs.columns, err = rows.mc.readColumns(resLen)
189+
rows.rs.columns, err = rows.mc.readColumns(resLen, nil)
190190
return err
191191
}
192192

@@ -208,7 +208,7 @@ func (rows *textRows) NextResultSet() (err error) {
208208
return err
209209
}
210210

211-
rows.rs.columns, err = rows.mc.readColumns(resLen)
211+
rows.rs.columns, err = rows.mc.readColumns(resLen, nil)
212212
return err
213213
}
214214

statement.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
7474
// Columns
7575
if metadataFollows && stmt.mc.extCapabilities&clientCacheMetadata != 0 {
7676
// we can not skip column metadata because next stmt.Query() may use it.
77-
if stmt.columns, err = mc.readColumns(resLen); err != nil {
77+
if stmt.columns, err = mc.readColumns(resLen, stmt.columns); err != nil {
7878
return nil, err
7979
}
8080
} else {
@@ -125,7 +125,7 @@ func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
125125
if resLen > 0 {
126126
rows.mc = mc
127127
if metadataFollows {
128-
if rows.rs.columns, err = mc.readColumns(resLen); err != nil {
128+
if rows.rs.columns, err = mc.readColumns(resLen, stmt.columns); err != nil {
129129
return nil, err
130130
}
131131
stmt.columns = rows.rs.columns

0 commit comments

Comments
 (0)