Merge pull request #459 from go-jet/strict-scan
Add support for strict scan.
This commit is contained in:
commit
7ab44bc61c
9 changed files with 402 additions and 207 deletions
23
qrm/qrm.go
23
qrm/qrm.go
|
|
@ -10,6 +10,21 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Config holds the configuration settings for QRM scanning behavior.
|
||||||
|
type Config struct {
|
||||||
|
// StrictScan, when true, causes the scanning function to panic if it encounters any
|
||||||
|
// unused columns in the SQL query result. This ensures that every column is mapped
|
||||||
|
// to a field in the destination struct.
|
||||||
|
// Does not apply to statements build with SELECT_JSON_OBJ or SELECT_JSON_ARR
|
||||||
|
StrictScan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalConfig is the package-wide configuration for SQL scanning.
|
||||||
|
// This variable should be modified only once, for instance, during application initialization.
|
||||||
|
var GlobalConfig = Config{
|
||||||
|
StrictScan: false,
|
||||||
|
}
|
||||||
|
|
||||||
// ErrNoRows is returned by Query when query result set is empty
|
// ErrNoRows is returned by Query when query result set is empty
|
||||||
var ErrNoRows = errors.New("qrm: no rows in result set")
|
var ErrNoRows = errors.New("qrm: no rows in result set")
|
||||||
|
|
||||||
|
|
@ -199,12 +214,16 @@ func ScanOneRowToDest(scanContext *ScanContext, rows *sql.Rows, destPtr interfac
|
||||||
|
|
||||||
destValuePtr := reflect.ValueOf(destPtr)
|
destValuePtr := reflect.ValueOf(destPtr)
|
||||||
|
|
||||||
|
scanContext.rowNum++
|
||||||
|
|
||||||
_, err = mapRowToStruct(scanContext, "", destValuePtr, nil)
|
_, err = mapRowToStruct(scanContext, "", destValuePtr, nil)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("jet: failed to scan a row into destination, %w", err)
|
return fmt.Errorf("jet: failed to scan a row into destination, %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scanContext.EnsureEveryColumnRead() // can panic
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,6 +265,10 @@ func queryToSlice(ctx context.Context, db Queryable, query string, args []interf
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return scanContext.rowNum, err
|
return scanContext.rowNum, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if scanContext.rowNum == 1 && GlobalConfig.StrictScan {
|
||||||
|
scanContext.EnsureEveryColumnRead()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = rows.Close()
|
err = rows.Close()
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,9 @@ type ScanContext struct {
|
||||||
groupKeyInfoCache map[string]groupKeyInfo
|
groupKeyInfoCache map[string]groupKeyInfo
|
||||||
typeInfoMap map[string]typeInfo
|
typeInfoMap map[string]typeInfo
|
||||||
|
|
||||||
typesVisited typeStack // to prevent circular dependency scan
|
typesVisited typeStack // to prevent circular dependency scan
|
||||||
|
columnAlias []string
|
||||||
|
columnIndexRead []bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewScanContext creates new ScanContext from rows
|
// NewScanContext creates new ScanContext from rows
|
||||||
|
|
@ -57,9 +59,26 @@ func NewScanContext(rows *sql.Rows) (*ScanContext, error) {
|
||||||
typeInfoMap: make(map[string]typeInfo),
|
typeInfoMap: make(map[string]typeInfo),
|
||||||
|
|
||||||
typesVisited: newTypeStack(),
|
typesVisited: newTypeStack(),
|
||||||
|
|
||||||
|
columnAlias: aliases,
|
||||||
|
columnIndexRead: make([]bool, len(aliases)),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ScanContext) EnsureEveryColumnRead() {
|
||||||
|
var neverUsedColumns []string
|
||||||
|
|
||||||
|
for index, read := range s.columnIndexRead {
|
||||||
|
if !read {
|
||||||
|
neverUsedColumns = append(neverUsedColumns, `'`+s.columnAlias[index]+`'`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(neverUsedColumns) > 0 {
|
||||||
|
panic("jet: columns never used: " + strings.Join(neverUsedColumns, ", "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func createScanSlice(columnCount int) []interface{} {
|
func createScanSlice(columnCount int) []interface{} {
|
||||||
scanPtrSlice := make([]interface{}, columnCount)
|
scanPtrSlice := make([]interface{}, columnCount)
|
||||||
|
|
||||||
|
|
@ -244,6 +263,9 @@ func (s *ScanContext) typeToColumnIndex(typeName, fieldName string) int {
|
||||||
// rowElemValue always returns non-ptr value,
|
// rowElemValue always returns non-ptr value,
|
||||||
// invalid value is nil
|
// invalid value is nil
|
||||||
func (s *ScanContext) rowElemValue(index int) reflect.Value {
|
func (s *ScanContext) rowElemValue(index int) reflect.Value {
|
||||||
|
if s.rowNum == 1 {
|
||||||
|
s.columnIndexRead[index] = true
|
||||||
|
}
|
||||||
scannedValue := reflect.ValueOf(s.row[index])
|
scannedValue := reflect.ValueOf(s.row[index])
|
||||||
return scannedValue.Elem().Elem() // no need to check validity of Elem, because s.row[index] always contains interface in interface
|
return scannedValue.Elem().Elem() // no need to check validity of Elem, because s.row[index] always contains interface in interface
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -591,10 +591,12 @@ func TestExpressionCast(t *testing.T) {
|
||||||
Raw("current_database()"),
|
Raw("current_database()"),
|
||||||
)
|
)
|
||||||
|
|
||||||
var dest []struct{}
|
allowUnusedColumns(func() {
|
||||||
err := query.Query(db, &dest)
|
var dest []struct{}
|
||||||
|
err := query.Query(db, &dest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStringOperators(t *testing.T) {
|
func TestStringOperators(t *testing.T) {
|
||||||
|
|
@ -673,10 +675,11 @@ func TestStringOperators(t *testing.T) {
|
||||||
TO_HEX(AllTypes.IntegerPtr),
|
TO_HEX(AllTypes.IntegerPtr),
|
||||||
)
|
)
|
||||||
|
|
||||||
var dest []struct{}
|
allowUnusedColumns(func() {
|
||||||
err := query.Query(db, &dest)
|
var dest []struct{}
|
||||||
|
err := query.Query(db, &dest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBytea(t *testing.T) {
|
func TestBytea(t *testing.T) {
|
||||||
|
|
@ -792,10 +795,12 @@ FROM test_sample.all_types;
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
var dest []struct{}
|
allowUnusedColumns(func() {
|
||||||
err := stmt.Query(db, &dest)
|
var dest []struct{}
|
||||||
|
err := stmt.Query(db, &dest)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBlobConversion(t *testing.T) {
|
func TestBlobConversion(t *testing.T) {
|
||||||
|
|
@ -1185,9 +1190,11 @@ LIMIT $27;
|
||||||
common.AllTypesIntegerExpResult `alias:"."`
|
common.AllTypesIntegerExpResult `alias:"."`
|
||||||
}
|
}
|
||||||
|
|
||||||
err := query.Query(db, &dest)
|
allowUnusedColumns(func() {
|
||||||
|
err := query.Query(db, &dest)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
//testutils.SaveJSONFile(dest, "./testdata/results/common/int_operators.json")
|
//testutils.SaveJSONFile(dest, "./testdata/results/common/int_operators.json")
|
||||||
//testutils.PrintJson(dest)
|
//testutils.PrintJson(dest)
|
||||||
|
|
@ -1271,9 +1278,12 @@ func TestTimeExpression(t *testing.T) {
|
||||||
// fmt.Println(query.DebugSql())
|
// fmt.Println(query.DebugSql())
|
||||||
|
|
||||||
var dest []struct{}
|
var dest []struct{}
|
||||||
err := query.Query(db, &dest)
|
|
||||||
|
|
||||||
require.NoError(t, err)
|
allowUnusedColumns(func() {
|
||||||
|
err := query.Query(db, &dest)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTimeScan(t *testing.T) {
|
func TestTimeScan(t *testing.T) {
|
||||||
|
|
@ -1616,8 +1626,11 @@ SELECT INTERVAL '1 YEAR',
|
||||||
FROM test_sample.all_types;
|
FROM test_sample.all_types;
|
||||||
`)
|
`)
|
||||||
|
|
||||||
err := stmt.Query(db, &struct{}{})
|
allowUnusedColumns(func() {
|
||||||
require.NoError(t, err)
|
err := stmt.Query(db, &struct{}{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
requireLogged(t, stmt)
|
requireLogged(t, stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1677,8 +1690,10 @@ SELECT EXTRACT(CENTURY FROM all_types.timestampz),
|
||||||
FROM test_sample.all_types;
|
FROM test_sample.all_types;
|
||||||
`)
|
`)
|
||||||
|
|
||||||
err := stmt.Query(db, &struct{}{})
|
allowUnusedColumns(func() {
|
||||||
require.NoError(t, err)
|
err := stmt.Query(db, &struct{}{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRowExpression(t *testing.T) {
|
func TestRowExpression(t *testing.T) {
|
||||||
|
|
@ -1717,8 +1732,10 @@ SELECT ROW($1::integer, $2::real, $3::text) AS "row",
|
||||||
ROW($26::timestamp with time zone) <= ROW($27::timestamp with time zone);
|
ROW($26::timestamp with time zone) <= ROW($27::timestamp with time zone);
|
||||||
`)
|
`)
|
||||||
|
|
||||||
err := stmt.Query(db, &struct{}{})
|
allowUnusedColumns(func() {
|
||||||
require.NoError(t, err)
|
err := stmt.Query(db, &struct{}{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAllTypesSubQueryFrom(t *testing.T) {
|
func TestAllTypesSubQueryFrom(t *testing.T) {
|
||||||
|
|
@ -2048,7 +2065,11 @@ FROM`
|
||||||
|
|
||||||
testutils.AssertDebugStatementSql(t, stmt1, expectedSQL+expected.sql+";\n", expected.args...)
|
testutils.AssertDebugStatementSql(t, stmt1, expectedSQL+expected.sql+";\n", expected.args...)
|
||||||
|
|
||||||
dest1 := []model.AllTypes{}
|
var dest1 []struct {
|
||||||
|
model.AllTypes
|
||||||
|
|
||||||
|
AliasedColumn []byte
|
||||||
|
}
|
||||||
err := stmt1.Query(db, &dest1)
|
err := stmt1.Query(db, &dest1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, len(dest1), 2)
|
require.Equal(t, len(dest1), 2)
|
||||||
|
|
@ -2064,12 +2085,17 @@ FROM`
|
||||||
|
|
||||||
stmt2 := SELECT(
|
stmt2 := SELECT(
|
||||||
subQuery.AllColumns(),
|
subQuery.AllColumns(),
|
||||||
).
|
).FROM(
|
||||||
FROM(subQuery)
|
subQuery,
|
||||||
|
)
|
||||||
|
|
||||||
testutils.AssertDebugStatementSql(t, stmt2, expectedSQL+expected.sql+";\n", expected.args...)
|
testutils.AssertDebugStatementSql(t, stmt2, expectedSQL+expected.sql+";\n", expected.args...)
|
||||||
|
|
||||||
dest2 := []model.AllTypes{}
|
var dest2 []struct {
|
||||||
|
model.AllTypes
|
||||||
|
|
||||||
|
AliasedColumn []byte
|
||||||
|
}
|
||||||
err = stmt2.Query(db, &dest2)
|
err = stmt2.Query(db, &dest2)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
||||||
|
|
@ -320,7 +320,6 @@ func testJoinEverything(t require.TestingT) {
|
||||||
Track.AllColumns,
|
Track.AllColumns,
|
||||||
Genre.AllColumns,
|
Genre.AllColumns,
|
||||||
MediaType.AllColumns,
|
MediaType.AllColumns,
|
||||||
PlaylistTrack.AllColumns,
|
|
||||||
Playlist.AllColumns,
|
Playlist.AllColumns,
|
||||||
Invoice.AllColumns,
|
Invoice.AllColumns,
|
||||||
Customer.AllColumns,
|
Customer.AllColumns,
|
||||||
|
|
@ -365,8 +364,6 @@ SELECT "Artist"."ArtistId" AS "Artist.ArtistId",
|
||||||
"Genre"."Name" AS "Genre.Name",
|
"Genre"."Name" AS "Genre.Name",
|
||||||
"MediaType"."MediaTypeId" AS "MediaType.MediaTypeId",
|
"MediaType"."MediaTypeId" AS "MediaType.MediaTypeId",
|
||||||
"MediaType"."Name" AS "MediaType.Name",
|
"MediaType"."Name" AS "MediaType.Name",
|
||||||
"PlaylistTrack"."PlaylistId" AS "PlaylistTrack.PlaylistId",
|
|
||||||
"PlaylistTrack"."TrackId" AS "PlaylistTrack.TrackId",
|
|
||||||
"Playlist"."PlaylistId" AS "Playlist.PlaylistId",
|
"Playlist"."PlaylistId" AS "Playlist.PlaylistId",
|
||||||
"Playlist"."Name" AS "Playlist.Name",
|
"Playlist"."Name" AS "Playlist.Name",
|
||||||
"Invoice"."InvoiceId" AS "Invoice.InvoiceId",
|
"Invoice"."InvoiceId" AS "Invoice.InvoiceId",
|
||||||
|
|
@ -1295,7 +1292,7 @@ func TestAggregateFunc(t *testing.T) {
|
||||||
WITHIN_GROUP_ORDER_BY(Invoice.BillingAddress.DESC()).AS("percentile_disc_3"),
|
WITHIN_GROUP_ORDER_BY(Invoice.BillingAddress.DESC()).AS("percentile_disc_3"),
|
||||||
|
|
||||||
PERCENTILE_CONT(Float(0.3)).WITHIN_GROUP_ORDER_BY(Invoice.Total).AS("percentile_cont_1"),
|
PERCENTILE_CONT(Float(0.3)).WITHIN_GROUP_ORDER_BY(Invoice.Total).AS("percentile_cont_1"),
|
||||||
PERCENTILE_CONT(Float(0.2)).WITHIN_GROUP_ORDER_BY(INTERVAL(1, HOUR).DESC()).AS("percentile_cont_int"),
|
PERCENTILE_CONT(Float(0.2)).WITHIN_GROUP_ORDER_BY(INTERVAL(1, HOUR).DESC()).AS("percentile_cont_interval"),
|
||||||
|
|
||||||
MODE().WITHIN_GROUP_ORDER_BY(Invoice.BillingPostalCode.DESC()).AS("mode_1"),
|
MODE().WITHIN_GROUP_ORDER_BY(Invoice.BillingPostalCode.DESC()).AS("mode_1"),
|
||||||
).FROM(
|
).FROM(
|
||||||
|
|
@ -1309,18 +1306,19 @@ SELECT PERCENTILE_DISC ($1::double precision) WITHIN GROUP (ORDER BY "Invoice"."
|
||||||
PERCENTILE_DISC ("Invoice"."Total" / $2) WITHIN GROUP (ORDER BY "Invoice"."InvoiceDate" ASC) AS "percentile_disc_2",
|
PERCENTILE_DISC ("Invoice"."Total" / $2) WITHIN GROUP (ORDER BY "Invoice"."InvoiceDate" ASC) AS "percentile_disc_2",
|
||||||
PERCENTILE_DISC ((select array_agg(s) from generate_series(0, 1, 0.2) as s)) WITHIN GROUP (ORDER BY "Invoice"."BillingAddress" DESC) AS "percentile_disc_3",
|
PERCENTILE_DISC ((select array_agg(s) from generate_series(0, 1, 0.2) as s)) WITHIN GROUP (ORDER BY "Invoice"."BillingAddress" DESC) AS "percentile_disc_3",
|
||||||
PERCENTILE_CONT ($3::double precision) WITHIN GROUP (ORDER BY "Invoice"."Total") AS "percentile_cont_1",
|
PERCENTILE_CONT ($3::double precision) WITHIN GROUP (ORDER BY "Invoice"."Total") AS "percentile_cont_1",
|
||||||
PERCENTILE_CONT ($4::double precision) WITHIN GROUP (ORDER BY INTERVAL '1 HOUR' DESC) AS "percentile_cont_int",
|
PERCENTILE_CONT ($4::double precision) WITHIN GROUP (ORDER BY INTERVAL '1 HOUR' DESC) AS "percentile_cont_interval",
|
||||||
MODE () WITHIN GROUP (ORDER BY "Invoice"."BillingPostalCode" DESC) AS "mode_1"
|
MODE () WITHIN GROUP (ORDER BY "Invoice"."BillingPostalCode" DESC) AS "mode_1"
|
||||||
FROM chinook."Invoice"
|
FROM chinook."Invoice"
|
||||||
GROUP BY "Invoice"."Total";
|
GROUP BY "Invoice"."Total";
|
||||||
`, 0.1, 100.0, 0.3, 0.2)
|
`, 0.1, 100.0, 0.3, 0.2)
|
||||||
|
|
||||||
var dest struct {
|
var dest struct {
|
||||||
PercentileDisc1 string
|
PercentileDisc1 string
|
||||||
PercentileDisc2 string
|
PercentileDisc2 string
|
||||||
PercentileDisc3 string
|
PercentileDisc3 string
|
||||||
PercentileCont1 string
|
PercentileCont1 string
|
||||||
Mode1 string
|
PercentileContInterval string
|
||||||
|
Mode1 string
|
||||||
}
|
}
|
||||||
|
|
||||||
err := stmt.Query(db, &dest)
|
err := stmt.Query(db, &dest)
|
||||||
|
|
@ -1332,6 +1330,7 @@ GROUP BY "Invoice"."Total";
|
||||||
"PercentileDisc2": "2009-01-19T00:00:00Z",
|
"PercentileDisc2": "2009-01-19T00:00:00Z",
|
||||||
"PercentileDisc3": "{\"Via Degli Scipioni, 43\",\"Qe 7 Bloco G\",\"Berger Stra<72>e 10\",\"696 Osborne Street\",\"2211 W Berry Street\",\"1033 N Park Ave\"}",
|
"PercentileDisc3": "{\"Via Degli Scipioni, 43\",\"Qe 7 Bloco G\",\"Berger Stra<72>e 10\",\"696 Osborne Street\",\"2211 W Berry Street\",\"1033 N Park Ave\"}",
|
||||||
"PercentileCont1": "0.99",
|
"PercentileCont1": "0.99",
|
||||||
|
"PercentileContInterval": "01:00:00",
|
||||||
"Mode1": "X1A 1N6"
|
"Mode1": "X1A 1N6"
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/go-jet/jet/v2/qrm"
|
||||||
"github.com/go-jet/jet/v2/stmtcache"
|
"github.com/go-jet/jet/v2/stmtcache"
|
||||||
"github.com/go-jet/jet/v2/tests/internal/utils/repo"
|
"github.com/go-jet/jet/v2/tests/internal/utils/repo"
|
||||||
"github.com/jackc/pgx/v4/stdlib"
|
"github.com/jackc/pgx/v4/stdlib"
|
||||||
|
|
@ -49,6 +50,8 @@ func skipForCockroachDB(t *testing.T) {
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
defer profile.Start().Stop()
|
defer profile.Start().Stop()
|
||||||
|
|
||||||
|
qrm.GlobalConfig.StrictScan = true
|
||||||
|
|
||||||
for _, driverName := range []string{"postgres", "pgx"} {
|
for _, driverName := range []string{"postgres", "pgx"} {
|
||||||
|
|
||||||
fmt.Printf("\nRunning postgres tests for driver: %s, caching enabled: %t \n", driverName, withStatementCaching)
|
fmt.Printf("\nRunning postgres tests for driver: %s, caching enabled: %t \n", driverName, withStatementCaching)
|
||||||
|
|
@ -95,6 +98,16 @@ func getConnectionString() string {
|
||||||
return dbconfig.PostgresConnectString
|
return dbconfig.PostgresConnectString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func allowUnusedColumns(f func()) {
|
||||||
|
defer func() {
|
||||||
|
qrm.GlobalConfig.StrictScan = true
|
||||||
|
}()
|
||||||
|
|
||||||
|
qrm.GlobalConfig.StrictScan = false
|
||||||
|
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
|
||||||
var loggedSQL string
|
var loggedSQL string
|
||||||
var loggedSQLArgs []interface{}
|
var loggedSQLArgs []interface{}
|
||||||
var loggedDebugSQL string
|
var loggedDebugSQL string
|
||||||
|
|
|
||||||
|
|
@ -137,8 +137,10 @@ WHERE sample_ranges.date_range @> $36::date;
|
||||||
}
|
}
|
||||||
|
|
||||||
var dest sample
|
var dest sample
|
||||||
err := query.Query(db, &dest)
|
allowUnusedColumns(func() {
|
||||||
require.NoError(t, err)
|
err := query.Query(db, &dest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
expectedRow := sample{
|
expectedRow := sample{
|
||||||
SampleRanges: sampleRangeRow,
|
SampleRanges: sampleRangeRow,
|
||||||
|
|
@ -219,7 +221,7 @@ func TestRangeSelectColumnsFromSubQuery(t *testing.T) {
|
||||||
int4Range := Int4RangeColumn("range4").From(subQuery)
|
int4Range := Int4RangeColumn("range4").From(subQuery)
|
||||||
|
|
||||||
stmt := SELECT(
|
stmt := SELECT(
|
||||||
subQuery.AllColumns(),
|
subQuery.AllColumns().Except(int4Range),
|
||||||
int4Range,
|
int4Range,
|
||||||
).FROM(
|
).FROM(
|
||||||
subQuery,
|
subQuery,
|
||||||
|
|
@ -232,7 +234,6 @@ SELECT sub_query."sample_ranges.date_range" AS "sample_ranges.date_range",
|
||||||
sub_query."sample_ranges.int4_range" AS "sample_ranges.int4_range",
|
sub_query."sample_ranges.int4_range" AS "sample_ranges.int4_range",
|
||||||
sub_query."sample_ranges.int8_range" AS "sample_ranges.int8_range",
|
sub_query."sample_ranges.int8_range" AS "sample_ranges.int8_range",
|
||||||
sub_query."sample_ranges.num_range" AS "sample_ranges.num_range",
|
sub_query."sample_ranges.num_range" AS "sample_ranges.num_range",
|
||||||
sub_query.range4 AS "range4",
|
|
||||||
sub_query.range4 AS "range4"
|
sub_query.range4 AS "range4"
|
||||||
FROM (
|
FROM (
|
||||||
SELECT sample_ranges.date_range AS "sample_ranges.date_range",
|
SELECT sample_ranges.date_range AS "sample_ranges.date_range",
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,18 @@ func TestScanToInvalidDestination(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestScanToValidDestination(t *testing.T) {
|
func TestScanToValidDestination(t *testing.T) {
|
||||||
|
|
||||||
|
t.Run("pointer to empty struct - non strict scan", func(t *testing.T) {
|
||||||
|
allowUnusedColumns(func() {
|
||||||
|
var dest []struct{}
|
||||||
|
err := oneInventoryQuery.Query(db, &dest)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("pointer to struct", func(t *testing.T) {
|
t.Run("pointer to struct", func(t *testing.T) {
|
||||||
dest := []struct{}{}
|
var dest model.Inventory
|
||||||
err := oneInventoryQuery.Query(db, &dest)
|
err := oneInventoryQuery.Query(db, &dest)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
@ -63,20 +73,24 @@ func TestScanToValidDestination(t *testing.T) {
|
||||||
|
|
||||||
t.Run("global query function scan", func(t *testing.T) {
|
t.Run("global query function scan", func(t *testing.T) {
|
||||||
queryStr, args := oneInventoryQuery.Sql()
|
queryStr, args := oneInventoryQuery.Sql()
|
||||||
dest := []struct{}{}
|
var dest model.Inventory
|
||||||
rowProcessed, err := qrm.Query(nil, db, queryStr, args, &dest)
|
rowProcessed, err := qrm.Query(nil, db, queryStr, args, &dest)
|
||||||
require.Equal(t, rowProcessed, int64(1))
|
require.Equal(t, rowProcessed, int64(1))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("pointer to slice", func(t *testing.T) {
|
t.Run("pointer to slice", func(t *testing.T) {
|
||||||
err := oneInventoryQuery.Query(db, &[]struct{}{})
|
var dest []model.Inventory
|
||||||
|
|
||||||
|
err := oneInventoryQuery.Query(db, &dest)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("pointer to slice of pointer to structs", func(t *testing.T) {
|
t.Run("pointer to slice of pointer to structs", func(t *testing.T) {
|
||||||
err := oneInventoryQuery.Query(db, &[]*struct{}{})
|
var dest []*model.Inventory
|
||||||
|
|
||||||
|
err := oneInventoryQuery.Query(db, &dest)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
|
|
@ -84,7 +98,7 @@ func TestScanToValidDestination(t *testing.T) {
|
||||||
t.Run("pointer to slice of integers", func(t *testing.T) {
|
t.Run("pointer to slice of integers", func(t *testing.T) {
|
||||||
var dest []int32
|
var dest []int32
|
||||||
|
|
||||||
err := oneInventoryQuery.Query(db, &dest)
|
err := Inventory.SELECT(Inventory.InventoryID).Query(db, &dest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, dest[0], int32(1))
|
require.Equal(t, dest[0], int32(1))
|
||||||
})
|
})
|
||||||
|
|
@ -92,7 +106,7 @@ func TestScanToValidDestination(t *testing.T) {
|
||||||
t.Run("pointer to slice integer pointers", func(t *testing.T) {
|
t.Run("pointer to slice integer pointers", func(t *testing.T) {
|
||||||
var dest []*int32
|
var dest []*int32
|
||||||
|
|
||||||
err := oneInventoryQuery.Query(db, &dest)
|
err := Inventory.SELECT(Inventory.InventoryID).Query(db, &dest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, dest[0], ptr.Of(int32(1)))
|
require.Equal(t, dest[0], ptr.Of(int32(1)))
|
||||||
})
|
})
|
||||||
|
|
@ -114,8 +128,6 @@ func TestScanToStruct(t *testing.T) {
|
||||||
SELECT(Inventory.AllColumns).
|
SELECT(Inventory.AllColumns).
|
||||||
ORDER_BY(Inventory.InventoryID)
|
ORDER_BY(Inventory.InventoryID)
|
||||||
|
|
||||||
//fmt.Println(query.DebugSql())
|
|
||||||
|
|
||||||
t.Run("one struct", func(t *testing.T) {
|
t.Run("one struct", func(t *testing.T) {
|
||||||
dest := model.Inventory{}
|
dest := model.Inventory{}
|
||||||
err := query.LIMIT(1).Query(db, &dest)
|
err := query.LIMIT(1).Query(db, &dest)
|
||||||
|
|
@ -173,6 +185,7 @@ func TestScanToStruct(t *testing.T) {
|
||||||
InventoryID *int32 `sql:"primary_key"`
|
InventoryID *int32 `sql:"primary_key"`
|
||||||
FilmID int16
|
FilmID int16
|
||||||
StoreID *int16
|
StoreID *int16
|
||||||
|
LastUpdate time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
dest := Inventory{}
|
dest := Inventory{}
|
||||||
|
|
@ -184,12 +197,15 @@ func TestScanToStruct(t *testing.T) {
|
||||||
require.Equal(t, *dest.InventoryID, int32(1))
|
require.Equal(t, *dest.InventoryID, int32(1))
|
||||||
require.Equal(t, dest.FilmID, int16(1))
|
require.Equal(t, dest.FilmID, int16(1))
|
||||||
require.Equal(t, *dest.StoreID, int16(1))
|
require.Equal(t, *dest.StoreID, int16(1))
|
||||||
|
require.NotEmpty(t, dest.LastUpdate)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("type convert int32 to int", func(t *testing.T) {
|
t.Run("type convert int32 to int", func(t *testing.T) {
|
||||||
type Inventory struct {
|
type Inventory struct {
|
||||||
InventoryID int
|
InventoryID *int32 `sql:"primary_key"`
|
||||||
FilmID string
|
FilmID int16
|
||||||
|
StoreID *int16
|
||||||
|
LastUpdate time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
dest := Inventory{}
|
dest := Inventory{}
|
||||||
|
|
@ -267,6 +283,8 @@ func TestScanToNestedStruct(t *testing.T) {
|
||||||
dest := struct {
|
dest := struct {
|
||||||
model.Inventory
|
model.Inventory
|
||||||
model.Actor //unused
|
model.Actor //unused
|
||||||
|
model.Film
|
||||||
|
model.Store
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
err := query.Query(db, &dest)
|
err := query.Query(db, &dest)
|
||||||
|
|
@ -280,6 +298,8 @@ func TestScanToNestedStruct(t *testing.T) {
|
||||||
dest := struct {
|
dest := struct {
|
||||||
model.Inventory
|
model.Inventory
|
||||||
*model.Actor //unused
|
*model.Actor //unused
|
||||||
|
model.Film
|
||||||
|
model.Store
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
err := query.Query(db, &dest)
|
err := query.Query(db, &dest)
|
||||||
|
|
@ -293,6 +313,8 @@ func TestScanToNestedStruct(t *testing.T) {
|
||||||
dest := struct {
|
dest := struct {
|
||||||
model.Inventory
|
model.Inventory
|
||||||
Actor *model.Actor //unused
|
Actor *model.Actor //unused
|
||||||
|
model.Film
|
||||||
|
model.Store
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
err := query.Query(db, &dest)
|
err := query.Query(db, &dest)
|
||||||
|
|
@ -312,6 +334,8 @@ func TestScanToNestedStruct(t *testing.T) {
|
||||||
dest := struct {
|
dest := struct {
|
||||||
model.Inventory
|
model.Inventory
|
||||||
Actor *model.Actor //unused
|
Actor *model.Actor //unused
|
||||||
|
model.Film
|
||||||
|
model.Store
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
err := query.Query(db, &dest)
|
err := query.Query(db, &dest)
|
||||||
|
|
@ -327,6 +351,8 @@ func TestScanToNestedStruct(t *testing.T) {
|
||||||
Actor *struct {
|
Actor *struct {
|
||||||
model.Actor
|
model.Actor
|
||||||
} //unused
|
} //unused
|
||||||
|
model.Film
|
||||||
|
model.Store
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
err := query.Query(db, &dest)
|
err := query.Query(db, &dest)
|
||||||
|
|
@ -343,6 +369,8 @@ func TestScanToNestedStruct(t *testing.T) {
|
||||||
model.Actor //unused
|
model.Actor //unused
|
||||||
model.Language //unesed
|
model.Language //unesed
|
||||||
}
|
}
|
||||||
|
model.Film
|
||||||
|
model.Store
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
err := query.Query(db, &dest)
|
err := query.Query(db, &dest)
|
||||||
|
|
@ -362,6 +390,7 @@ func TestScanToNestedStruct(t *testing.T) {
|
||||||
model.Actor //unselected
|
model.Actor //unselected
|
||||||
model.Film //selected
|
model.Film //selected
|
||||||
}
|
}
|
||||||
|
model.Store
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
err := query.Query(db, &dest)
|
err := query.Query(db, &dest)
|
||||||
|
|
@ -382,6 +411,7 @@ func TestScanToNestedStruct(t *testing.T) {
|
||||||
*model.Film //selected
|
*model.Film //selected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
model.Store
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
err := query.Query(db, &dest)
|
err := query.Query(db, &dest)
|
||||||
|
|
@ -443,8 +473,13 @@ func TestScanToSlice(t *testing.T) {
|
||||||
ORDER_BY(Inventory.InventoryID).
|
ORDER_BY(Inventory.InventoryID).
|
||||||
LIMIT(10)
|
LIMIT(10)
|
||||||
|
|
||||||
|
justIDs := Inventory.
|
||||||
|
SELECT(Inventory.InventoryID).
|
||||||
|
ORDER_BY(Inventory.InventoryID).
|
||||||
|
LIMIT(10)
|
||||||
|
|
||||||
t.Run("slice od inventory", func(t *testing.T) {
|
t.Run("slice od inventory", func(t *testing.T) {
|
||||||
dest := []model.Inventory{}
|
var dest []model.Inventory
|
||||||
|
|
||||||
err := query.Query(db, &dest)
|
err := query.Query(db, &dest)
|
||||||
|
|
||||||
|
|
@ -454,95 +489,111 @@ func TestScanToSlice(t *testing.T) {
|
||||||
testutils.AssertDeepEqual(t, dest[1], inventory2)
|
testutils.AssertDeepEqual(t, dest[1], inventory2)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("slice of ints", func(t *testing.T) {
|
t.Run("slice of int32 non strict scan", func(t *testing.T) {
|
||||||
|
allowUnusedColumns(func() {
|
||||||
|
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 of int32 strict scan", func(t *testing.T) {
|
||||||
var dest []int32
|
var dest []int32
|
||||||
|
|
||||||
err := query.Query(db, &dest)
|
err := justIDs.Query(db, &dest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
testutils.AssertDeepEqual(t, dest, []int32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
|
testutils.AssertDeepEqual(t, dest, []int32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("slice type convertible", func(t *testing.T) {
|
t.Run("slice type convertible", func(t *testing.T) {
|
||||||
var dest []int
|
var dest []int
|
||||||
|
|
||||||
err := query.Query(db, &dest)
|
err := justIDs.Query(db, &dest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("slice type mismatch", func(t *testing.T) {
|
t.Run("slice type mismatch", func(t *testing.T) {
|
||||||
var dest []bool
|
var dest []bool
|
||||||
|
|
||||||
err := query.Query(db, &dest)
|
err := justIDs.Query(db, &dest)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.EqualError(t, err, `jet: can't append int64 to []bool slice: can't assign int64(2) to bool`)
|
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) {
|
t.Run("slice of complex structs", func(t *testing.T) {
|
||||||
query := Inventory.
|
query := SELECT(
|
||||||
INNER_JOIN(Film, Inventory.FilmID.EQ(Film.FilmID)).
|
Inventory.AllColumns,
|
||||||
INNER_JOIN(Store, Inventory.StoreID.EQ(Store.StoreID)).
|
Film.AllColumns,
|
||||||
SELECT(
|
Store.AllColumns,
|
||||||
Inventory.AllColumns,
|
).FROM(
|
||||||
Film.AllColumns,
|
Inventory.
|
||||||
Store.AllColumns,
|
INNER_JOIN(Film, Inventory.FilmID.EQ(Film.FilmID)).
|
||||||
).
|
INNER_JOIN(Store, Inventory.StoreID.EQ(Store.StoreID)),
|
||||||
ORDER_BY(Inventory.InventoryID).
|
).ORDER_BY(
|
||||||
LIMIT(10)
|
Inventory.InventoryID,
|
||||||
|
).LIMIT(10)
|
||||||
|
|
||||||
t.Run("struct with slice of ints", func(t *testing.T) {
|
t.Run("struct with slice of ints - non strict scan", func(t *testing.T) {
|
||||||
var dest struct {
|
allowUnusedColumns(func() {
|
||||||
model.Film
|
var dest struct {
|
||||||
IDs []int32 `alias:"inventory.inventory_id"`
|
model.Film
|
||||||
}
|
IDs []int32 `alias:"inventory.inventory_id"`
|
||||||
|
}
|
||||||
|
|
||||||
err := query.Query(db, &dest)
|
err := query.Query(db, &dest)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
testutils.AssertDeepEqual(t, dest.Film, film1)
|
testutils.AssertDeepEqual(t, dest.Film, film1)
|
||||||
testutils.AssertDeepEqual(t, dest.IDs, []int32{1, 2, 3, 4, 5, 6, 7, 8})
|
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) {
|
t.Run("slice of structs with slice of ints", func(t *testing.T) {
|
||||||
var dest []struct {
|
allowUnusedColumns(func() {
|
||||||
model.Film
|
var dest []struct {
|
||||||
IDs []int32 `alias:"inventory.inventory_id"`
|
model.Film
|
||||||
}
|
IDs []int32 `alias:"inventory.inventory_id"`
|
||||||
|
}
|
||||||
|
|
||||||
err := query.Query(db, &dest)
|
err := query.Query(db, &dest)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, len(dest), 2)
|
require.Equal(t, len(dest), 2)
|
||||||
testutils.AssertDeepEqual(t, dest[0].Film, film1)
|
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[0].IDs, []int32{1, 2, 3, 4, 5, 6, 7, 8})
|
||||||
testutils.AssertDeepEqual(t, dest[1].Film, film2)
|
testutils.AssertDeepEqual(t, dest[1].Film, film2)
|
||||||
testutils.AssertDeepEqual(t, dest[1].IDs, []int32{9, 10})
|
testutils.AssertDeepEqual(t, dest[1].IDs, []int32{9, 10})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("slice of structs with slice of pointer to ints", func(t *testing.T) {
|
t.Run("slice of structs with slice of pointer to ints", func(t *testing.T) {
|
||||||
var dest []struct {
|
allowUnusedColumns(func() {
|
||||||
model.Film
|
var dest []struct {
|
||||||
IDs []*int32 `alias:"inventory.inventory_id"`
|
model.Film
|
||||||
}
|
IDs []*int32 `alias:"inventory.inventory_id"`
|
||||||
|
}
|
||||||
|
|
||||||
err := query.Query(db, &dest)
|
err := query.Query(db, &dest)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, len(dest), 2)
|
require.Equal(t, len(dest), 2)
|
||||||
testutils.AssertDeepEqual(t, dest[0].Film, film1)
|
testutils.AssertDeepEqual(t, dest[0].Film, film1)
|
||||||
testutils.AssertDeepEqual(t, dest[0].IDs, []*int32{ptr.Of(int32(1)), ptr.Of(int32(2)), ptr.Of(int32(3)), ptr.Of(int32(4)),
|
testutils.AssertDeepEqual(t, dest[0].IDs, []*int32{ptr.Of(int32(1)), ptr.Of(int32(2)), ptr.Of(int32(3)), ptr.Of(int32(4)),
|
||||||
ptr.Of(int32(5)), ptr.Of(int32(6)), ptr.Of(int32(7)), ptr.Of(int32(8))})
|
ptr.Of(int32(5)), ptr.Of(int32(6)), ptr.Of(int32(7)), ptr.Of(int32(8))})
|
||||||
testutils.AssertDeepEqual(t, dest[1].Film, film2)
|
testutils.AssertDeepEqual(t, dest[1].Film, film2)
|
||||||
testutils.AssertDeepEqual(t, dest[1].IDs, []*int32{ptr.Of(int32(9)), ptr.Of(int32(10))})
|
testutils.AssertDeepEqual(t, dest[1].IDs, []*int32{ptr.Of(int32(9)), ptr.Of(int32(10))})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("complex struct 1", func(t *testing.T) {
|
t.Run("complex struct 1", func(t *testing.T) {
|
||||||
dest := []struct {
|
var dest []struct {
|
||||||
model.Inventory
|
model.Inventory
|
||||||
model.Film
|
model.Film
|
||||||
model.Store
|
model.Store
|
||||||
}{}
|
}
|
||||||
|
|
||||||
err := query.Query(db, &dest)
|
err := query.Query(db, &dest)
|
||||||
|
|
||||||
|
|
@ -619,6 +670,7 @@ func TestScanToSlice(t *testing.T) {
|
||||||
|
|
||||||
Inventories []struct {
|
Inventories []struct {
|
||||||
model.Inventory
|
model.Inventory
|
||||||
|
model.Store
|
||||||
|
|
||||||
Rentals *[]model.Rental
|
Rentals *[]model.Rental
|
||||||
Rentals2 []model.Rental
|
Rentals2 []model.Rental
|
||||||
|
|
@ -639,11 +691,11 @@ func TestScanToSlice(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("slice of complex structs 2", func(t *testing.T) {
|
t.Run("slice of complex structs 2", func(t *testing.T) {
|
||||||
query := Country.
|
query := SELECT(Country.AllColumns, City.AllColumns, Address.AllColumns, Customer.AllColumns).
|
||||||
INNER_JOIN(City, City.CountryID.EQ(Country.CountryID)).
|
FROM(Country.
|
||||||
INNER_JOIN(Address, Address.CityID.EQ(City.CityID)).
|
INNER_JOIN(City, City.CountryID.EQ(Country.CountryID)).
|
||||||
INNER_JOIN(Customer, Customer.AddressID.EQ(Address.AddressID)).
|
INNER_JOIN(Address, Address.CityID.EQ(City.CityID)).
|
||||||
SELECT(Country.AllColumns, City.AllColumns, Address.AllColumns, Customer.AllColumns).
|
INNER_JOIN(Customer, Customer.AddressID.EQ(Address.AddressID))).
|
||||||
ORDER_BY(Country.CountryID.ASC(), City.CityID.ASC(), Address.AddressID.ASC(), Customer.CustomerID.ASC()).
|
ORDER_BY(Country.CountryID.ASC(), City.CityID.ASC(), Address.AddressID.ASC(), Customer.CustomerID.ASC()).
|
||||||
LIMIT(1000)
|
LIMIT(1000)
|
||||||
|
|
||||||
|
|
@ -654,7 +706,7 @@ func TestScanToSlice(t *testing.T) {
|
||||||
Cities []struct {
|
Cities []struct {
|
||||||
model.City
|
model.City
|
||||||
|
|
||||||
Adresses []struct {
|
Addresses []struct {
|
||||||
model.Address
|
model.Address
|
||||||
|
|
||||||
Customer model.Customer
|
Customer model.Customer
|
||||||
|
|
@ -669,14 +721,14 @@ func TestScanToSlice(t *testing.T) {
|
||||||
testutils.AssertDeepEqual(t, dest[100].Country, countryUk)
|
testutils.AssertDeepEqual(t, dest[100].Country, countryUk)
|
||||||
require.Equal(t, len(dest[100].Cities), 8)
|
require.Equal(t, len(dest[100].Cities), 8)
|
||||||
testutils.AssertDeepEqual(t, dest[100].Cities[2].City, cityLondon)
|
testutils.AssertDeepEqual(t, dest[100].Cities[2].City, cityLondon)
|
||||||
require.Equal(t, len(dest[100].Cities[2].Adresses), 2)
|
require.Equal(t, len(dest[100].Cities[2].Addresses), 2)
|
||||||
testutils.AssertDeepEqual(t, dest[100].Cities[2].Adresses[0].Address, address256)
|
testutils.AssertDeepEqual(t, dest[100].Cities[2].Addresses[0].Address, address256)
|
||||||
testutils.AssertDeepEqual(t, dest[100].Cities[2].Adresses[0].Customer, customer256)
|
testutils.AssertDeepEqual(t, dest[100].Cities[2].Addresses[0].Customer, customer256)
|
||||||
testutils.AssertDeepEqual(t, dest[100].Cities[2].Adresses[1].Address, addres517)
|
testutils.AssertDeepEqual(t, dest[100].Cities[2].Addresses[1].Address, addres517)
|
||||||
testutils.AssertDeepEqual(t, dest[100].Cities[2].Adresses[1].Customer, customer512)
|
testutils.AssertDeepEqual(t, dest[100].Cities[2].Addresses[1].Customer, customer512)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("dest1", func(t *testing.T) {
|
t.Run("dest2", func(t *testing.T) {
|
||||||
var dest []*struct {
|
var dest []*struct {
|
||||||
*model.Country
|
*model.Country
|
||||||
|
|
||||||
|
|
@ -707,7 +759,7 @@ func TestScanToSlice(t *testing.T) {
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("dest1", func(t *testing.T) {
|
t.Run("dest3", func(t *testing.T) {
|
||||||
var dest []*struct {
|
var dest []*struct {
|
||||||
*model.Country
|
*model.Country
|
||||||
|
|
||||||
|
|
@ -1074,6 +1126,47 @@ VALUES (1234, 0, 'Joe', '', NULL, 1, TRUE, '2020-02-02 10:00:00Z', NULL, 1);
|
||||||
testutils.AssertExecAndRollback(t, stmt, db)
|
testutils.AssertExecAndRollback(t, stmt, db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStrictScan(t *testing.T) {
|
||||||
|
|
||||||
|
stmt := SELECT(
|
||||||
|
Actor.AllColumns,
|
||||||
|
).FROM(
|
||||||
|
Actor,
|
||||||
|
).LIMIT(10)
|
||||||
|
|
||||||
|
type Actor struct {
|
||||||
|
ActorID int32 `sql:"primary_key"`
|
||||||
|
FirstName string
|
||||||
|
//LastName string
|
||||||
|
//LastUpdate time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
var dest []Actor
|
||||||
|
|
||||||
|
require.PanicsWithValue(t, "jet: columns never used: 'actor.last_name', 'actor.last_update'", func() {
|
||||||
|
err := stmt.Query(db, &dest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
var dest2 []model.Actor
|
||||||
|
|
||||||
|
err := stmt.Query(db, &dest2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Run("using_rows", func(t *testing.T) {
|
||||||
|
rows, err := stmt.Rows(ctx, db)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.True(t, rows.Next())
|
||||||
|
|
||||||
|
require.PanicsWithValue(t, "jet: columns never used: 'actor.last_name', 'actor.last_update'", func() {
|
||||||
|
var actor Actor
|
||||||
|
err = rows.Scan(&actor)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
var address256 = model.Address{
|
var address256 = model.Address{
|
||||||
AddressID: 256,
|
AddressID: 256,
|
||||||
Address: "1497 Yuzhou Drive",
|
Address: "1497 Yuzhou Drive",
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,11 @@ LIMIT 30;
|
||||||
|
|
||||||
testutils.AssertDebugStatementSql(t, query, expectedSQL, int64(30))
|
testutils.AssertDebugStatementSql(t, query, expectedSQL, int64(30))
|
||||||
|
|
||||||
var dest []model.Payment
|
var dest []struct {
|
||||||
|
model.Payment
|
||||||
|
|
||||||
|
Customer model.Customer
|
||||||
|
}
|
||||||
|
|
||||||
err := query.Query(db, &dest)
|
err := query.Query(db, &dest)
|
||||||
|
|
||||||
|
|
@ -246,8 +250,10 @@ LIMIT 12;
|
||||||
testutils.AssertDebugStatementSql(t, query, expectedSQL, int64(1), int64(1), int64(10), int64(1), int64(2), int64(1), int64(12))
|
testutils.AssertDebugStatementSql(t, query, expectedSQL, int64(1), int64(1), int64(10), int64(1), int64(2), int64(1), int64(12))
|
||||||
|
|
||||||
var dest []struct{}
|
var dest []struct{}
|
||||||
err := query.Query(db, &dest)
|
allowUnusedColumns(func() {
|
||||||
require.NoError(t, err)
|
err := query.Query(db, &dest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectFetchFirst(t *testing.T) {
|
func TestSelectFetchFirst(t *testing.T) {
|
||||||
|
|
@ -369,10 +375,7 @@ OFFSET (
|
||||||
func TestSelectJoinQueryStruct(t *testing.T) {
|
func TestSelectJoinQueryStruct(t *testing.T) {
|
||||||
|
|
||||||
expectedSQL := `
|
expectedSQL := `
|
||||||
SELECT film_actor.actor_id AS "film_actor.actor_id",
|
SELECT film.film_id AS "film.film_id",
|
||||||
film_actor.film_id AS "film_actor.film_id",
|
|
||||||
film_actor.last_update AS "film_actor.last_update",
|
|
||||||
film.film_id AS "film.film_id",
|
|
||||||
film.title AS "film.title",
|
film.title AS "film.title",
|
||||||
film.description AS "film.description",
|
film.description AS "film.description",
|
||||||
film.release_year AS "film.release_year",
|
film.release_year AS "film.release_year",
|
||||||
|
|
@ -420,7 +423,6 @@ LIMIT 1000;
|
||||||
INNER_JOIN(Inventory, Inventory.FilmID.EQ(Film.FilmID)).
|
INNER_JOIN(Inventory, Inventory.FilmID.EQ(Film.FilmID)).
|
||||||
INNER_JOIN(Rental, Rental.InventoryID.EQ(Inventory.InventoryID)).
|
INNER_JOIN(Rental, Rental.InventoryID.EQ(Inventory.InventoryID)).
|
||||||
SELECT(
|
SELECT(
|
||||||
FilmActor.AllColumns,
|
|
||||||
Film.AllColumns,
|
Film.AllColumns,
|
||||||
Language.AllColumns,
|
Language.AllColumns,
|
||||||
Actor.AllColumns,
|
Actor.AllColumns,
|
||||||
|
|
@ -1235,6 +1237,8 @@ func TestSelectOrderByAscDesc(t *testing.T) {
|
||||||
|
|
||||||
func TestSelectOrderBy(t *testing.T) {
|
func TestSelectOrderBy(t *testing.T) {
|
||||||
|
|
||||||
|
var destRentals []model.Rental
|
||||||
|
|
||||||
t.Run("default", func(t *testing.T) {
|
t.Run("default", func(t *testing.T) {
|
||||||
stmt := SELECT(
|
stmt := SELECT(
|
||||||
Rental.AllColumns,
|
Rental.AllColumns,
|
||||||
|
|
@ -1256,7 +1260,8 @@ FROM dvds.rental
|
||||||
ORDER BY rental.return_date
|
ORDER BY rental.return_date
|
||||||
LIMIT 200;
|
LIMIT 200;
|
||||||
`)
|
`)
|
||||||
require.NoError(t, stmt.Query(db, &struct{}{}))
|
|
||||||
|
require.NoError(t, stmt.Query(db, &destRentals))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("NULLS FIRST", func(t *testing.T) {
|
t.Run("NULLS FIRST", func(t *testing.T) {
|
||||||
|
|
@ -1280,7 +1285,7 @@ FROM dvds.rental
|
||||||
ORDER BY rental.return_date NULLS FIRST
|
ORDER BY rental.return_date NULLS FIRST
|
||||||
LIMIT 200;
|
LIMIT 200;
|
||||||
`)
|
`)
|
||||||
require.NoError(t, stmt.Query(db, &struct{}{}))
|
require.NoError(t, stmt.Query(db, &destRentals))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("NULLS LAST", func(t *testing.T) {
|
t.Run("NULLS LAST", func(t *testing.T) {
|
||||||
|
|
@ -1304,7 +1309,7 @@ FROM dvds.rental
|
||||||
ORDER BY rental.return_date NULLS LAST
|
ORDER BY rental.return_date NULLS LAST
|
||||||
LIMIT 200;
|
LIMIT 200;
|
||||||
`)
|
`)
|
||||||
require.NoError(t, stmt.Query(db, &struct{}{}))
|
require.NoError(t, stmt.Query(db, &destRentals))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("ASC", func(t *testing.T) {
|
t.Run("ASC", func(t *testing.T) {
|
||||||
|
|
@ -1328,7 +1333,7 @@ FROM dvds.rental
|
||||||
ORDER BY rental.return_date ASC
|
ORDER BY rental.return_date ASC
|
||||||
LIMIT 200;
|
LIMIT 200;
|
||||||
`)
|
`)
|
||||||
require.NoError(t, stmt.Query(db, &struct{}{}))
|
require.NoError(t, stmt.Query(db, &destRentals))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("ASC NULLS FIRST", func(t *testing.T) {
|
t.Run("ASC NULLS FIRST", func(t *testing.T) {
|
||||||
|
|
@ -1353,7 +1358,7 @@ ORDER BY rental.return_date ASC NULLS FIRST
|
||||||
LIMIT 200;
|
LIMIT 200;
|
||||||
`)
|
`)
|
||||||
|
|
||||||
require.NoError(t, stmt.Query(db, &struct{}{}))
|
require.NoError(t, stmt.Query(db, &destRentals))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("ASC NULLS LAST", func(t *testing.T) {
|
t.Run("ASC NULLS LAST", func(t *testing.T) {
|
||||||
|
|
@ -1379,7 +1384,7 @@ LIMIT 200
|
||||||
OFFSET 15800;
|
OFFSET 15800;
|
||||||
`)
|
`)
|
||||||
|
|
||||||
require.NoError(t, stmt.Query(db, &struct{}{}))
|
require.NoError(t, stmt.Query(db, &destRentals))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("DESC", func(t *testing.T) {
|
t.Run("DESC", func(t *testing.T) {
|
||||||
|
|
@ -1405,7 +1410,7 @@ LIMIT 200
|
||||||
OFFSET 15800;
|
OFFSET 15800;
|
||||||
`)
|
`)
|
||||||
|
|
||||||
require.NoError(t, stmt.Query(db, &struct{}{}))
|
require.NoError(t, stmt.Query(db, &destRentals))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("DESC NULLS LAST", func(t *testing.T) {
|
t.Run("DESC NULLS LAST", func(t *testing.T) {
|
||||||
|
|
@ -1431,7 +1436,7 @@ LIMIT 200
|
||||||
OFFSET 15800;
|
OFFSET 15800;
|
||||||
`)
|
`)
|
||||||
|
|
||||||
require.NoError(t, stmt.Query(db, &struct{}{}))
|
require.NoError(t, stmt.Query(db, &destRentals))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("DESC NULLS FIRST", func(t *testing.T) {
|
t.Run("DESC NULLS FIRST", func(t *testing.T) {
|
||||||
|
|
@ -1456,7 +1461,7 @@ ORDER BY rental.return_date DESC NULLS FIRST
|
||||||
LIMIT 200;
|
LIMIT 200;
|
||||||
`)
|
`)
|
||||||
|
|
||||||
require.NoError(t, stmt.Query(db, &struct{}{}))
|
require.NoError(t, stmt.Query(db, &destRentals))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1552,14 +1557,14 @@ LIMIT 1000;
|
||||||
|
|
||||||
testutils.AssertDebugStatementSql(t, query, expectedSQL, int64(1000))
|
testutils.AssertDebugStatementSql(t, query, expectedSQL, int64(1000))
|
||||||
|
|
||||||
var customerAddresCrosJoined []struct {
|
var customerAddersCrossJoined []struct {
|
||||||
model.Customer
|
model.Customer
|
||||||
model.Address
|
model.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
err := query.Query(db, &customerAddresCrosJoined)
|
err := query.Query(db, &customerAddersCrossJoined)
|
||||||
|
|
||||||
require.Equal(t, len(customerAddresCrosJoined), 1000)
|
require.Equal(t, len(customerAddersCrossJoined), 1000)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
@ -1682,7 +1687,6 @@ func TestSelectSubQuery(t *testing.T) {
|
||||||
SELECT(
|
SELECT(
|
||||||
rRatingFilms.AllColumns(),
|
rRatingFilms.AllColumns(),
|
||||||
Actor.AllColumns,
|
Actor.AllColumns,
|
||||||
FilmActor.AllColumns,
|
|
||||||
).FROM(
|
).FROM(
|
||||||
rRatingFilms.
|
rRatingFilms.
|
||||||
INNER_JOIN(FilmActor, FilmActor.FilmID.EQ(rFilmID)).
|
INNER_JOIN(FilmActor, FilmActor.FilmID.EQ(rFilmID)).
|
||||||
|
|
@ -1701,10 +1705,7 @@ SELECT "rFilms"."film.film_id" AS "film.film_id",
|
||||||
actor.actor_id AS "actor.actor_id",
|
actor.actor_id AS "actor.actor_id",
|
||||||
actor.first_name AS "actor.first_name",
|
actor.first_name AS "actor.first_name",
|
||||||
actor.last_name AS "actor.last_name",
|
actor.last_name AS "actor.last_name",
|
||||||
actor.last_update AS "actor.last_update",
|
actor.last_update AS "actor.last_update"
|
||||||
film_actor.actor_id AS "film_actor.actor_id",
|
|
||||||
film_actor.film_id AS "film_actor.film_id",
|
|
||||||
film_actor.last_update AS "film_actor.last_update"
|
|
||||||
FROM (
|
FROM (
|
||||||
SELECT film.film_id AS "film.film_id",
|
SELECT film.film_id AS "film.film_id",
|
||||||
film.title AS "film.title",
|
film.title AS "film.title",
|
||||||
|
|
@ -1871,9 +1872,7 @@ SELECT customer.customer_id AS "customer.customer_id",
|
||||||
customer.active AS "customer.active",
|
customer.active AS "customer.active",
|
||||||
SUM(payment.amount) AS "amount.sum",
|
SUM(payment.amount) AS "amount.sum",
|
||||||
AVG(payment.amount) AS "amount.avg",
|
AVG(payment.amount) AS "amount.avg",
|
||||||
MAX(payment.payment_date) AS "amount.max_date",
|
|
||||||
MAX(payment.amount) AS "amount.max",
|
MAX(payment.amount) AS "amount.max",
|
||||||
MIN(payment.payment_date) AS "amount.min_date",
|
|
||||||
MIN(payment.amount) AS "amount.min",
|
MIN(payment.amount) AS "amount.min",
|
||||||
COUNT(payment.amount) AS "amount.count"
|
COUNT(payment.amount) AS "amount.count"
|
||||||
FROM dvds.payment
|
FROM dvds.payment
|
||||||
|
|
@ -1887,9 +1886,7 @@ ORDER BY customer.customer_id, SUM(payment.amount) ASC;
|
||||||
|
|
||||||
SUMf(Payment.Amount).AS("amount.sum"),
|
SUMf(Payment.Amount).AS("amount.sum"),
|
||||||
AVG(Payment.Amount).AS("amount.avg"),
|
AVG(Payment.Amount).AS("amount.avg"),
|
||||||
MAX(Payment.PaymentDate).AS("amount.max_date"),
|
|
||||||
MAXf(Payment.Amount).AS("amount.max"),
|
MAXf(Payment.Amount).AS("amount.max"),
|
||||||
MIN(Payment.PaymentDate).AS("amount.min_date"),
|
|
||||||
MINf(Payment.Amount).AS("amount.min"),
|
MINf(Payment.Amount).AS("amount.min"),
|
||||||
COUNT(Payment.Amount).AS("amount.count"),
|
COUNT(Payment.Amount).AS("amount.count"),
|
||||||
).FROM(
|
).FROM(
|
||||||
|
|
@ -2795,8 +2792,10 @@ ORDER BY actor.actor_id ASC, film.film_id ASC;
|
||||||
Actors []model.Actor
|
Actors []model.Actor
|
||||||
}
|
}
|
||||||
|
|
||||||
err = stmt.Query(db, &dest2)
|
allowUnusedColumns(func() {
|
||||||
require.NoError(t, err)
|
err = stmt.Query(db, &dest2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
//testutils.SaveJSONFile(dest2, "./testdata/results/postgres/quick-start-dest2.json")
|
//testutils.SaveJSONFile(dest2, "./testdata/results/postgres/quick-start-dest2.json")
|
||||||
testutils.AssertJSONFile(t, dest2, "./testdata/results/postgres/quick-start-dest2.json")
|
testutils.AssertJSONFile(t, dest2, "./testdata/results/postgres/quick-start-dest2.json")
|
||||||
|
|
@ -2819,18 +2818,19 @@ func TestSelectQuickStartWithSubQueries(t *testing.T) {
|
||||||
|
|
||||||
categoryID := Category.CategoryID.From(categoriesNotAction)
|
categoryID := Category.CategoryID.From(categoriesNotAction)
|
||||||
|
|
||||||
stmt := Actor.
|
stmt := SELECT(
|
||||||
INNER_JOIN(FilmActor, Actor.ActorID.EQ(FilmActor.ActorID)).
|
Actor.AllColumns,
|
||||||
INNER_JOIN(filmLogerThan180, filmID.EQ(FilmActor.FilmID)).
|
filmLogerThan180.AllColumns(),
|
||||||
INNER_JOIN(Language, Language.LanguageID.EQ(filmLanguageID)).
|
Language.AllColumns,
|
||||||
INNER_JOIN(FilmCategory, FilmCategory.FilmID.EQ(filmID)).
|
categoriesNotAction.AllColumns(),
|
||||||
INNER_JOIN(categoriesNotAction, categoryID.EQ(FilmCategory.CategoryID)).
|
).FROM(
|
||||||
SELECT(
|
Actor.
|
||||||
Actor.AllColumns,
|
INNER_JOIN(FilmActor, Actor.ActorID.EQ(FilmActor.ActorID)).
|
||||||
filmLogerThan180.AllColumns(),
|
INNER_JOIN(filmLogerThan180, filmID.EQ(FilmActor.FilmID)).
|
||||||
Language.AllColumns,
|
INNER_JOIN(Language, Language.LanguageID.EQ(filmLanguageID)).
|
||||||
categoriesNotAction.AllColumns(),
|
INNER_JOIN(FilmCategory, FilmCategory.FilmID.EQ(filmID)).
|
||||||
).ORDER_BY(
|
INNER_JOIN(categoriesNotAction, categoryID.EQ(FilmCategory.CategoryID)),
|
||||||
|
).ORDER_BY(
|
||||||
Actor.ActorID.ASC(),
|
Actor.ActorID.ASC(),
|
||||||
filmID.ASC(),
|
filmID.ASC(),
|
||||||
)
|
)
|
||||||
|
|
@ -2860,8 +2860,10 @@ func TestSelectQuickStartWithSubQueries(t *testing.T) {
|
||||||
Actors []model.Actor
|
Actors []model.Actor
|
||||||
}
|
}
|
||||||
|
|
||||||
err = stmt.Query(db, &dest2)
|
allowUnusedColumns(func() {
|
||||||
require.NoError(t, err)
|
err = stmt.Query(db, &dest2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
//jsonSave("./testdata/quick-start-dest2.json", dest2)
|
//jsonSave("./testdata/quick-start-dest2.json", dest2)
|
||||||
testutils.AssertJSONFile(t, dest2, "./testdata/results/postgres/quick-start-dest2.json")
|
testutils.AssertJSONFile(t, dest2, "./testdata/results/postgres/quick-start-dest2.json")
|
||||||
|
|
@ -2892,9 +2894,10 @@ SELECT true,
|
||||||
'date';
|
'date';
|
||||||
`)
|
`)
|
||||||
|
|
||||||
dest := []struct{}{}
|
allowUnusedColumns(func() {
|
||||||
err := query.Query(db, &dest)
|
err := query.Query(db, &[]struct{}{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectWindowFunction(t *testing.T) {
|
func TestSelectWindowFunction(t *testing.T) {
|
||||||
|
|
@ -2928,45 +2931,52 @@ FROM dvds.payment
|
||||||
WHERE payment.payment_id < $3
|
WHERE payment.payment_id < $3
|
||||||
GROUP BY payment.amount, payment.customer_id, payment.payment_date;
|
GROUP BY payment.amount, payment.customer_id, payment.payment_date;
|
||||||
`
|
`
|
||||||
query := Payment.
|
query := SELECT(
|
||||||
SELECT(
|
AVG(Payment.Amount).OVER(),
|
||||||
AVG(Payment.Amount).OVER(),
|
AVG(Payment.Amount).OVER(PARTITION_BY(Payment.CustomerID)),
|
||||||
AVG(Payment.Amount).OVER(PARTITION_BY(Payment.CustomerID)),
|
MAXf(Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate.DESC())),
|
||||||
MAXf(Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate.DESC())),
|
MINf(Payment.Amount).OVER(PARTITION_BY(Payment.CustomerID).ORDER_BY(Payment.PaymentDate.DESC())),
|
||||||
MINf(Payment.Amount).OVER(PARTITION_BY(Payment.CustomerID).ORDER_BY(Payment.PaymentDate.DESC())),
|
SUMf(Payment.Amount).OVER(PARTITION_BY(Payment.CustomerID).
|
||||||
SUMf(Payment.Amount).OVER(PARTITION_BY(Payment.CustomerID).
|
ORDER_BY(Payment.PaymentDate.DESC()).ROWS(PRECEDING(1), FOLLOWING(6))),
|
||||||
ORDER_BY(Payment.PaymentDate.DESC()).ROWS(PRECEDING(1), FOLLOWING(6))),
|
SUMf(Payment.Amount).OVER(PARTITION_BY(Payment.CustomerID).
|
||||||
SUMf(Payment.Amount).OVER(PARTITION_BY(Payment.CustomerID).
|
ORDER_BY(Payment.PaymentDate.DESC()).RANGE(PRECEDING(UNBOUNDED), FOLLOWING(UNBOUNDED))),
|
||||||
ORDER_BY(Payment.PaymentDate.DESC()).RANGE(PRECEDING(UNBOUNDED), FOLLOWING(UNBOUNDED))),
|
MAXi(Payment.CustomerID).OVER(ORDER_BY(Payment.PaymentDate.DESC()).ROWS(CURRENT_ROW, FOLLOWING(UNBOUNDED))),
|
||||||
MAXi(Payment.CustomerID).OVER(ORDER_BY(Payment.PaymentDate.DESC()).ROWS(CURRENT_ROW, FOLLOWING(UNBOUNDED))),
|
MINi(Payment.CustomerID).OVER(PARTITION_BY(Payment.CustomerID).ORDER_BY(Payment.PaymentDate.DESC())),
|
||||||
MINi(Payment.CustomerID).OVER(PARTITION_BY(Payment.CustomerID).ORDER_BY(Payment.PaymentDate.DESC())),
|
SUMi(Payment.CustomerID).OVER(PARTITION_BY(Payment.CustomerID).ORDER_BY(Payment.PaymentDate.DESC())),
|
||||||
SUMi(Payment.CustomerID).OVER(PARTITION_BY(Payment.CustomerID).ORDER_BY(Payment.PaymentDate.DESC())),
|
ROW_NUMBER().OVER(ORDER_BY(Payment.PaymentDate)),
|
||||||
ROW_NUMBER().OVER(ORDER_BY(Payment.PaymentDate)),
|
RANK().OVER(ORDER_BY(Payment.PaymentDate)),
|
||||||
RANK().OVER(ORDER_BY(Payment.PaymentDate)),
|
DENSE_RANK().OVER(ORDER_BY(Payment.PaymentDate)),
|
||||||
DENSE_RANK().OVER(ORDER_BY(Payment.PaymentDate)),
|
CUME_DIST().OVER(ORDER_BY(Payment.PaymentDate)),
|
||||||
CUME_DIST().OVER(ORDER_BY(Payment.PaymentDate)),
|
NTILE(11).OVER(ORDER_BY(Payment.PaymentDate)),
|
||||||
NTILE(11).OVER(ORDER_BY(Payment.PaymentDate)),
|
LAG(Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate)),
|
||||||
LAG(Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate)),
|
LAG(Payment.Amount, 2).OVER(ORDER_BY(Payment.PaymentDate)),
|
||||||
LAG(Payment.Amount, 2).OVER(ORDER_BY(Payment.PaymentDate)),
|
LAG(Payment.Amount, 2, Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate)),
|
||||||
LAG(Payment.Amount, 2, Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate)),
|
LAG(Payment.Amount, 2, 100).OVER(ORDER_BY(Payment.PaymentDate)),
|
||||||
LAG(Payment.Amount, 2, 100).OVER(ORDER_BY(Payment.PaymentDate)),
|
LEAD(Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate)),
|
||||||
LEAD(Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate)),
|
LEAD(Payment.Amount, 2).OVER(ORDER_BY(Payment.PaymentDate)),
|
||||||
LEAD(Payment.Amount, 2).OVER(ORDER_BY(Payment.PaymentDate)),
|
LEAD(Payment.Amount, 2, Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate)),
|
||||||
LEAD(Payment.Amount, 2, Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate)),
|
LEAD(Payment.Amount, 2, 100).OVER(ORDER_BY(Payment.PaymentDate)),
|
||||||
LEAD(Payment.Amount, 2, 100).OVER(ORDER_BY(Payment.PaymentDate)),
|
FIRST_VALUE(Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate)),
|
||||||
FIRST_VALUE(Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate)),
|
LAST_VALUE(Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate)),
|
||||||
LAST_VALUE(Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate)),
|
NTH_VALUE(Payment.Amount, 3).OVER(ORDER_BY(Payment.PaymentDate)),
|
||||||
NTH_VALUE(Payment.Amount, 3).OVER(ORDER_BY(Payment.PaymentDate)),
|
).FROM(
|
||||||
).GROUP_BY(Payment.Amount, Payment.CustomerID, Payment.PaymentDate).
|
Payment,
|
||||||
WHERE(Payment.PaymentID.LT(Int(10)))
|
).WHERE(
|
||||||
|
Payment.PaymentID.LT(Int(10)),
|
||||||
|
).GROUP_BY(
|
||||||
|
Payment.Amount,
|
||||||
|
Payment.CustomerID,
|
||||||
|
Payment.PaymentDate,
|
||||||
|
)
|
||||||
|
|
||||||
//fmt.Println(query.Sql())
|
//fmt.Println(query.Sql())
|
||||||
|
|
||||||
testutils.AssertStatementSql(t, query, expectedSQL, 100, 100, int64(10))
|
testutils.AssertStatementSql(t, query, expectedSQL, 100, 100, int64(10))
|
||||||
|
|
||||||
dest := []struct{}{}
|
allowUnusedColumns(func() {
|
||||||
err := query.Query(db, &dest)
|
err := query.Query(db, &[]struct{}{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectWindowClause(t *testing.T) {
|
func TestSelectWindowClause(t *testing.T) {
|
||||||
|
|
@ -3001,10 +3011,11 @@ ORDER BY payment.customer_id;
|
||||||
|
|
||||||
testutils.AssertStatementSql(t, query, expectedSQL, int64(10))
|
testutils.AssertStatementSql(t, query, expectedSQL, int64(10))
|
||||||
|
|
||||||
dest := []struct{}{}
|
allowUnusedColumns(func() {
|
||||||
err := query.Query(db, &dest)
|
err := query.Query(db, &[]struct{}{})
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectSimpleView(t *testing.T) {
|
func TestSelectSimpleView(t *testing.T) {
|
||||||
|
|
@ -3050,12 +3061,14 @@ func TestSelectJoinViewWithTable(t *testing.T) {
|
||||||
query := SELECT(
|
query := SELECT(
|
||||||
view.CustomerList.AllColumns,
|
view.CustomerList.AllColumns,
|
||||||
Rental.AllColumns,
|
Rental.AllColumns,
|
||||||
).
|
).FROM(
|
||||||
FROM(view.CustomerList.
|
view.CustomerList.
|
||||||
INNER_JOIN(Rental, view.CustomerList.ID.EQ(Rental.CustomerID)),
|
INNER_JOIN(Rental, view.CustomerList.ID.EQ(Rental.CustomerID)),
|
||||||
).
|
).WHERE(
|
||||||
ORDER_BY(view.CustomerList.ID).
|
view.CustomerList.ID.LT_EQ(Int(2)),
|
||||||
WHERE(view.CustomerList.ID.LT_EQ(Int(2)))
|
).ORDER_BY(
|
||||||
|
view.CustomerList.ID,
|
||||||
|
)
|
||||||
|
|
||||||
var dest []struct {
|
var dest []struct {
|
||||||
model.CustomerList `sql:"primary_key=ID"`
|
model.CustomerList `sql:"primary_key=ID"`
|
||||||
|
|
@ -3162,7 +3175,7 @@ FROM dvds.customer
|
||||||
WHERE ($1::boolean AND (customer.customer_id = $2)) AND (customer.activebool = $3::boolean);
|
WHERE ($1::boolean AND (customer.customer_id = $2)) AND (customer.activebool = $3::boolean);
|
||||||
`, true, int64(1), true)
|
`, true, int64(1), true)
|
||||||
|
|
||||||
dest := []model.Customer{}
|
var dest []model.Customer
|
||||||
err := stmt.Query(db, &dest)
|
err := stmt.Query(db, &dest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, dest, 1)
|
require.Len(t, dest, 1)
|
||||||
|
|
@ -3710,8 +3723,10 @@ SELECT SUM((CASE WHEN staff.active IS TRUE THEN $1::smallint ELSE $2::integer EN
|
||||||
FROM dvds.staff;
|
FROM dvds.staff;
|
||||||
`)
|
`)
|
||||||
|
|
||||||
err := stmt.Query(db, &struct{}{})
|
allowUnusedColumns(func() {
|
||||||
require.NoError(t, err)
|
err := stmt.Query(db, &struct{}{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GET_FILM_COUNT(lenFrom, lenTo IntegerExpression) IntegerExpression {
|
func GET_FILM_COUNT(lenFrom, lenTo IntegerExpression) IntegerExpression {
|
||||||
|
|
|
||||||
|
|
@ -454,8 +454,11 @@ FROM cte2;
|
||||||
} `alias:"territories4.*"`
|
} `alias:"territories4.*"`
|
||||||
}
|
}
|
||||||
|
|
||||||
err := stmt.Query(db, &dest)
|
allowUnusedColumns(func() {
|
||||||
require.NoError(t, err)
|
err := stmt.Query(db, &dest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
require.Len(t, dest, 53)
|
require.Len(t, dest, 53)
|
||||||
require.Equal(t, dest[0].Territories1.Territories, model.Territories{
|
require.Equal(t, dest[0].Territories1.Territories, model.Territories{
|
||||||
TerritoryID: "01581",
|
TerritoryID: "01581",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue