2023-11-02 12:38:06 +01:00
|
|
|
// Copyright 2023 The Xorm Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package statements
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"xorm.io/builder"
|
|
|
|
"xorm.io/xorm/internal/utils"
|
2024-03-17 10:12:22 +01:00
|
|
|
"xorm.io/xorm/schemas"
|
2023-11-02 12:38:06 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
func (statement *Statement) writePagination(bw *builder.BytesWriter) error {
|
|
|
|
dbType := statement.dialect.URI().DBType
|
2024-03-17 10:12:22 +01:00
|
|
|
if dbType == schemas.MSSQL || dbType == schemas.ORACLE {
|
2023-11-02 12:38:06 +01:00
|
|
|
return statement.writeOffsetFetch(bw)
|
|
|
|
}
|
|
|
|
return statement.writeLimitOffset(bw)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (statement *Statement) writeLimitOffset(w builder.Writer) error {
|
|
|
|
if statement.Start > 0 {
|
|
|
|
if statement.LimitN != nil {
|
|
|
|
_, err := fmt.Fprintf(w, " LIMIT %v OFFSET %v", *statement.LimitN, statement.Start)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err := fmt.Fprintf(w, " OFFSET %v", statement.Start)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if statement.LimitN != nil {
|
|
|
|
_, err := fmt.Fprint(w, " LIMIT ", *statement.LimitN)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// no limit statement
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (statement *Statement) writeOffsetFetch(w builder.Writer) error {
|
|
|
|
if statement.LimitN != nil {
|
|
|
|
_, err := fmt.Fprintf(w, " OFFSET %v ROWS FETCH NEXT %v ROWS ONLY", statement.Start, *statement.LimitN)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if statement.Start > 0 {
|
|
|
|
_, err := fmt.Fprintf(w, " OFFSET %v ROWS", statement.Start)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (statement *Statement) writeWhereWithMssqlPagination(w *builder.BytesWriter) error {
|
2024-03-17 10:12:22 +01:00
|
|
|
if statement.cond.IsValid() {
|
|
|
|
if _, err := fmt.Fprint(w, " WHERE "); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := statement.cond.WriteTo(statement.QuoteReplacer(w)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-11-02 12:38:06 +01:00
|
|
|
}
|
2024-03-17 10:12:22 +01:00
|
|
|
|
2023-11-02 12:38:06 +01:00
|
|
|
return statement.writeMssqlPaginationCond(w)
|
|
|
|
}
|
|
|
|
|
|
|
|
// write subquery to implement limit offset
|
|
|
|
// (mssql legacy only)
|
|
|
|
func (statement *Statement) writeMssqlPaginationCond(w *builder.BytesWriter) error {
|
|
|
|
if statement.Start <= 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if statement.RefTable == nil {
|
|
|
|
return errors.New("unsupported query limit without reference table")
|
|
|
|
}
|
|
|
|
|
|
|
|
var column string
|
|
|
|
if len(statement.RefTable.PKColumns()) == 0 {
|
|
|
|
for _, index := range statement.RefTable.Indexes {
|
|
|
|
if len(index.Cols) == 1 {
|
|
|
|
column = index.Cols[0]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(column) == 0 {
|
|
|
|
column = statement.RefTable.ColumnsSeq()[0]
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
column = statement.RefTable.PKColumns()[0].Name
|
|
|
|
}
|
|
|
|
if statement.NeedTableName() {
|
|
|
|
if len(statement.TableAlias) > 0 {
|
|
|
|
column = fmt.Sprintf("%s.%s", statement.TableAlias, column)
|
|
|
|
} else {
|
|
|
|
column = fmt.Sprintf("%s.%s", statement.TableName(), column)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
subWriter := builder.NewWriter()
|
|
|
|
if _, err := fmt.Fprintf(subWriter, "(%s NOT IN (SELECT TOP %d %s",
|
|
|
|
column, statement.Start, column); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := statement.writeFrom(subWriter); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := statement.writeWhere(subWriter); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := statement.writeOrderBys(subWriter); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := statement.writeGroupBy(subWriter); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if _, err := fmt.Fprint(subWriter, "))"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-03-17 10:12:22 +01:00
|
|
|
if err := statement.writeWhereOrAnd(w, statement.cond.IsValid()); err != nil {
|
|
|
|
return err
|
2023-11-02 12:38:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return utils.WriteBuilder(w, subWriter)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (statement *Statement) writeOracleLimit(columnStr string) func(w *builder.BytesWriter) error {
|
|
|
|
return func(w *builder.BytesWriter) error {
|
|
|
|
if statement.LimitN == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
oldString := w.String()
|
|
|
|
w.Reset()
|
|
|
|
rawColStr := columnStr
|
|
|
|
if rawColStr == "*" {
|
|
|
|
rawColStr = "at.*"
|
|
|
|
}
|
|
|
|
_, err := fmt.Fprintf(w, "SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d",
|
|
|
|
columnStr, rawColStr, oldString, statement.Start+*statement.LimitN, statement.Start)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|