Qrm refactor

- Allow custom types Scan method to read values returned by the driver rather then the value from intermediate Null types. Scan to intermidiate Null types removed.
- Better error handling
This commit is contained in:
go-jet 2021-10-15 17:43:10 +02:00
parent 555ec293fb
commit 0d418890ab
11 changed files with 459 additions and 574 deletions

View file

@ -17,11 +17,12 @@ import (
)
func TestAllTypesSelect(t *testing.T) {
skipForPgxDriver(t) // pgx driver returns time with time zone as string
dest := []model.AllTypes{}
err := AllTypes.SELECT(AllTypes.AllColumns).Query(db, &dest)
err := AllTypes.SELECT(
AllTypes.AllColumns,
).LIMIT(2).
Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, dest[0], allTypesRow0)
@ -29,8 +30,6 @@ func TestAllTypesSelect(t *testing.T) {
}
func TestAllTypesViewSelect(t *testing.T) {
skipForPgxDriver(t) // pgx driver returns time with time zone as string
type AllTypesView model.AllTypes
dest := []AllTypesView{}
@ -43,7 +42,7 @@ func TestAllTypesViewSelect(t *testing.T) {
}
func TestAllTypesInsertModel(t *testing.T) {
skipForPgxDriver(t) // pgx driver does not handle well time with time zone
skipForPgxDriver(t) // pgx driver bug ERROR: date/time field value out of range: "0000-01-01 12:05:06Z" (SQLSTATE 22008)
query := AllTypes.INSERT(AllTypes.AllColumns).
MODEL(allTypesRow0).
@ -60,8 +59,6 @@ func TestAllTypesInsertModel(t *testing.T) {
}
func TestAllTypesInsertQuery(t *testing.T) {
skipForPgxDriver(t) // pgx driver does not handle well time with time zone
query := AllTypes.INSERT(AllTypes.AllColumns).
QUERY(
AllTypes.
@ -80,8 +77,6 @@ func TestAllTypesInsertQuery(t *testing.T) {
}
func TestAllTypesFromSubQuery(t *testing.T) {
skipForPgxDriver(t)
subQuery := SELECT(AllTypes.AllColumns).
FROM(AllTypes).
AsTable("allTypesSubQuery")
@ -302,10 +297,10 @@ LIMIT $11;
func TestExpressionCast(t *testing.T) {
skipForPgxDriver(t) // for some reason, pgx driver, 150:char(12) returns as int value
skipForPgxDriver(t) // pgx driver bug 'cannot convert 151 to Text'
query := AllTypes.SELECT(
CAST(Int(150)).AS_CHAR(12).AS("char12"),
CAST(Int(151)).AS_CHAR(12).AS("char12"),
CAST(String("TRUE")).AS_BOOL(),
CAST(String("111")).AS_SMALLINT(),
CAST(String("111")).AS_INTEGER(),
@ -349,7 +344,7 @@ func TestExpressionCast(t *testing.T) {
}
func TestStringOperators(t *testing.T) {
skipForPgxDriver(t) // pgx driver returns text column as int value
skipForPgxDriver(t) // pgx driver bug 'cannot convert 11 to Text'
query := AllTypes.SELECT(
AllTypes.Text.EQ(AllTypes.Char),
@ -866,8 +861,6 @@ func TestInterval(t *testing.T) {
}
func TestSubQueryColumnReference(t *testing.T) {
skipForPgxDriver(t) // pgx driver returns time with time zone as string value
type expected struct {
sql string
args []interface{}
@ -1044,8 +1037,6 @@ FROM`
}
func TestTimeLiterals(t *testing.T) {
skipForPgxDriver(t) // pgx driver returns time with time zone as string
loc, err := time.LoadLocation("Europe/Berlin")
require.NoError(t, err)
@ -1060,8 +1051,6 @@ func TestTimeLiterals(t *testing.T) {
).FROM(AllTypes).
LIMIT(1)
//fmt.Println(query.Sql())
testutils.AssertStatementSql(t, query, `
SELECT $1::date AS "date",
$2::time without time zone AS "time",
@ -1073,25 +1062,29 @@ LIMIT $6;
`)
var dest struct {
Date time.Time
Time time.Time
Timez time.Time
Timestamp time.Time
//Timestampz time.Time
Date time.Time
Time time.Time
Timez time.Time
Timestamp time.Time
Timestampz time.Time
}
err = query.Query(db, &dest)
require.NoError(t, err)
//testutils.PrintJson(dest)
// pq driver will return time with time zone in local timezone,
// while pgx driver will return time in UTC time zone
dest.Timez = dest.Timez.UTC()
dest.Timestampz = dest.Timestampz.UTC()
testutils.AssertJSON(t, dest, `
{
"Date": "2009-11-17T00:00:00Z",
"Time": "0000-01-01T20:34:58.651387Z",
"Timez": "0000-01-01T20:34:58.651387+01:00",
"Timestamp": "2009-11-17T20:34:58.651387Z"
"Timez": "0000-01-01T19:34:58.651387Z",
"Timestamp": "2009-11-17T20:34:58.651387Z",
"Timestampz": "2009-11-17T19:34:58.651387Z"
}
`)
requireLogged(t, query)

View file

@ -31,7 +31,7 @@ func TestMain(m *testing.M) {
setTestRoot()
for _, driverName := range []string{"postgres", "pgx"} {
for _, driverName := range []string{"pgx", "postgres"} {
fmt.Printf("\nRunning postgres tests for '%s' driver\n", driverName)
func() {
@ -81,8 +81,16 @@ func requireLogged(t *testing.T, statement postgres.Statement) {
}
func skipForPgxDriver(t *testing.T) {
switch db.Driver().(type) {
case *stdlib.Driver:
if isPgxDriver() {
t.SkipNow()
}
}
func isPgxDriver() bool {
switch db.Driver().(type) {
case *stdlib.Driver:
return true
}
return false
}

View file

@ -78,16 +78,31 @@ func TestScanToValidDestination(t *testing.T) {
require.NoError(t, err)
})
t.Run("pointer to slice of strings", func(t *testing.T) {
err := oneInventoryQuery.Query(db, &[]int32{})
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 of strings", func(t *testing.T) {
err := oneInventoryQuery.Query(db, &[]*int32{})
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))
})
}
@ -189,7 +204,9 @@ func TestScanToStruct(t *testing.T) {
dest := Inventory{}
testutils.AssertQueryPanicErr(t, query, db, &dest, `jet: Scan: unable to scan type int32 into UUID, at 'InventoryID uuid.UUID' of type postgres.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) {
@ -200,7 +217,9 @@ func TestScanToStruct(t *testing.T) {
dest := []Inventory{}
testutils.AssertQueryPanicErr(t, query.OFFSET(10), db, &dest, `jet: can't set int16 to bool`)
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")
})
}
@ -451,8 +470,9 @@ func TestScanToSlice(t *testing.T) {
t.Run("slice type mismatch", func(t *testing.T) {
var dest []bool
testutils.AssertQueryPanicErr(t, query, db, &dest, `jet: can't append int32 to []bool slice`)
//require.Error(t, err, `jet: can't append int32 to []bool slice `)
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`)
})
})
@ -764,16 +784,8 @@ func TestRowsScan(t *testing.T) {
requireLogged(t, stmt)
}
func TestScanNumericToNumber(t *testing.T) {
func TestScanNumericToFloat(t *testing.T) {
type Number struct {
Int8 int8
UInt8 uint8
Int16 int16
UInt16 uint16
Int32 int32
UInt32 uint32
Int64 int64
UInt64 uint64
Float32 float32
Float64 float64
}
@ -781,14 +793,6 @@ func TestScanNumericToNumber(t *testing.T) {
numeric := CAST(Decimal("1234567890.111")).AS_NUMERIC()
stmt := SELECT(
numeric.AS("number.int8"),
numeric.AS("number.uint8"),
numeric.AS("number.int16"),
numeric.AS("number.uint16"),
numeric.AS("number.int32"),
numeric.AS("number.uint32"),
numeric.AS("number.int64"),
numeric.AS("number.uint64"),
numeric.AS("number.float32"),
numeric.AS("number.float64"),
)
@ -796,19 +800,30 @@ func TestScanNumericToNumber(t *testing.T) {
var number Number
err := stmt.Query(db, &number)
require.NoError(t, err)
require.Equal(t, number.Int8, int8(-46)) // overflow
require.Equal(t, number.UInt8, uint8(210)) // overflow
require.Equal(t, number.Int16, int16(722)) // overflow
require.Equal(t, number.UInt16, uint16(722)) // overflow
require.Equal(t, number.Int32, int32(1234567890))
require.Equal(t, number.UInt32, uint32(1234567890))
require.Equal(t, number.Int64, int64(1234567890))
require.Equal(t, number.UInt64, uint64(1234567890))
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`)
}
}
// 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) {