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:
parent
c10244aeab
commit
c86903fd1d
8 changed files with 428 additions and 174 deletions
|
|
@ -206,7 +206,7 @@ GROUP BY payment.customer_id;
|
|||
"RentalID": null,
|
||||
"Amount": 0,
|
||||
"PaymentDate": "0001-01-01T00:00:00Z",
|
||||
"LastUpdate": "0001-01-01T00:00:00Z",
|
||||
"LastUpdate": null,
|
||||
"Count": 8,
|
||||
"Sum": 38.92,
|
||||
"Avg": 4.865,
|
||||
|
|
@ -964,14 +964,14 @@ func TestRowsScan(t *testing.T) {
|
|||
rows, err := stmt.Rows(context.Background(), db)
|
||||
require.NoError(t, err)
|
||||
|
||||
var inventory struct {
|
||||
model.Inventory
|
||||
|
||||
Film model.Film
|
||||
Store model.Store
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var inventory struct {
|
||||
model.Inventory
|
||||
|
||||
Film model.Film
|
||||
Store model.Store
|
||||
}
|
||||
|
||||
err = rows.Scan(&inventory)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
@ -1056,3 +1056,50 @@ func TestScanNumericToNumber(t *testing.T) {
|
|||
require.Equal(t, number.Float32, float32(1.234568e+09))
|
||||
require.Equal(t, number.Float64, float64(1.23456789e+09))
|
||||
}
|
||||
|
||||
// scan into custom base types should be equivalent to the scan into base go types
|
||||
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
|
||||
OriginalLanguageID *MyUint8
|
||||
RentalDuration MyUint8
|
||||
RentalRate MyFloat32
|
||||
Length *MyUint32
|
||||
ReplacementCost MyFloat64
|
||||
Rating *model.FilmRating
|
||||
SpecialFeatures *MyString
|
||||
LastUpdate MyTime
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue