From a817d1d7c45bad4c90187de968c2d3fe71194ec7 Mon Sep 17 00:00:00 2001 From: Kamil Akhmetov Date: Wed, 12 Apr 2023 17:26:20 +0300 Subject: [PATCH] Invalidate cache on Rows.NextResultSet call When reading multiple result sets from sqlx.Rows using StructScan, some information obtained usCCCing costly reflect operations is cached for performance optimisation. However, different result sets in a single sqlx.Rows object might have different structures, so cache should be invalidated and rebuild after each NextResultSet call. Now this method is not overwritten, so old cache interferes with new structures after first result set scan. Relates #857 --- sqlx.go | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/sqlx.go b/sqlx.go index f7b28768..b4589c19 100644 --- a/sqlx.go +++ b/sqlx.go @@ -5,7 +5,6 @@ import ( "database/sql/driver" "errors" "fmt" - "io/ioutil" "path/filepath" "reflect" @@ -51,9 +50,9 @@ func mapper() *reflectx.Mapper { // isScannable takes the reflect.Type and the actual dest value and returns // whether or not it's Scannable. Something is scannable if: -// * it is not a struct -// * it implements sql.Scanner -// * it has no exported fields +// - it is not a struct +// - it implements sql.Scanner +// - it has no exported fields func isScannable(t reflect.Type) bool { if reflect.PtrTo(t).Implements(_scannerInterface) { return true @@ -633,6 +632,17 @@ func (r *Rows) StructScan(dest interface{}) error { return r.Err() } +// NextResultSet behaves like sql.NextResultSet, but cleans cache of previous result set. +func (r *Rows) NextResultSet() bool { + if !r.Rows.NextResultSet() { + return false + } + r.started = false + r.fields = nil + r.values = nil + return true +} + // Connect to a database and verify with a ping. func Connect(driverName, dataSourceName string) (*DB, error) { db, err := Open(driverName, dataSourceName) @@ -884,9 +894,9 @@ func structOnlyError(t reflect.Type) error { // then each row must only have one column which can scan into that type. This // allows you to do something like: // -// rows, _ := db.Query("select id from people;") -// var ids []int -// scanAll(rows, &ids, false) +// rows, _ := db.Query("select id from people;") +// var ids []int +// scanAll(rows, &ids, false) // // and ids will be a list of the id results. I realize that this is a desirable // interface to expose to users, but for now it will only be exposed via changes