jet/tests/postgres/scan_test.go
go-jet c86903fd1d 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).
2022-02-09 12:34:10 +01:00

1158 lines
30 KiB
Go

package postgres
import (
"context"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/postgres"
"github.com/go-jet/jet/v2/qrm"
"github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/model"
. "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/table"
)
var oneInventoryQuery = Inventory.
SELECT(Inventory.AllColumns).
LIMIT(1).
ORDER_BY(Inventory.InventoryID)
func TestScanToInvalidDestination(t *testing.T) {
t.Run("nil dest", func(t *testing.T) {
testutils.AssertQueryPanicErr(t, oneInventoryQuery, db, nil, "jet: destination is nil")
})
t.Run("struct dest", func(t *testing.T) {
testutils.AssertQueryPanicErr(t, oneInventoryQuery, db, struct{}{}, "jet: destination has to be a pointer to slice or pointer to struct")
})
t.Run("slice dest", func(t *testing.T) {
testutils.AssertQueryPanicErr(t, oneInventoryQuery, db, []struct{}{}, "jet: destination has to be a pointer to slice or pointer to struct")
})
t.Run("slice of pointers to pointer dest", func(t *testing.T) {
testutils.AssertQueryPanicErr(t, oneInventoryQuery, db, []**struct{}{}, "jet: destination has to be a pointer to slice or pointer to struct")
})
t.Run("map dest", func(t *testing.T) {
testutils.AssertQueryPanicErr(t, oneInventoryQuery, db, &map[string]string{}, "jet: destination has to be a pointer to slice or pointer to struct")
})
t.Run("map dest", func(t *testing.T) {
testutils.AssertQueryPanicErr(t, oneInventoryQuery, db, []map[string]string{}, "jet: destination has to be a pointer to slice or pointer to struct")
})
t.Run("map dest", func(t *testing.T) {
testutils.AssertQueryPanicErr(t, oneInventoryQuery, db, &[]map[string]string{}, "jet: unsupported slice element type")
})
}
func TestScanToValidDestination(t *testing.T) {
t.Run("pointer to struct", func(t *testing.T) {
dest := []struct{}{}
err := oneInventoryQuery.Query(db, &dest)
require.NoError(t, err)
})
t.Run("global query function scan", func(t *testing.T) {
queryStr, args := oneInventoryQuery.Sql()
dest := []struct{}{}
rowProcessed, err := qrm.Query(nil, db, queryStr, args, &dest)
require.Equal(t, rowProcessed, int64(1))
require.NoError(t, err)
})
t.Run("pointer to slice", func(t *testing.T) {
err := oneInventoryQuery.Query(db, &[]struct{}{})
require.NoError(t, err)
})
t.Run("pointer to slice of pointer to structs", func(t *testing.T) {
err := oneInventoryQuery.Query(db, &[]*struct{}{})
require.NoError(t, err)
})
t.Run("pointer to slice of integers", func(t *testing.T) {
var dest []int32
err := oneInventoryQuery.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, dest[0], int32(1))
})
t.Run("pointer to slice integer pointers", func(t *testing.T) {
var dest []*int32
err := oneInventoryQuery.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, dest[0], testutils.Int32Ptr(1))
})
t.Run("NULL to integer", func(t *testing.T) {
var dest struct {
Int64 int64
UInt64 uint64
}
err := SELECT(NULL.AS("int64"), NULL.AS("uint64")).Query(db, &dest)
require.NoError(t, err)
require.Equal(t, dest.Int64, int64(0))
require.Equal(t, dest.UInt64, uint64(0))
})
}
func TestScanToStruct(t *testing.T) {
query := Inventory.
SELECT(Inventory.AllColumns).
ORDER_BY(Inventory.InventoryID)
//fmt.Println(query.DebugSql())
t.Run("one struct", func(t *testing.T) {
dest := model.Inventory{}
err := query.LIMIT(1).Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, inventory1, dest)
})
t.Run("multiple structs, just first one used", func(t *testing.T) {
dest := model.Inventory{}
err := query.LIMIT(10).Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, inventory1, dest)
})
t.Run("one struct", func(t *testing.T) {
dest := struct {
model.Inventory
}{}
err := query.LIMIT(1).Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, inventory1, dest.Inventory)
})
t.Run("one struct", func(t *testing.T) {
dest := struct {
*model.Inventory
}{}
err := query.LIMIT(1).Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, inventory1, *dest.Inventory)
})
t.Run("invalid dest", func(t *testing.T) {
dest := struct {
Inventory **model.Inventory
}{}
testutils.AssertQueryPanicErr(t, query, db, &dest, "jet: unsupported dest type: Inventory **model.Inventory")
})
t.Run("invalid dest 2", func(t *testing.T) {
dest := struct {
Inventory ***model.Inventory
}{}
testutils.AssertQueryPanicErr(t, query, db, &dest, "jet: unsupported dest type: Inventory ***model.Inventory")
})
t.Run("custom struct", func(t *testing.T) {
type Inventory struct {
InventoryID *int32 `sql:"primary_key"`
FilmID int16
StoreID *int16
}
dest := Inventory{}
err := query.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, *dest.InventoryID, int32(1))
require.Equal(t, dest.FilmID, int16(1))
require.Equal(t, *dest.StoreID, int16(1))
})
t.Run("type convert int32 to int", func(t *testing.T) {
type Inventory struct {
InventoryID int
FilmID string
}
dest := Inventory{}
err := query.Query(db, &dest)
require.NoError(t, err)
})
t.Run("type mismatch scanner type", func(t *testing.T) {
type Inventory struct {
InventoryID uuid.UUID
FilmID string
}
dest := Inventory{}
err := query.Query(db, &dest)
require.Error(t, err)
require.EqualError(t, err, "jet: can't scan int64('\\x01') to 'InventoryID uuid.UUID': Scan: unable to scan type int64 into UUID")
})
t.Run("type mismatch base type", func(t *testing.T) {
type Inventory struct {
InventoryID int32
FilmID bool
}
dest := []Inventory{}
err := query.OFFSET(10).Query(db, &dest)
require.Error(t, err)
require.EqualError(t, err, "jet: can't assign int64('\\x02') to 'FilmID bool': can't assign int64(2) to bool")
})
}
func TestScanToNestedStruct(t *testing.T) {
query := Inventory.
INNER_JOIN(Film, Inventory.FilmID.EQ(Film.FilmID)).
INNER_JOIN(Store, Inventory.StoreID.EQ(Store.StoreID)).
SELECT(Inventory.AllColumns, Film.AllColumns, Store.AllColumns).
WHERE(Inventory.InventoryID.EQ(Int(1)))
t.Run("embedded structs", func(t *testing.T) {
dest := struct {
model.Inventory
model.Film
model.Store
}{}
err := query.Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, dest.Inventory, inventory1)
testutils.AssertDeepEqual(t, dest.Film, film1)
testutils.AssertDeepEqual(t, dest.Store, store1)
})
t.Run("embedded pointer structs", func(t *testing.T) {
dest := struct {
*model.Inventory
*model.Film
*model.Store
}{}
err := query.Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, *dest.Inventory, inventory1)
testutils.AssertDeepEqual(t, *dest.Film, film1)
testutils.AssertDeepEqual(t, *dest.Store, store1)
})
t.Run("embedded unused structs", func(t *testing.T) {
dest := struct {
model.Inventory
model.Actor //unused
}{}
err := query.Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, dest.Inventory, inventory1)
testutils.AssertDeepEqual(t, dest.Actor, model.Actor{})
})
t.Run("embedded unused pointer structs", func(t *testing.T) {
dest := struct {
model.Inventory
*model.Actor //unused
}{}
err := query.Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, dest.Inventory, inventory1)
testutils.AssertDeepEqual(t, dest.Actor, (*model.Actor)(nil))
})
t.Run("embedded unused pointer structs", func(t *testing.T) {
dest := struct {
model.Inventory
Actor *model.Actor //unused
}{}
err := query.Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, dest.Inventory, inventory1)
testutils.AssertDeepEqual(t, dest.Actor, (*model.Actor)(nil))
})
t.Run("embedded pointer to selected column", func(t *testing.T) {
query := Inventory.
INNER_JOIN(Film, Inventory.FilmID.EQ(Film.FilmID)).
INNER_JOIN(Store, Inventory.StoreID.EQ(Store.StoreID)).
SELECT(Inventory.AllColumns, Film.AllColumns, Store.AllColumns, String("").AS("actor.first_name")).
WHERE(Inventory.InventoryID.EQ(Int(1)))
dest := struct {
model.Inventory
Actor *model.Actor //unused
}{}
err := query.Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, dest.Inventory, inventory1)
require.True(t, dest.Actor != nil)
})
t.Run("struct embedded unused pointer", func(t *testing.T) {
dest := struct {
model.Inventory
Actor *struct {
model.Actor
} //unused
}{}
err := query.Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, dest.Inventory, inventory1)
testutils.AssertDeepEqual(t, dest.Actor, (*struct{ model.Actor })(nil))
})
t.Run("multiple embedded unused pointer", func(t *testing.T) {
dest := struct {
model.Inventory
Actor *struct {
model.Actor //unused
model.Language //unesed
}
}{}
err := query.Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, dest.Inventory, inventory1)
testutils.AssertDeepEqual(t, dest.Actor, (*struct {
model.Actor
model.Language
})(nil))
})
t.Run("field not nil, embedded selected model", func(t *testing.T) {
dest := struct {
model.Inventory
Actor *struct {
model.Actor //unselected
model.Film //selected
}
}{}
err := query.Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, dest.Inventory, inventory1)
require.True(t, dest.Actor != nil)
testutils.AssertDeepEqual(t, dest.Actor.Actor, model.Actor{})
testutils.AssertDeepEqual(t, dest.Actor.Film, film1)
})
t.Run("field not nil, deeply nested selected model", func(t *testing.T) {
dest := struct {
model.Inventory
Actor *struct {
model.Actor //unselected
Film *struct {
*model.Film //selected
}
}
}{}
err := query.Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, dest.Inventory, inventory1)
require.True(t, dest.Actor != nil)
require.True(t, dest.Actor.Film != nil)
testutils.AssertDeepEqual(t, dest.Actor.Film.Film, &film1)
})
t.Run("embedded structs", func(t *testing.T) {
query := Inventory.
INNER_JOIN(Film, Inventory.FilmID.EQ(Film.FilmID)).
INNER_JOIN(Store, Inventory.StoreID.EQ(Store.StoreID)).
INNER_JOIN(Language, Film.LanguageID.EQ(Language.LanguageID)).
SELECT(Inventory.AllColumns, Film.AllColumns, Store.AllColumns, Language.AllColumns).
WHERE(Inventory.InventoryID.EQ(Int(1)))
type Language3 model.Language
dest := struct {
model.Inventory
Film struct {
model.Film
Language model.Language
Language2 *model.Language `alias:"Language.*"`
Language3 *Language3 `alias:"language"`
Lang struct {
model.Language
}
Lang2 *struct {
model.Language
}
}
Store model.Store
}{}
err := query.Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, dest.Inventory, inventory1)
testutils.AssertDeepEqual(t, dest.Film.Film, film1)
testutils.AssertDeepEqual(t, dest.Store, store1)
testutils.AssertDeepEqual(t, dest.Film.Language, language1)
testutils.AssertDeepEqual(t, dest.Film.Lang.Language, language1)
testutils.AssertDeepEqual(t, dest.Film.Lang2.Language, language1)
testutils.AssertDeepEqual(t, dest.Film.Language2, &language1)
testutils.AssertDeepEqual(t, model.Language(*dest.Film.Language3), language1)
})
}
func TestScanToSlice(t *testing.T) {
t.Run("slice of structs", func(t *testing.T) {
query := Inventory.
SELECT(Inventory.AllColumns).
ORDER_BY(Inventory.InventoryID).
LIMIT(10)
t.Run("slice od inventory", func(t *testing.T) {
dest := []model.Inventory{}
err := query.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, len(dest), 10)
testutils.AssertDeepEqual(t, dest[0], inventory1)
testutils.AssertDeepEqual(t, dest[1], inventory2)
})
t.Run("slice of ints", func(t *testing.T) {
var dest []int32
err := query.Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, dest, []int32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
})
t.Run("slice type convertible", func(t *testing.T) {
var dest []int
err := query.Query(db, &dest)
require.NoError(t, err)
})
t.Run("slice type mismatch", func(t *testing.T) {
var dest []bool
err := query.Query(db, &dest)
require.Error(t, err)
require.EqualError(t, err, `jet: can't append int64 to []bool slice: can't assign int64(2) to bool`)
})
})
t.Run("slice of complex structs", func(t *testing.T) {
query := Inventory.
INNER_JOIN(Film, Inventory.FilmID.EQ(Film.FilmID)).
INNER_JOIN(Store, Inventory.StoreID.EQ(Store.StoreID)).
SELECT(
Inventory.AllColumns,
Film.AllColumns,
Store.AllColumns,
).
ORDER_BY(Inventory.InventoryID).
LIMIT(10)
t.Run("struct with slice of ints", func(t *testing.T) {
var dest struct {
model.Film
IDs []int32 `alias:"inventory.inventory_id"`
}
err := query.Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, dest.Film, film1)
testutils.AssertDeepEqual(t, dest.IDs, []int32{1, 2, 3, 4, 5, 6, 7, 8})
})
t.Run("slice of structs with slice of ints", func(t *testing.T) {
var dest []struct {
model.Film
IDs []int32 `alias:"inventory.inventory_id"`
}
err := query.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, len(dest), 2)
testutils.AssertDeepEqual(t, dest[0].Film, film1)
testutils.AssertDeepEqual(t, dest[0].IDs, []int32{1, 2, 3, 4, 5, 6, 7, 8})
testutils.AssertDeepEqual(t, dest[1].Film, film2)
testutils.AssertDeepEqual(t, dest[1].IDs, []int32{9, 10})
})
t.Run("slice of structs with slice of pointer to ints", func(t *testing.T) {
var dest []struct {
model.Film
IDs []*int32 `alias:"inventory.inventory_id"`
}
err := query.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, len(dest), 2)
testutils.AssertDeepEqual(t, dest[0].Film, film1)
testutils.AssertDeepEqual(t, dest[0].IDs, []*int32{testutils.Int32Ptr(1), testutils.Int32Ptr(2), testutils.Int32Ptr(3), testutils.Int32Ptr(4),
testutils.Int32Ptr(5), testutils.Int32Ptr(6), testutils.Int32Ptr(7), testutils.Int32Ptr(8)})
testutils.AssertDeepEqual(t, dest[1].Film, film2)
testutils.AssertDeepEqual(t, dest[1].IDs, []*int32{testutils.Int32Ptr(9), testutils.Int32Ptr(10)})
})
t.Run("complex struct 1", func(t *testing.T) {
dest := []struct {
model.Inventory
model.Film
model.Store
}{}
err := query.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, len(dest), 10)
testutils.AssertDeepEqual(t, dest[0].Inventory, inventory1)
testutils.AssertDeepEqual(t, dest[0].Film, film1)
testutils.AssertDeepEqual(t, dest[0].Store, store1)
testutils.AssertDeepEqual(t, dest[1].Inventory, inventory2)
})
t.Run("complex struct 2", func(t *testing.T) {
var dest []struct {
*model.Inventory
model.Film
*model.Store
}
err := query.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, len(dest), 10)
testutils.AssertDeepEqual(t, dest[0].Inventory, &inventory1)
testutils.AssertDeepEqual(t, dest[0].Film, film1)
testutils.AssertDeepEqual(t, dest[0].Store, &store1)
testutils.AssertDeepEqual(t, dest[1].Inventory, &inventory2)
})
t.Run("complex struct 3", func(t *testing.T) {
var dest []struct {
Inventory model.Inventory
Film *model.Film
Store struct {
*model.Store
}
}
err := query.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, len(dest), 10)
testutils.AssertDeepEqual(t, dest[0].Inventory, inventory1)
testutils.AssertDeepEqual(t, dest[0].Film, &film1)
testutils.AssertDeepEqual(t, dest[0].Store.Store, &store1)
testutils.AssertDeepEqual(t, dest[1].Inventory, inventory2)
})
t.Run("complex struct 4", func(t *testing.T) {
var dest []struct {
model.Film
Inventories []struct {
model.Inventory
model.Store
}
}
err := query.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, len(dest), 2)
testutils.AssertDeepEqual(t, dest[0].Film, film1)
testutils.AssertDeepEqual(t, len(dest[0].Inventories), 8)
testutils.AssertDeepEqual(t, dest[0].Inventories[0].Inventory, inventory1)
testutils.AssertDeepEqual(t, dest[0].Inventories[0].Store, store1)
})
t.Run("complex struct 5", func(t *testing.T) {
var dest []struct {
model.Film
Inventories []struct {
model.Inventory
Rentals *[]model.Rental
Rentals2 []model.Rental
}
}
err := query.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, len(dest), 2)
testutils.AssertDeepEqual(t, dest[0].Film, film1)
require.Equal(t, len(dest[0].Inventories), 8)
testutils.AssertDeepEqual(t, dest[0].Inventories[0].Inventory, inventory1)
require.True(t, dest[0].Inventories[0].Rentals == nil)
require.True(t, dest[0].Inventories[0].Rentals2 == nil)
})
})
t.Run("slice of complex structs 2", func(t *testing.T) {
query := Country.
INNER_JOIN(City, City.CountryID.EQ(Country.CountryID)).
INNER_JOIN(Address, Address.CityID.EQ(City.CityID)).
INNER_JOIN(Customer, Customer.AddressID.EQ(Address.AddressID)).
SELECT(Country.AllColumns, City.AllColumns, Address.AllColumns, Customer.AllColumns).
ORDER_BY(Country.CountryID.ASC(), City.CityID.ASC(), Address.AddressID.ASC(), Customer.CustomerID.ASC()).
LIMIT(1000)
t.Run("dest1", func(t *testing.T) {
var dest []struct {
model.Country
Cities []struct {
model.City
Adresses []struct {
model.Address
Customer model.Customer
}
}
}
err := query.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, len(dest), 108)
testutils.AssertDeepEqual(t, dest[100].Country, countryUk)
require.Equal(t, len(dest[100].Cities), 8)
testutils.AssertDeepEqual(t, dest[100].Cities[2].City, cityLondon)
require.Equal(t, len(dest[100].Cities[2].Adresses), 2)
testutils.AssertDeepEqual(t, dest[100].Cities[2].Adresses[0].Address, address256)
testutils.AssertDeepEqual(t, dest[100].Cities[2].Adresses[0].Customer, customer256)
testutils.AssertDeepEqual(t, dest[100].Cities[2].Adresses[1].Address, addres517)
testutils.AssertDeepEqual(t, dest[100].Cities[2].Adresses[1].Customer, customer512)
})
t.Run("dest1", func(t *testing.T) {
var dest []*struct {
*model.Country
Cities []*struct {
*model.City
Adresses *[]*struct {
*model.Address
Customer *model.Customer
}
}
}
err := query.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, len(dest), 108)
testutils.AssertDeepEqual(t, dest[100].Country, &countryUk)
require.Equal(t, len(dest[100].Cities), 8)
testutils.AssertDeepEqual(t, dest[100].Cities[2].City, &cityLondon)
require.Equal(t, len(*dest[100].Cities[2].Adresses), 2)
testutils.AssertDeepEqual(t, (*dest[100].Cities[2].Adresses)[0].Address, &address256)
testutils.AssertDeepEqual(t, (*dest[100].Cities[2].Adresses)[0].Customer, &customer256)
testutils.AssertDeepEqual(t, (*dest[100].Cities[2].Adresses)[1].Address, &addres517)
testutils.AssertDeepEqual(t, (*dest[100].Cities[2].Adresses)[1].Customer, &customer512)
})
})
t.Run("dest1", func(t *testing.T) {
var dest []*struct {
*model.Country
Cities []**struct {
*model.City
}
}
testutils.AssertQueryPanicErr(t, oneInventoryQuery, db, &dest, "jet: unsupported slice element type at 'Cities []**struct { *model.City }'")
})
}
func TestStructScanErrNoRows(t *testing.T) {
query := SELECT(Customer.AllColumns).
FROM(Customer).
WHERE(Customer.CustomerID.EQ(Int(-1)))
customer := model.Customer{}
err := query.Query(db, &customer)
require.Error(t, err, qrm.ErrNoRows.Error())
}
func TestStructScanAllNull(t *testing.T) {
query := SELECT(NULL.AS("null1"), NULL.AS("null2"))
dest := struct {
Null1 *int
Null2 *int
}{}
err := query.Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, dest, struct {
Null1 *int
Null2 *int
}{})
}
func TestRowsScan(t *testing.T) {
stmt := SELECT(
Inventory.AllColumns,
).FROM(
Inventory,
).ORDER_BY(
Inventory.InventoryID.ASC(),
)
rows, err := stmt.Rows(context.Background(), db)
require.NoError(t, err)
for rows.Next() {
var inventory model.Inventory
err = rows.Scan(&inventory)
require.NoError(t, err)
require.NotEqual(t, inventory.InventoryID, int32(0))
require.NotEqual(t, inventory.FilmID, int16(0))
require.NotEqual(t, inventory.StoreID, int16(0))
require.NotEqual(t, inventory.LastUpdate, time.Time{})
if inventory.InventoryID == 2103 {
require.Equal(t, inventory.FilmID, int16(456))
require.Equal(t, inventory.StoreID, int16(2))
require.Equal(t, inventory.LastUpdate.Format(time.RFC3339), "2006-02-15T10:09:17Z")
}
}
err = rows.Close()
require.NoError(t, err)
err = rows.Err()
require.NoError(t, err)
requireLogged(t, stmt)
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
Float64 float64
}
numeric := CAST(Decimal("1234567890.111")).AS_NUMERIC()
stmt := SELECT(
numeric.AS("number.float32"),
numeric.AS("number.float64"),
)
var number Number
err := stmt.Query(db, &number)
require.NoError(t, err)
require.Equal(t, number.Float32, float32(1.234568e+09))
require.Equal(t, number.Float64, float64(1.234567890111e+09))
}
func TestScanNumericToIntegerError(t *testing.T) {
var dest struct {
Integer int32
}
err := SELECT(
CAST(Decimal("1234567890.111")).AS_NUMERIC().AS("integer"),
).Query(db, &dest)
require.Error(t, err)
if isPgxDriver() {
require.Contains(t, err.Error(), `jet: can't assign string("1234567890.111") to 'Integer int32': converting driver.Value type string ("1234567890.111") to a int64: invalid syntax`)
} else {
require.Contains(t, err.Error(), `jet: can't assign []uint8("1234567890.111") to 'Integer int32': converting driver.Value type []uint8 ("1234567890.111") to a int64: invalid syntax`)
}
}
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) {
tx, err := db.Begin()
require.NoError(t, err)
defer tx.Rollback()
// add actor without associated film (so that destination Title array is NULL).
_, err = Actor.INSERT().
MODEL(
model.Actor{
ActorID: 201,
FirstName: "Brigitte",
LastName: "Bardot",
LastUpdate: time.Time{},
},
).Exec(tx)
require.NoError(t, err)
stmt := SELECT(
Actor.ActorID.AS("actor_id"),
Film.Title.AS("title"),
).FROM(
Actor.
LEFT_JOIN(FilmActor, Actor.ActorID.EQ(FilmActor.ActorID)).
LEFT_JOIN(Film, Film.FilmID.EQ(FilmActor.FilmID)),
).WHERE(
Actor.ActorID.GT(Int(199)),
).ORDER_BY(Actor.ActorID.DESC())
var dest []struct {
ActorID int `sql:"primary_key"`
Title []string
}
err = stmt.Query(tx, &dest)
require.NoError(t, err)
require.Equal(t, dest[0].ActorID, 201)
require.Equal(t, dest[0].Title, []string(nil))
require.Equal(t, dest[1].ActorID, 200)
require.Len(t, dest[1].Title, 20)
}
var address256 = model.Address{
AddressID: 256,
Address: "1497 Yuzhou Drive",
Address2: testutils.StringPtr(""),
District: "England",
CityID: 312,
PostalCode: testutils.StringPtr("3433"),
Phone: "246810237916",
LastUpdate: *testutils.TimestampWithoutTimeZone("2006-02-15 09:45:30", 0),
}
var addres517 = model.Address{
AddressID: 517,
Address: "548 Uruapan Street",
Address2: testutils.StringPtr(""),
District: "Ontario",
CityID: 312,
PostalCode: testutils.StringPtr("35653"),
Phone: "879347453467",
LastUpdate: *testutils.TimestampWithoutTimeZone("2006-02-15 09:45:30", 0),
}
var customer256 = model.Customer{
CustomerID: 252,
StoreID: 2,
FirstName: "Mattie",
LastName: "Hoffman",
Email: testutils.StringPtr("mattie.hoffman@sakilacustomer.org"),
AddressID: 256,
Activebool: true,
CreateDate: *testutils.TimestampWithoutTimeZone("2006-02-14 00:00:00", 0),
LastUpdate: testutils.TimestampWithoutTimeZone("2013-05-26 14:49:45.738", 0),
Active: testutils.Int32Ptr(1),
}
var customer512 = model.Customer{
CustomerID: 512,
StoreID: 1,
FirstName: "Cecil",
LastName: "Vines",
Email: testutils.StringPtr("cecil.vines@sakilacustomer.org"),
AddressID: 517,
Activebool: true,
CreateDate: *testutils.TimestampWithoutTimeZone("2006-02-14 00:00:00", 0),
LastUpdate: testutils.TimestampWithoutTimeZone("2013-05-26 14:49:45.738", 0),
Active: testutils.Int32Ptr(1),
}
var countryUk = model.Country{
CountryID: 102,
Country: "United Kingdom",
LastUpdate: *testutils.TimestampWithoutTimeZone("2006-02-15 09:44:00", 0),
}
var cityLondon = model.City{
CityID: 312,
City: "London",
CountryID: 102,
LastUpdate: *testutils.TimestampWithoutTimeZone("2006-02-15 09:45:25", 0),
}
var inventory1 = model.Inventory{
InventoryID: 1,
FilmID: 1,
StoreID: 1,
LastUpdate: *testutils.TimestampWithoutTimeZone("2006-02-15 10:09:17", 0),
}
var inventory2 = model.Inventory{
InventoryID: 2,
FilmID: 1,
StoreID: 1,
LastUpdate: *testutils.TimestampWithoutTimeZone("2006-02-15 10:09:17", 0),
}
var film1 = model.Film{
FilmID: 1,
Title: "Academy Dinosaur",
Description: testutils.StringPtr("A Epic Drama of a Feminist And a Mad Scientist who must Battle a Teacher in The Canadian Rockies"),
ReleaseYear: testutils.Int32Ptr(2006),
LanguageID: 1,
RentalDuration: 6,
RentalRate: 0.99,
Length: testutils.Int16Ptr(86),
ReplacementCost: 20.99,
Rating: &pgRating,
LastUpdate: *testutils.TimestampWithoutTimeZone("2013-05-26 14:50:58.951", 3),
SpecialFeatures: testutils.StringPtr("{\"Deleted Scenes\",\"Behind the Scenes\"}"),
Fulltext: "'academi':1 'battl':15 'canadian':20 'dinosaur':2 'drama':5 'epic':4 'feminist':8 'mad':11 'must':14 'rocki':21 'scientist':12 'teacher':17",
}
var film2 = model.Film{
FilmID: 2,
Title: "Ace Goldfinger",
Description: testutils.StringPtr("A Astounding Epistle of a Database Administrator And a Explorer who must Find a Car in Ancient China"),
ReleaseYear: testutils.Int32Ptr(2006),
LanguageID: 1,
RentalDuration: 3,
RentalRate: 4.99,
Length: testutils.Int16Ptr(48),
ReplacementCost: 12.99,
Rating: &gRating,
LastUpdate: *testutils.TimestampWithoutTimeZone("2013-05-26 14:50:58.951", 3),
SpecialFeatures: testutils.StringPtr(`{Trailers,"Deleted Scenes"}`),
Fulltext: `'ace':1 'administr':9 'ancient':19 'astound':4 'car':17 'china':20 'databas':8 'epistl':5 'explor':12 'find':15 'goldfing':2 'must':14`,
}
var store1 = model.Store{
StoreID: 1,
ManagerStaffID: 1,
AddressID: 1,
LastUpdate: *testutils.TimestampWithoutTimeZone("2006-02-15 09:57:12", 0),
}
var pgRating = model.MpaaRating_Pg
var gRating = model.MpaaRating_G
var language1 = model.Language{
LanguageID: 1,
Name: "English ",
LastUpdate: *testutils.TimestampWithoutTimeZone("2006-02-15 10:02:19", 0),
}