Add support for NULLS_FIRST and NULLS_LAST sorting order.

This commit is contained in:
go-jet 2024-02-10 14:03:31 +01:00
parent 0fc51cf402
commit dab153a739
8 changed files with 882 additions and 19 deletions

View file

@ -12,6 +12,7 @@ type Dialect interface {
IdentifierQuoteChar() byte IdentifierQuoteChar() byte
ArgumentPlaceholder() QueryPlaceholderFunc ArgumentPlaceholder() QueryPlaceholderFunc
IsReservedWord(name string) bool IsReservedWord(name string) bool
SerializeOrderBy() func(expression Expression, ascending, nullsFirst *bool) SerializerFunc
} }
// SerializerFunc func // SerializerFunc func
@ -33,6 +34,7 @@ type DialectParams struct {
IdentifierQuoteChar byte IdentifierQuoteChar byte
ArgumentPlaceholder QueryPlaceholderFunc ArgumentPlaceholder QueryPlaceholderFunc
ReservedWords []string ReservedWords []string
SerializeOrderBy func(expression Expression, ascending, nullsFirst *bool) SerializerFunc
} }
// NewDialect creates new dialect with params // NewDialect creates new dialect with params
@ -46,6 +48,7 @@ func NewDialect(params DialectParams) Dialect {
identifierQuoteChar: params.IdentifierQuoteChar, identifierQuoteChar: params.IdentifierQuoteChar,
argumentPlaceholder: params.ArgumentPlaceholder, argumentPlaceholder: params.ArgumentPlaceholder,
reservedWords: arrayOfStringsToMapOfStrings(params.ReservedWords), reservedWords: arrayOfStringsToMapOfStrings(params.ReservedWords),
serializeOrderBy: params.SerializeOrderBy,
} }
} }
@ -58,8 +61,7 @@ type dialectImpl struct {
identifierQuoteChar byte identifierQuoteChar byte
argumentPlaceholder QueryPlaceholderFunc argumentPlaceholder QueryPlaceholderFunc
reservedWords map[string]bool reservedWords map[string]bool
serializeOrderBy func(expression Expression, ascending, nullsFirst *bool) SerializerFunc
supportsReturning bool
} }
func (d *dialectImpl) Name() string { func (d *dialectImpl) Name() string {
@ -101,6 +103,10 @@ func (d *dialectImpl) IsReservedWord(name string) bool {
return isReservedWord return isReservedWord
} }
func (d *dialectImpl) SerializeOrderBy() func(expression Expression, ascending, nullsFirst *bool) SerializerFunc {
return d.serializeOrderBy
}
func arrayOfStringsToMapOfStrings(arr []string) map[string]bool { func arrayOfStringsToMapOfStrings(arr []string) map[string]bool {
ret := map[string]bool{} ret := map[string]bool{}
for _, elem := range arr { for _, elem := range arr {

View file

@ -64,14 +64,24 @@ func (e *ExpressionInterfaceImpl) AS(alias string) Projection {
return newAlias(e.Parent, alias) return newAlias(e.Parent, alias)
} }
// ASC expression will be used to sort query result in ascending order // ASC expression will be used to sort a query result in ascending order
func (e *ExpressionInterfaceImpl) ASC() OrderByClause { func (e *ExpressionInterfaceImpl) ASC() OrderByClause {
return newOrderByClause(e.Parent, true) return newOrderByAscending(e.Parent, true)
} }
// DESC expression will be used to sort query result in descending order // DESC expression will be used to sort a query result in descending order
func (e *ExpressionInterfaceImpl) DESC() OrderByClause { func (e *ExpressionInterfaceImpl) DESC() OrderByClause {
return newOrderByClause(e.Parent, false) return newOrderByAscending(e.Parent, false)
}
// NULLS_FIRST specifies sort where null values appear before all non-null values
func (e *ExpressionInterfaceImpl) NULLS_FIRST() OrderByClause {
return newOrderByNullsFirst(e.Parent, true)
}
// NULLS_LAST specifies sort where null values appear after all non-null values
func (e *ExpressionInterfaceImpl) NULLS_LAST() OrderByClause {
return newOrderByNullsFirst(e.Parent, false)
} }
func (e *ExpressionInterfaceImpl) serializeForGroupBy(statement StatementType, out *SQLBuilder) { func (e *ExpressionInterfaceImpl) serializeForGroupBy(statement StatementType, out *SQLBuilder) {

View file

@ -2,28 +2,78 @@ package jet
// OrderByClause interface // OrderByClause interface
type OrderByClause interface { type OrderByClause interface {
// NULLS_FIRST specifies sort where null values appear before all non-null values.
// For some dialects(mysql,mariadb), which do not support NULL_FIRST, NULL_FIRST is simulated
// with additional IS_NOT_NULL expression.
// For instance,
// Rental.ReturnDate.DESC().NULLS_FIRST()
// would translate to,
// rental.return_date IS NOT NULL, rental.return_date DESC
NULLS_FIRST() OrderByClause
// NULLS_LAST specifies sort where null values appear after all non-null values.
// For some dialects(mysql,mariadb), which do not support NULLS_LAST, NULLS_LAST is simulated
// with additional IS_NULL expression.
// For instance,
// Rental.ReturnDate.ASC().NULLS_LAST()
// would translate to,
// rental.return_date IS NULL, rental.return_date ASC
NULLS_LAST() OrderByClause
serializeForOrderBy(statement StatementType, out *SQLBuilder) serializeForOrderBy(statement StatementType, out *SQLBuilder)
} }
type orderByClauseImpl struct { type orderByClauseImpl struct {
expression Expression expression Expression
ascent bool ascending *bool
nullsFirst *bool
} }
func (o *orderByClauseImpl) serializeForOrderBy(statement StatementType, out *SQLBuilder) { func (ord *orderByClauseImpl) NULLS_FIRST() OrderByClause {
if o.expression == nil { nullsFirst := true
ord.nullsFirst = &nullsFirst
return ord
}
func (ord *orderByClauseImpl) NULLS_LAST() OrderByClause {
nullsFirst := false
ord.nullsFirst = &nullsFirst
return ord
}
func (ord *orderByClauseImpl) serializeForOrderBy(statement StatementType, out *SQLBuilder) {
customSerializer := out.Dialect.SerializeOrderBy()
if customSerializer != nil {
customSerializer(ord.expression, ord.ascending, ord.nullsFirst)(statement, out)
return
}
if ord.expression == nil {
panic("jet: nil expression in ORDER BY clause") panic("jet: nil expression in ORDER BY clause")
} }
o.expression.serializeForOrderBy(statement, out) ord.expression.serializeForOrderBy(statement, out)
if o.ascent { if ord.ascending != nil {
out.WriteString("ASC") if *ord.ascending {
} else { out.WriteString("ASC")
out.WriteString("DESC") } else {
out.WriteString("DESC")
}
}
if ord.nullsFirst != nil {
if *ord.nullsFirst {
out.WriteString("NULLS FIRST")
} else {
out.WriteString("NULLS LAST")
}
} }
} }
func newOrderByClause(expression Expression, ascent bool) OrderByClause { func newOrderByAscending(expression Expression, ascending bool) OrderByClause {
return &orderByClauseImpl{expression: expression, ascent: ascent} return &orderByClauseImpl{expression: expression, ascending: &ascending}
}
func newOrderByNullsFirst(expression Expression, nullsFirst bool) OrderByClause {
return &orderByClauseImpl{expression: expression, nullsFirst: &nullsFirst}
} }

View file

@ -44,6 +44,10 @@ func Serialize(exp Serializer, statementType StatementType, out *SQLBuilder, opt
exp.serialize(statementType, out, options...) exp.serialize(statementType, out, options...)
} }
func SerializeForOrderBy(exp Expression, statementType StatementType, out *SQLBuilder) {
exp.serializeForOrderBy(statementType, out)
}
func contains(options []SerializeOption, option SerializeOption) bool { func contains(options []SerializeOption, option SerializeOption) bool {
for _, opt := range options { for _, opt := range options {
if opt == option { if opt == option {

View file

@ -26,7 +26,8 @@ func newDialect() jet.Dialect {
ArgumentPlaceholder: func(int) string { ArgumentPlaceholder: func(int) string {
return "?" return "?"
}, },
ReservedWords: reservedWords, ReservedWords: reservedWords,
SerializeOrderBy: serializeOrderBy,
} }
return jet.NewDialect(mySQLDialectParams) return jet.NewDialect(mySQLDialectParams)
@ -162,6 +163,53 @@ func mysqlNOTREGEXPLIKEoperator(expressions ...jet.Serializer) jet.SerializerFun
} }
} }
func serializeOrderBy(expression Expression, ascending, nullsFirst *bool) jet.SerializerFunc {
return func(statement jet.StatementType, out *jet.SQLBuilder, options ...jet.SerializeOption) {
if nullsFirst == nil {
jet.SerializeForOrderBy(expression, statement, out)
if ascending != nil {
serializeAscending(*ascending, out)
}
return
}
asc := true
if ascending != nil {
asc = *ascending
}
if asc {
if !*nullsFirst {
jet.SerializeForOrderBy(expression.IS_NULL(), statement, out)
out.WriteString(", ")
}
jet.SerializeForOrderBy(expression, statement, out)
if ascending != nil {
serializeAscending(asc, out)
}
} else {
if *nullsFirst {
jet.SerializeForOrderBy(expression.IS_NOT_NULL(), statement, out)
out.WriteString(", ")
}
jet.SerializeForOrderBy(expression, statement, out)
serializeAscending(asc, out)
}
}
}
func serializeAscending(ascending bool, out *jet.SQLBuilder) {
if ascending {
out.WriteString("ASC")
} else {
out.WriteString("DESC")
}
}
var reservedWords = []string{ var reservedWords = []string{
"ACCESSIBLE", "ACCESSIBLE",
"ADD", "ADD",

View file

@ -3,6 +3,7 @@ package mysql
import ( import (
"context" "context"
"database/sql" "database/sql"
"github.com/go-jet/jet/v2/postgres"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -296,6 +297,296 @@ ORDER BY inventory.film_id, inventory.store_id;
`) `)
} }
func TestOrderBy(t *testing.T) {
// NULLS_FIRST and NULLS_LAST are simulated using IS_NULL, ...
ensureNullsFirstRentalResult := func(t *testing.T, stmt postgres.Statement, asc bool) {
var dest []model.Rental
err := stmt.Query(db, &dest)
require.NoError(t, err)
require.Len(t, dest, 200)
require.Nil(t, dest[0].ReturnDate)
require.NotNil(t, dest[199].ReturnDate)
if asc {
require.True(t, dest[198].ReturnDate.Before(*dest[199].ReturnDate))
} else {
require.True(t, dest[199].ReturnDate.Before(*dest[198].ReturnDate))
}
}
ensureNullsLastRentalResult := func(t *testing.T, stmt postgres.Statement, asc bool) {
var dest []model.Rental
err := stmt.Query(db, &dest)
require.NoError(t, err)
require.Len(t, dest, 200)
require.NotNil(t, dest[0].ReturnDate)
require.Nil(t, dest[199].ReturnDate)
if asc {
require.True(t, dest[0].ReturnDate.Before(*dest[1].ReturnDate))
} else {
require.True(t, dest[1].ReturnDate.Before(*dest[0].ReturnDate))
}
}
t.Run("default", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate,
).LIMIT(200)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM dvds.rental
ORDER BY rental.return_date
LIMIT 200;
`)
ensureNullsFirstRentalResult(t, stmt, true)
})
t.Run("NULLS FIRST", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.NULLS_FIRST(),
).LIMIT(200)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM dvds.rental
ORDER BY rental.return_date
LIMIT 200;
`)
ensureNullsFirstRentalResult(t, stmt, true)
})
t.Run("NULLS LAST", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.NULLS_LAST(),
).LIMIT(200).OFFSET(15800)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM dvds.rental
ORDER BY rental.return_date IS NULL, rental.return_date
LIMIT 200
OFFSET 15800;
`)
ensureNullsLastRentalResult(t, stmt, true)
})
t.Run("ASC", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.ASC(),
).LIMIT(200)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM dvds.rental
ORDER BY rental.return_date ASC
LIMIT 200;
`)
ensureNullsFirstRentalResult(t, stmt, true)
})
t.Run("ASC NULLS FIRST", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.ASC().NULLS_FIRST(),
).LIMIT(200)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM dvds.rental
ORDER BY rental.return_date ASC
LIMIT 200;
`)
ensureNullsFirstRentalResult(t, stmt, true)
})
t.Run("ASC NULLS LAST", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.ASC().NULLS_LAST(),
).LIMIT(200).OFFSET(15800)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM dvds.rental
ORDER BY rental.return_date IS NULL, rental.return_date ASC
LIMIT 200
OFFSET 15800;
`)
ensureNullsLastRentalResult(t, stmt, true)
})
t.Run("DESC", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.DESC(),
).LIMIT(200).OFFSET(15800)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM dvds.rental
ORDER BY rental.return_date DESC
LIMIT 200
OFFSET 15800;
`)
ensureNullsLastRentalResult(t, stmt, false)
})
t.Run("DESC NULLS LAST", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.DESC().NULLS_LAST(),
).LIMIT(200).OFFSET(15800)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM dvds.rental
ORDER BY rental.return_date DESC
LIMIT 200
OFFSET 15800;
`)
ensureNullsLastRentalResult(t, stmt, false)
})
t.Run("DESC NULLS FIRST", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.DESC().NULLS_FIRST(),
).LIMIT(200)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM dvds.rental
ORDER BY rental.return_date IS NOT NULL, rental.return_date DESC
LIMIT 200;
`)
ensureNullsFirstRentalResult(t, stmt, false)
})
t.Run("complex", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.RentalID.DESC(),
Rental.ReturnDate.DESC().NULLS_FIRST(),
Rental.LastUpdate.ASC(),
Rental.InventoryID.ADD(Rental.RentalID).ASC().NULLS_LAST(),
).LIMIT(200)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM dvds.rental
ORDER BY rental.rental_id DESC, rental.return_date IS NOT NULL, rental.return_date DESC, rental.last_update ASC, (rental.inventory_id + rental.rental_id) IS NULL, rental.inventory_id + rental.rental_id ASC
LIMIT 200;
`)
require.NoError(t, stmt.Query(db, &struct{}{}))
})
}
func TestSubQuery(t *testing.T) { func TestSubQuery(t *testing.T) {
rRatingFilms := SELECT( rRatingFilms := SELECT(

View file

@ -1151,7 +1151,7 @@ func TestSelectOrderByAscDesc(t *testing.T) {
firstCustomerAsc := customersAsc[0] firstCustomerAsc := customersAsc[0]
lastCustomerAsc := customersAsc[len(customersAsc)-1] lastCustomerAsc := customersAsc[len(customersAsc)-1]
customersDesc := []model.Customer{} var customersDesc []model.Customer
err = Customer.SELECT(Customer.CustomerID, Customer.FirstName, Customer.LastName). err = Customer.SELECT(Customer.CustomerID, Customer.FirstName, Customer.LastName).
ORDER_BY(Customer.FirstName.DESC()). ORDER_BY(Customer.FirstName.DESC()).
Query(db, &customersDesc) Query(db, &customersDesc)
@ -1164,7 +1164,7 @@ func TestSelectOrderByAscDesc(t *testing.T) {
testutils.AssertDeepEqual(t, firstCustomerAsc, lastCustomerDesc) testutils.AssertDeepEqual(t, firstCustomerAsc, lastCustomerDesc)
testutils.AssertDeepEqual(t, lastCustomerAsc, firstCustomerDesc) testutils.AssertDeepEqual(t, lastCustomerAsc, firstCustomerDesc)
customersAscDesc := []model.Customer{} var customersAscDesc []model.Customer
err = Customer.SELECT(Customer.CustomerID, Customer.FirstName, Customer.LastName). err = Customer.SELECT(Customer.CustomerID, Customer.FirstName, Customer.LastName).
ORDER_BY(Customer.FirstName.ASC(), Customer.LastName.DESC()). ORDER_BY(Customer.FirstName.ASC(), Customer.LastName.DESC()).
Query(db, &customersAscDesc) Query(db, &customersAscDesc)
@ -1187,6 +1187,233 @@ func TestSelectOrderByAscDesc(t *testing.T) {
testutils.AssertDeepEqual(t, customerAscDesc327, customersAscDesc[327]) testutils.AssertDeepEqual(t, customerAscDesc327, customersAscDesc[327])
} }
func TestOrderBy(t *testing.T) {
t.Run("default", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate,
).LIMIT(200)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM dvds.rental
ORDER BY rental.return_date
LIMIT 200;
`)
require.NoError(t, stmt.Query(db, &struct{}{}))
})
t.Run("NULLS FIRST", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.NULLS_FIRST(),
).LIMIT(200)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM dvds.rental
ORDER BY rental.return_date NULLS FIRST
LIMIT 200;
`)
require.NoError(t, stmt.Query(db, &struct{}{}))
})
t.Run("NULLS LAST", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.NULLS_LAST(),
).LIMIT(200)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM dvds.rental
ORDER BY rental.return_date NULLS LAST
LIMIT 200;
`)
require.NoError(t, stmt.Query(db, &struct{}{}))
})
t.Run("ASC", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.ASC(),
).LIMIT(200)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM dvds.rental
ORDER BY rental.return_date ASC
LIMIT 200;
`)
require.NoError(t, stmt.Query(db, &struct{}{}))
})
t.Run("ASC NULLS FIRST", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.ASC().NULLS_FIRST(),
).LIMIT(200)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM dvds.rental
ORDER BY rental.return_date ASC NULLS FIRST
LIMIT 200;
`)
require.NoError(t, stmt.Query(db, &struct{}{}))
})
t.Run("ASC NULLS LAST", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.ASC().NULLS_LAST(),
).LIMIT(200).OFFSET(15800)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM dvds.rental
ORDER BY rental.return_date ASC NULLS LAST
LIMIT 200
OFFSET 15800;
`)
require.NoError(t, stmt.Query(db, &struct{}{}))
})
t.Run("DESC", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.DESC(),
).LIMIT(200).OFFSET(15800)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM dvds.rental
ORDER BY rental.return_date DESC
LIMIT 200
OFFSET 15800;
`)
require.NoError(t, stmt.Query(db, &struct{}{}))
})
t.Run("DESC NULLS LAST", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.DESC().NULLS_LAST(),
).LIMIT(200).OFFSET(15800)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM dvds.rental
ORDER BY rental.return_date DESC NULLS LAST
LIMIT 200
OFFSET 15800;
`)
require.NoError(t, stmt.Query(db, &struct{}{}))
})
t.Run("DESC NULLS FIRST", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.DESC().NULLS_FIRST(),
).LIMIT(200)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM dvds.rental
ORDER BY rental.return_date DESC NULLS FIRST
LIMIT 200;
`)
require.NoError(t, stmt.Query(db, &struct{}{}))
})
}
func TestSelectFullJoin(t *testing.T) { func TestSelectFullJoin(t *testing.T) {
expectedSQL := ` expectedSQL := `
SELECT customer.customer_id AS "customer.customer_id", SELECT customer.customer_id AS "customer.customer_id",

View file

@ -145,6 +145,233 @@ ORDER BY payment.customer_id, SUM(payment.amount) ASC;
requireLogged(t, query) requireLogged(t, query)
} }
func TestOrderBy(t *testing.T) {
t.Run("default", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate,
).LIMIT(200)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM rental
ORDER BY rental.return_date
LIMIT 200;
`)
require.NoError(t, stmt.Query(db, &struct{}{}))
})
t.Run("NULLS FIRST", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.NULLS_FIRST(),
).LIMIT(200)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM rental
ORDER BY rental.return_date NULLS FIRST
LIMIT 200;
`)
require.NoError(t, stmt.Query(db, &struct{}{}))
})
t.Run("NULLS LAST", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.NULLS_LAST(),
).LIMIT(200)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM rental
ORDER BY rental.return_date NULLS LAST
LIMIT 200;
`)
require.NoError(t, stmt.Query(db, &struct{}{}))
})
t.Run("ASC", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.ASC(),
).LIMIT(200)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM rental
ORDER BY rental.return_date ASC
LIMIT 200;
`)
require.NoError(t, stmt.Query(db, &struct{}{}))
})
t.Run("ASC NULLS FIRST", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.ASC().NULLS_FIRST(),
).LIMIT(200)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM rental
ORDER BY rental.return_date ASC NULLS FIRST
LIMIT 200;
`)
require.NoError(t, stmt.Query(db, &struct{}{}))
})
t.Run("ASC NULLS LAST", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.ASC().NULLS_LAST(),
).LIMIT(200).OFFSET(15800)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM rental
ORDER BY rental.return_date ASC NULLS LAST
LIMIT 200
OFFSET 15800;
`)
require.NoError(t, stmt.Query(db, &struct{}{}))
})
t.Run("DESC", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.DESC(),
).LIMIT(200).OFFSET(15800)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM rental
ORDER BY rental.return_date DESC
LIMIT 200
OFFSET 15800;
`)
require.NoError(t, stmt.Query(db, &struct{}{}))
})
t.Run("DESC NULLS LAST", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.DESC().NULLS_LAST(),
).LIMIT(200).OFFSET(15800)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM rental
ORDER BY rental.return_date DESC NULLS LAST
LIMIT 200
OFFSET 15800;
`)
require.NoError(t, stmt.Query(db, &struct{}{}))
})
t.Run("DESC NULLS FIRST", func(t *testing.T) {
stmt := SELECT(
Rental.AllColumns,
).FROM(
Rental,
).ORDER_BY(
Rental.ReturnDate.DESC().NULLS_FIRST(),
).LIMIT(200)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM rental
ORDER BY rental.return_date DESC NULLS FIRST
LIMIT 200;
`)
require.NoError(t, stmt.Query(db, &struct{}{}))
})
}
func TestAggregateFunctionDistinct(t *testing.T) { func TestAggregateFunctionDistinct(t *testing.T) {
stmt := SELECT( stmt := SELECT(
Payment.CustomerID, Payment.CustomerID,