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

View file

@ -64,14 +64,24 @@ func (e *ExpressionInterfaceImpl) AS(alias string) Projection {
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 {
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 {
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) {

View file

@ -2,28 +2,78 @@ package jet
// 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)
}
type orderByClauseImpl struct {
expression Expression
ascent bool
ascending *bool
nullsFirst *bool
}
func (o *orderByClauseImpl) serializeForOrderBy(statement StatementType, out *SQLBuilder) {
if o.expression == nil {
func (ord *orderByClauseImpl) NULLS_FIRST() OrderByClause {
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")
}
o.expression.serializeForOrderBy(statement, out)
ord.expression.serializeForOrderBy(statement, out)
if o.ascent {
out.WriteString("ASC")
} else {
out.WriteString("DESC")
if ord.ascending != nil {
if *ord.ascending {
out.WriteString("ASC")
} 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 {
return &orderByClauseImpl{expression: expression, ascent: ascent}
func newOrderByAscending(expression Expression, ascending bool) OrderByClause {
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...)
}
func SerializeForOrderBy(exp Expression, statementType StatementType, out *SQLBuilder) {
exp.serializeForOrderBy(statementType, out)
}
func contains(options []SerializeOption, option SerializeOption) bool {
for _, opt := range options {
if opt == option {