Additional scan performance improvements

Move typeStack to ScanContext, so it is shared between rows.Scan calls.
Use string.Builder for string concatenations.
Simplify value assign logic.
Move convert value to the last assign step (needs for type conversions are rare).
This commit is contained in:
go-jet 2022-02-09 12:34:10 +01:00
parent c10244aeab
commit c86903fd1d
8 changed files with 428 additions and 174 deletions

View file

@ -786,6 +786,123 @@ func TestRowsScan(t *testing.T) {
requireQueryLogged(t, stmt, 0)
}
func TestScanNullColumn(t *testing.T) {
stmt := SELECT(
Address.AllColumns,
).FROM(
Address,
).WHERE(
Address.Address2.IS_NULL(),
)
var dest []model.Address
err := stmt.Query(db, &dest)
require.NoError(t, err)
testutils.AssertJSON(t, dest, `
[
{
"AddressID": 1,
"Address": "47 MySakila Drive",
"Address2": null,
"District": "Alberta",
"CityID": 300,
"PostalCode": "",
"Phone": "",
"LastUpdate": "2006-02-15T09:45:30Z"
},
{
"AddressID": 2,
"Address": "28 MySQL Boulevard",
"Address2": null,
"District": "QLD",
"CityID": 576,
"PostalCode": "",
"Phone": "",
"LastUpdate": "2006-02-15T09:45:30Z"
},
{
"AddressID": 3,
"Address": "23 Workhaven Lane",
"Address2": null,
"District": "Alberta",
"CityID": 300,
"PostalCode": "",
"Phone": "14033335568",
"LastUpdate": "2006-02-15T09:45:30Z"
},
{
"AddressID": 4,
"Address": "1411 Lillydale Drive",
"Address2": null,
"District": "QLD",
"CityID": 576,
"PostalCode": "",
"Phone": "6172235589",
"LastUpdate": "2006-02-15T09:45:30Z"
}
]
`)
}
func TestRowsScanSetZeroValue(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).WHERE(
Rental.RentalID.IN(Int(16049), Int(15966)),
).ORDER_BY(
Rental.RentalID.DESC(),
)
rows, err := stmt.Rows(context.Background(), db)
require.NoError(t, err)
defer rows.Close()
// destination object is used as destination for all rows scan.
// this tests checks that ReturnedDate is set to nil with the second call
// check qrm.setZeroValue
var dest model.Rental
for rows.Next() {
err := rows.Scan(&dest)
require.NoError(t, err)
if dest.RentalID == 16049 {
testutils.AssertJSON(t, dest, `
{
"RentalID": 16049,
"RentalDate": "2005-08-23T22:50:12Z",
"InventoryID": 2666,
"CustomerID": 393,
"ReturnDate": "2005-08-30T01:01:12Z",
"StaffID": 2,
"LastUpdate": "2006-02-16T02:30:53Z"
}
`)
} else {
testutils.AssertJSON(t, dest, `
{
"RentalID": 15966,
"RentalDate": "2006-02-14T15:16:03Z",
"InventoryID": 4472,
"CustomerID": 374,
"ReturnDate": null,
"StaffID": 1,
"LastUpdate": "2006-02-16T02:30:53Z"
}
`)
}
}
err = rows.Close()
require.NoError(t, err)
err = rows.Err()
require.NoError(t, err)
}
func TestScanNumericToFloat(t *testing.T) {
type Number struct {
Float32 float32
@ -826,6 +943,54 @@ func TestScanNumericToIntegerError(t *testing.T) {
}
func TestScanIntoCustomBaseTypes(t *testing.T) {
type MyUint8 uint8
type MyUint16 uint16
type MyUint32 uint32
type MyInt16 int16
type MyFloat32 float32
type MyFloat64 float64
type MyString string
type MyTime = time.Time
type film struct {
FilmID MyUint16 `sql:"primary_key"`
Title MyString
Description *MyString
ReleaseYear *MyInt16
LanguageID MyUint8
RentalDuration MyUint8
RentalRate MyFloat32
Length *MyUint32
ReplacementCost MyFloat64
Rating *model.MpaaRating
LastUpdate MyTime
SpecialFeatures *MyString
Fulltext MyString
}
stmt := SELECT(
Film.AllColumns,
).FROM(
Film,
).ORDER_BY(
Film.FilmID.ASC(),
).LIMIT(3)
var films []model.Film
err := stmt.Query(db, &films)
require.NoError(t, err)
var myFilms []film
err = stmt.Query(db, &myFilms)
require.NoError(t, err)
require.Equal(t, testutils.ToJSON(films), testutils.ToJSON(myFilms))
}
// QueryContext panic when the scanned value is nil and the destination is a slice of primitive
// https://github.com/go-jet/jet/issues/91
func TestScanToPrimitiveElementsSlice(t *testing.T) {

View file

@ -2521,6 +2521,79 @@ func TestRecursionScanNx1(t *testing.T) {
})
}
type StoreInfo struct {
model.Store
Staffs ManagerInfo
}
type ManagerInfo struct {
model.Staff
Store *StoreInfo
}
func TestRecursionScan1x1(t *testing.T) {
stmt := SELECT(
Store.AllColumns,
Staff.AllColumns,
).FROM(
Store.
INNER_JOIN(Staff, Staff.StaffID.EQ(Store.ManagerStaffID)),
).ORDER_BY(
Store.StoreID,
)
var dest []StoreInfo
err := stmt.Query(db, &dest)
require.NoError(t, err)
testutils.AssertJSON(t, dest, `
[
{
"StoreID": 1,
"ManagerStaffID": 1,
"AddressID": 1,
"LastUpdate": "2006-02-15T09:57:12Z",
"Staffs": {
"StaffID": 1,
"FirstName": "Mike",
"LastName": "Hillyer",
"AddressID": 3,
"Email": "Mike.Hillyer@sakilastaff.com",
"StoreID": 1,
"Active": true,
"Username": "Mike",
"Password": "8cb2237d0679ca88db6464eac60da96345513964",
"LastUpdate": "2006-05-16T16:13:11.79328Z",
"Picture": "iVBORw0KWgo=",
"Store": null
}
},
{
"StoreID": 2,
"ManagerStaffID": 2,
"AddressID": 2,
"LastUpdate": "2006-02-15T09:57:12Z",
"Staffs": {
"StaffID": 2,
"FirstName": "Jon",
"LastName": "Stephens",
"AddressID": 4,
"Email": "Jon.Stephens@sakilastaff.com",
"StoreID": 2,
"Active": true,
"Username": "Jon",
"Password": "8cb2237d0679ca88db6464eac60da96345513964",
"LastUpdate": "2006-05-16T16:13:11.79328Z",
"Picture": null,
"Store": null
}
}
]
`)
}
// In parameterized statements integer literals, like Int(num), are replaced with a placeholders. For some expressions,
// postgres interpreter will not have enough information to deduce the type. If this is the case postgres returns an error.
// Int8, Int16, .... functions will add automatic type cast over placeholder, so type deduction is always possible.

View file

@ -2,7 +2,6 @@ package postgres
import (
"context"
"fmt"
"github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/postgres"
"github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/northwind/model"
@ -864,5 +863,4 @@ WHERE orders1."orders.order_id" < $1;
err := stmt.Query(db, &dest)
require.NoError(t, err)
require.Len(t, dest, 72)
fmt.Println(len(dest))
}