Add support for FETCH FIRST clause.
This commit is contained in:
parent
e03773a79e
commit
e51ddd5506
3 changed files with 145 additions and 3 deletions
|
|
@ -234,6 +234,29 @@ func (o *ClauseOffset) Serialize(statementType StatementType, out *SQLBuilder, o
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClauseFetch struct
|
||||||
|
type ClauseFetch struct {
|
||||||
|
Count IntegerExpression
|
||||||
|
WithTies bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize serializes ClauseFetch into sql builder output
|
||||||
|
func (o *ClauseFetch) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
|
||||||
|
if is.Nil(o.Count) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
out.NewLine()
|
||||||
|
out.WriteString("FETCH FIRST")
|
||||||
|
o.Count.serialize(statementType, out, options...)
|
||||||
|
|
||||||
|
if o.WithTies {
|
||||||
|
out.WriteString("ROWS WITH TIES")
|
||||||
|
} else {
|
||||||
|
out.WriteString("ROWS ONLY")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ClauseFor struct
|
// ClauseFor struct
|
||||||
type ClauseFor struct {
|
type ClauseFor struct {
|
||||||
Lock RowLock
|
Lock RowLock
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ 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
|
||||||
|
FETCH_FIRST(count IntegerExpression) fetchExpand
|
||||||
FOR(lock RowLock) SelectStatement
|
FOR(lock RowLock) SelectStatement
|
||||||
|
|
||||||
UNION(rhs SelectStatement) setStatement
|
UNION(rhs SelectStatement) setStatement
|
||||||
|
|
@ -72,9 +73,18 @@ func SELECT(projection Projection, projections ...Projection) SelectStatement {
|
||||||
|
|
||||||
func newSelectStatement(table ReadableTable, projections []Projection) SelectStatement {
|
func newSelectStatement(table ReadableTable, projections []Projection) SelectStatement {
|
||||||
newSelect := &selectStatementImpl{}
|
newSelect := &selectStatementImpl{}
|
||||||
newSelect.ExpressionStatement = jet.NewExpressionStatementImpl(Dialect, jet.SelectStatementType, newSelect, &newSelect.Select,
|
newSelect.ExpressionStatement = jet.NewExpressionStatementImpl(Dialect, jet.SelectStatementType, newSelect,
|
||||||
&newSelect.From, &newSelect.Where, &newSelect.GroupBy, &newSelect.Having, &newSelect.Window, &newSelect.OrderBy,
|
&newSelect.Select,
|
||||||
&newSelect.Limit, &newSelect.Offset, &newSelect.For)
|
&newSelect.From,
|
||||||
|
&newSelect.Where,
|
||||||
|
&newSelect.GroupBy,
|
||||||
|
&newSelect.Having,
|
||||||
|
&newSelect.Window,
|
||||||
|
&newSelect.OrderBy,
|
||||||
|
&newSelect.Limit,
|
||||||
|
&newSelect.Offset,
|
||||||
|
&newSelect.Fetch,
|
||||||
|
&newSelect.For)
|
||||||
|
|
||||||
newSelect.Select.ProjectionList = projections
|
newSelect.Select.ProjectionList = projections
|
||||||
if table != nil {
|
if table != nil {
|
||||||
|
|
@ -101,6 +111,7 @@ type selectStatementImpl struct {
|
||||||
OrderBy jet.ClauseOrderBy
|
OrderBy jet.ClauseOrderBy
|
||||||
Limit jet.ClauseLimit
|
Limit jet.ClauseLimit
|
||||||
Offset jet.ClauseOffset
|
Offset jet.ClauseOffset
|
||||||
|
Fetch jet.ClauseFetch
|
||||||
For jet.ClauseFor
|
For jet.ClauseFor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -150,6 +161,14 @@ func (s *selectStatementImpl) OFFSET(offset int64) SelectStatement {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *selectStatementImpl) FETCH_FIRST(count IntegerExpression) fetchExpand {
|
||||||
|
s.Fetch.Count = count
|
||||||
|
|
||||||
|
return fetchExpand{
|
||||||
|
selectStatement: s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *selectStatementImpl) FOR(lock RowLock) SelectStatement {
|
func (s *selectStatementImpl) FOR(lock RowLock) SelectStatement {
|
||||||
s.For.Lock = lock
|
s.For.Lock = lock
|
||||||
return s
|
return s
|
||||||
|
|
@ -188,3 +207,19 @@ func readableTablesToSerializerList(tables []ReadableTable) []jet.Serializer {
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fetchExpand struct {
|
||||||
|
selectStatement *selectStatementImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f fetchExpand) ROWS_ONLY() SelectStatement {
|
||||||
|
f.selectStatement.Fetch.WithTies = false
|
||||||
|
|
||||||
|
return f.selectStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f fetchExpand) ROWS_WITH_TIES() SelectStatement {
|
||||||
|
f.selectStatement.Fetch.WithTies = true
|
||||||
|
|
||||||
|
return f.selectStatement
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -236,6 +236,90 @@ LIMIT 12;
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFetchFirst(t *testing.T) {
|
||||||
|
|
||||||
|
t.Run("rows only", func(t *testing.T) {
|
||||||
|
stmt := SELECT(Actor.AllColumns).
|
||||||
|
FROM(Actor).
|
||||||
|
ORDER_BY(Actor.ActorID).
|
||||||
|
OFFSET(2).
|
||||||
|
FETCH_FIRST(Int(3)).ROWS_ONLY()
|
||||||
|
|
||||||
|
testutils.AssertStatementSql(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
|
||||||
|
OFFSET $1
|
||||||
|
FETCH FIRST $2 ROWS ONLY;
|
||||||
|
`)
|
||||||
|
|
||||||
|
var dest []model.Actor
|
||||||
|
|
||||||
|
err := stmt.Query(db, &dest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, dest, 3)
|
||||||
|
require.Equal(t, dest[0].ActorID, int32(3))
|
||||||
|
require.Equal(t, dest[2].ActorID, int32(5))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("rows with ties", func(t *testing.T) {
|
||||||
|
skipForCockroachDB(t) // ROWS_WITH_TIES is not supported on cockroachdb
|
||||||
|
|
||||||
|
stmt := SELECT(Actor.AllColumns).
|
||||||
|
FROM(Actor).
|
||||||
|
ORDER_BY(Actor.LastUpdate).
|
||||||
|
FETCH_FIRST(Int(3)).ROWS_WITH_TIES()
|
||||||
|
|
||||||
|
testutils.AssertStatementSql(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.last_update
|
||||||
|
FETCH FIRST $1 ROWS WITH TIES;
|
||||||
|
`)
|
||||||
|
|
||||||
|
var dest []model.Actor
|
||||||
|
|
||||||
|
err := stmt.Query(db, &dest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, dest, 200)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("complex expression", func(t *testing.T) {
|
||||||
|
stmt := SELECT(Actor.AllColumns).
|
||||||
|
FROM(Actor).
|
||||||
|
ORDER_BY(Actor.LastUpdate).
|
||||||
|
FETCH_FIRST(IntExp(
|
||||||
|
SELECT(MAX(Store.StoreID)).
|
||||||
|
FROM(Store),
|
||||||
|
)).ROWS_ONLY()
|
||||||
|
|
||||||
|
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.last_update
|
||||||
|
FETCH FIRST (
|
||||||
|
SELECT MAX(store.store_id)
|
||||||
|
FROM dvds.store
|
||||||
|
) ROWS ONLY;
|
||||||
|
`)
|
||||||
|
|
||||||
|
var dest []model.Actor
|
||||||
|
|
||||||
|
err := stmt.Query(db, &dest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, dest, 2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestJoinQueryStruct(t *testing.T) {
|
func TestJoinQueryStruct(t *testing.T) {
|
||||||
|
|
||||||
expectedSQL := `
|
expectedSQL := `
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue