Add support for expression in OFFSET clause.
This commit is contained in:
parent
dab153a739
commit
255f4a8eaf
9 changed files with 113 additions and 23 deletions
|
|
@ -579,5 +579,5 @@ To run the tests, additional dependencies are required:
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright 2019-2023 Goran Bjelanovic
|
Copyright 2019-2024 Goran Bjelanovic
|
||||||
Licensed under the Apache License, Version 2.0.
|
Licensed under the Apache License, Version 2.0.
|
||||||
|
|
|
||||||
|
|
@ -222,16 +222,18 @@ func (l *ClauseLimit) Serialize(statementType StatementType, out *SQLBuilder, op
|
||||||
|
|
||||||
// ClauseOffset struct
|
// ClauseOffset struct
|
||||||
type ClauseOffset struct {
|
type ClauseOffset struct {
|
||||||
Count int64
|
Count IntegerExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize serializes clause into SQLBuilder
|
// Serialize serializes clause into SQLBuilder
|
||||||
func (o *ClauseOffset) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
|
func (o *ClauseOffset) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
|
||||||
if o.Count >= 0 {
|
if is.Nil(o.Count) {
|
||||||
out.NewLine()
|
return
|
||||||
out.WriteString("OFFSET")
|
|
||||||
out.insertParametrizedArgument(o.Count)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out.NewLine()
|
||||||
|
out.WriteString("OFFSET")
|
||||||
|
o.Count.serialize(statementType, out, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClauseFetch struct
|
// ClauseFetch struct
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,6 @@ func newSelectStatement(table ReadableTable, projections []Projection) SelectSta
|
||||||
newSelect.From.Tables = []jet.Serializer{table}
|
newSelect.From.Tables = []jet.Serializer{table}
|
||||||
}
|
}
|
||||||
newSelect.Limit.Count = -1
|
newSelect.Limit.Count = -1
|
||||||
newSelect.Offset.Count = -1
|
|
||||||
newSelect.ShareLock.Name = "LOCK IN SHARE MODE"
|
newSelect.ShareLock.Name = "LOCK IN SHARE MODE"
|
||||||
newSelect.ShareLock.InNewLine = true
|
newSelect.ShareLock.InNewLine = true
|
||||||
|
|
||||||
|
|
@ -158,7 +157,7 @@ func (s *selectStatementImpl) LIMIT(limit int64) SelectStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *selectStatementImpl) OFFSET(offset int64) SelectStatement {
|
func (s *selectStatementImpl) OFFSET(offset int64) SelectStatement {
|
||||||
s.Offset.Count = offset
|
s.Offset.Count = Int(offset)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,6 @@ func newSetStatementImpl(operator string, all bool, selects []jet.SerializerStat
|
||||||
newSetStatement.setOperator.All = all
|
newSetStatement.setOperator.All = all
|
||||||
newSetStatement.setOperator.Selects = selects
|
newSetStatement.setOperator.Selects = selects
|
||||||
newSetStatement.setOperator.Limit.Count = -1
|
newSetStatement.setOperator.Limit.Count = -1
|
||||||
newSetStatement.setOperator.Offset.Count = -1
|
|
||||||
|
|
||||||
newSetStatement.setOperatorsImpl.parent = newSetStatement
|
newSetStatement.setOperatorsImpl.parent = newSetStatement
|
||||||
|
|
||||||
|
|
@ -81,7 +80,7 @@ func (s *setStatementImpl) LIMIT(limit int64) setStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *setStatementImpl) OFFSET(offset int64) setStatement {
|
func (s *setStatementImpl) OFFSET(offset int64) setStatement {
|
||||||
s.setOperator.Offset.Count = offset
|
s.setOperator.Offset.Count = Int(offset)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,8 @@ type SelectStatement interface {
|
||||||
ORDER_BY(orderByClauses ...OrderByClause) SelectStatement
|
ORDER_BY(orderByClauses ...OrderByClause) SelectStatement
|
||||||
LIMIT(limit int64) SelectStatement
|
LIMIT(limit int64) SelectStatement
|
||||||
OFFSET(offset int64) SelectStatement
|
OFFSET(offset int64) SelectStatement
|
||||||
|
// OFFSET_e can be used when an integer expression is needed as offset, otherwise OFFSET can be used
|
||||||
|
OFFSET_e(offset IntegerExpression) SelectStatement
|
||||||
FETCH_FIRST(count IntegerExpression) fetchExpand
|
FETCH_FIRST(count IntegerExpression) fetchExpand
|
||||||
FOR(lock RowLock) SelectStatement
|
FOR(lock RowLock) SelectStatement
|
||||||
|
|
||||||
|
|
@ -91,7 +93,6 @@ func newSelectStatement(table ReadableTable, projections []Projection) SelectSta
|
||||||
newSelect.From.Tables = []jet.Serializer{table}
|
newSelect.From.Tables = []jet.Serializer{table}
|
||||||
}
|
}
|
||||||
newSelect.Limit.Count = -1
|
newSelect.Limit.Count = -1
|
||||||
newSelect.Offset.Count = -1
|
|
||||||
|
|
||||||
newSelect.setOperatorsImpl.parent = newSelect
|
newSelect.setOperatorsImpl.parent = newSelect
|
||||||
|
|
||||||
|
|
@ -157,6 +158,11 @@ func (s *selectStatementImpl) LIMIT(limit int64) SelectStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *selectStatementImpl) OFFSET(offset int64) SelectStatement {
|
func (s *selectStatementImpl) OFFSET(offset int64) SelectStatement {
|
||||||
|
s.Offset.Count = Int(offset)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *selectStatementImpl) OFFSET_e(offset IntegerExpression) SelectStatement {
|
||||||
s.Offset.Count = offset
|
s.Offset.Count = offset
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ type setStatement interface {
|
||||||
|
|
||||||
LIMIT(limit int64) setStatement
|
LIMIT(limit int64) setStatement
|
||||||
OFFSET(offset int64) setStatement
|
OFFSET(offset int64) setStatement
|
||||||
|
// OFFSET_e can be used when an integer expression is needed as offset, otherwise OFFSET can be used
|
||||||
|
OFFSET_e(offset IntegerExpression) setStatement
|
||||||
|
|
||||||
AsTable(alias string) SelectTable
|
AsTable(alias string) SelectTable
|
||||||
}
|
}
|
||||||
|
|
@ -107,7 +109,6 @@ func newSetStatementImpl(operator string, all bool, selects []jet.SerializerStat
|
||||||
newSetStatement.setOperator.All = all
|
newSetStatement.setOperator.All = all
|
||||||
newSetStatement.setOperator.Selects = selects
|
newSetStatement.setOperator.Selects = selects
|
||||||
newSetStatement.setOperator.Limit.Count = -1
|
newSetStatement.setOperator.Limit.Count = -1
|
||||||
newSetStatement.setOperator.Offset.Count = -1
|
|
||||||
|
|
||||||
newSetStatement.setOperatorsImpl.parent = newSetStatement
|
newSetStatement.setOperatorsImpl.parent = newSetStatement
|
||||||
|
|
||||||
|
|
@ -125,6 +126,11 @@ func (s *setStatementImpl) LIMIT(limit int64) setStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *setStatementImpl) OFFSET(offset int64) setStatement {
|
func (s *setStatementImpl) OFFSET(offset int64) setStatement {
|
||||||
|
s.setOperator.Offset.Count = Int(offset)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *setStatementImpl) OFFSET_e(offset IntegerExpression) setStatement {
|
||||||
s.setOperator.Offset.Count = offset
|
s.setOperator.Offset.Count = offset
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,6 @@ func newSelectStatement(table ReadableTable, projections []Projection) SelectSta
|
||||||
newSelect.From.Tables = []jet.Serializer{table}
|
newSelect.From.Tables = []jet.Serializer{table}
|
||||||
}
|
}
|
||||||
newSelect.Limit.Count = -1
|
newSelect.Limit.Count = -1
|
||||||
newSelect.Offset.Count = -1
|
|
||||||
newSelect.ShareLock.Name = "LOCK IN SHARE MODE"
|
newSelect.ShareLock.Name = "LOCK IN SHARE MODE"
|
||||||
newSelect.ShareLock.InNewLine = true
|
newSelect.ShareLock.InNewLine = true
|
||||||
|
|
||||||
|
|
@ -141,7 +140,7 @@ func (s *selectStatementImpl) LIMIT(limit int64) SelectStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *selectStatementImpl) OFFSET(offset int64) SelectStatement {
|
func (s *selectStatementImpl) OFFSET(offset int64) SelectStatement {
|
||||||
s.Offset.Count = offset
|
s.Offset.Count = Int(offset)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,6 @@ func newSetStatementImpl(operator string, all bool, selects []jet.SerializerStat
|
||||||
newSetStatement.setOperator.All = all
|
newSetStatement.setOperator.All = all
|
||||||
newSetStatement.setOperator.Selects = selects
|
newSetStatement.setOperator.Selects = selects
|
||||||
newSetStatement.setOperator.Limit.Count = -1
|
newSetStatement.setOperator.Limit.Count = -1
|
||||||
newSetStatement.setOperator.Offset.Count = -1
|
|
||||||
newSetStatement.setOperator.SkipSelectWrap = true
|
newSetStatement.setOperator.SkipSelectWrap = true
|
||||||
|
|
||||||
newSetStatement.setOperatorsImpl.parent = newSetStatement
|
newSetStatement.setOperatorsImpl.parent = newSetStatement
|
||||||
|
|
@ -82,7 +81,7 @@ func (s *setStatementImpl) LIMIT(limit int64) setStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *setStatementImpl) OFFSET(offset int64) setStatement {
|
func (s *setStatementImpl) OFFSET(offset int64) setStatement {
|
||||||
s.setOperator.Offset.Count = offset
|
s.setOperator.Offset.Count = Int(offset)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -320,6 +320,38 @@ FETCH FIRST (
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOffsetExpression(t *testing.T) {
|
||||||
|
|
||||||
|
stmt := SELECT(Actor.AllColumns).
|
||||||
|
FROM(Actor).
|
||||||
|
ORDER_BY(Actor.ActorID).
|
||||||
|
OFFSET_e(IntExp(
|
||||||
|
SELECT(MAX(Store.StoreID)).
|
||||||
|
FROM(Store),
|
||||||
|
)).LIMIT(10)
|
||||||
|
|
||||||
|
testutils.AssertDebugStatementSql(t, stmt, `
|
||||||
|
SELECT actor.actor_id AS "actor.actor_id",
|
||||||
|
actor.first_name AS "actor.first_name",
|
||||||
|
actor.last_name AS "actor.last_name",
|
||||||
|
actor.last_update AS "actor.last_update"
|
||||||
|
FROM dvds.actor
|
||||||
|
ORDER BY actor.actor_id
|
||||||
|
LIMIT 10
|
||||||
|
OFFSET (
|
||||||
|
SELECT MAX(store.store_id)
|
||||||
|
FROM dvds.store
|
||||||
|
);
|
||||||
|
`)
|
||||||
|
|
||||||
|
var dest []model.Actor
|
||||||
|
|
||||||
|
err := stmt.Query(db, &dest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, dest, 10)
|
||||||
|
require.Equal(t, dest[0].ActorID, int32(3))
|
||||||
|
}
|
||||||
|
|
||||||
func TestJoinQueryStruct(t *testing.T) {
|
func TestJoinQueryStruct(t *testing.T) {
|
||||||
|
|
||||||
expectedSQL := `
|
expectedSQL := `
|
||||||
|
|
@ -2365,16 +2397,14 @@ OFFSET 20;
|
||||||
Payment.
|
Payment.
|
||||||
SELECT(Payment.PaymentID, Payment.Amount).
|
SELECT(Payment.PaymentID, Payment.Amount).
|
||||||
WHERE(Payment.Amount.GT_EQ(Float(200))),
|
WHERE(Payment.Amount.GT_EQ(Float(200))),
|
||||||
).
|
).ORDER_BY(
|
||||||
ORDER_BY(IntegerColumn("payment.payment_id").ASC(), Payment.Amount.DESC()).
|
IntegerColumn("payment.payment_id").ASC(),
|
||||||
LIMIT(10).
|
Payment.Amount.DESC(),
|
||||||
OFFSET(20)
|
).LIMIT(10).OFFSET(20)
|
||||||
|
|
||||||
//fmt.Println(query.DebugSql())
|
|
||||||
|
|
||||||
testutils.AssertDebugStatementSql(t, query, expectedQuery, float64(100), float64(200), int64(10), int64(20))
|
testutils.AssertDebugStatementSql(t, query, expectedQuery, float64(100), float64(200), int64(10), int64(20))
|
||||||
|
|
||||||
dest := []model.Payment{}
|
var dest []model.Payment
|
||||||
|
|
||||||
err := query.Query(db, &dest)
|
err := query.Query(db, &dest)
|
||||||
|
|
||||||
|
|
@ -2394,6 +2424,56 @@ OFFSET 20;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnionOffsetWithExpression(t *testing.T) {
|
||||||
|
stmt := UNION(
|
||||||
|
SELECT(Rental.AllColumns).
|
||||||
|
FROM(Rental).
|
||||||
|
WHERE(Rental.ReturnDate.IS_NULL()),
|
||||||
|
|
||||||
|
SELECT(Rental.AllColumns).
|
||||||
|
FROM(Rental).
|
||||||
|
WHERE(Rental.LastUpdate.GT(LOCALTIMESTAMP())),
|
||||||
|
).OFFSET_e(IntExp(
|
||||||
|
SELECT(Int32(3)),
|
||||||
|
)).LIMIT(10)
|
||||||
|
|
||||||
|
testutils.AssertStatementSql(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
|
||||||
|
WHERE rental.return_date IS NULL
|
||||||
|
)
|
||||||
|
UNION
|
||||||
|
(
|
||||||
|
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
|
||||||
|
WHERE rental.last_update > LOCALTIMESTAMP
|
||||||
|
)
|
||||||
|
LIMIT $1
|
||||||
|
OFFSET (
|
||||||
|
SELECT $2::integer
|
||||||
|
);
|
||||||
|
`)
|
||||||
|
|
||||||
|
var dest []model.Rental
|
||||||
|
|
||||||
|
err := stmt.Query(db, &dest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, dest, 10)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAllSetOperators(t *testing.T) {
|
func TestAllSetOperators(t *testing.T) {
|
||||||
var select1 = Payment.SELECT(Payment.AllColumns).WHERE(Payment.PaymentID.GT_EQ(Int(17600)).AND(Payment.PaymentID.LT(Int(17610))))
|
var select1 = Payment.SELECT(Payment.AllColumns).WHERE(Payment.PaymentID.GT_EQ(Int(17600)).AND(Payment.PaymentID.LT(Int(17610))))
|
||||||
var select2 = Payment.SELECT(Payment.AllColumns).WHERE(Payment.PaymentID.GT_EQ(Int(17620)).AND(Payment.PaymentID.LT(Int(17630))))
|
var select2 = Payment.SELECT(Payment.AllColumns).WHERE(Payment.PaymentID.GT_EQ(Int(17620)).AND(Payment.PaymentID.LT(Int(17630))))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue