MSSQL rows limit/offset via TOP or OFFSET ... FETCH NEXT ... ROWS

This commit is contained in:
Sergey Kurt 2017-03-14 11:13:56 +03:00
parent e2f32e746e
commit cbdbe4891a
9 changed files with 54 additions and 6 deletions

View file

@ -118,6 +118,9 @@ func (m *MockDriver) PrimaryKeyInfo(schema, tableName string) (*bdb.PrimaryKey,
// UseLastInsertID returns a database mock LastInsertID compatibility flag
func (m *MockDriver) UseLastInsertID() bool { return false }
// UseTopClause returns a database mock SQL TOP clause compatibility flag
func (m *MockDriver) UseTopClause() bool { return false }
// Open mimics a database open call and returns nil for no error
func (m *MockDriver) Open() error { return nil }

View file

@ -64,8 +64,13 @@ func (m *MSSQLDriver) Close() {
m.dbConn.Close()
}
// UseLastInsertID returns false for postgres
// UseLastInsertID returns false for mssql
func (m *MSSQLDriver) UseLastInsertID() bool {
return false
}
// UseTopClause returns true to indicate MS SQL supports SQL TOP clause
func (m *MSSQLDriver) UseTopClause() bool {
return true
}

View file

@ -81,6 +81,11 @@ func (m *MySQLDriver) UseLastInsertID() bool {
return true
}
// UseTopClause returns false to indicate MySQL doesnt support SQL TOP clause
func (m *MySQLDriver) UseTopClause() bool {
return false
}
// TableNames connects to the postgres database and
// retrieves all table names from the information_schema where the
// table schema is public.

View file

@ -78,6 +78,11 @@ func (p *PostgresDriver) UseLastInsertID() bool {
return false
}
// UseTopClause returns false to indicate PSQL doesnt support SQL TOP clause
func (m *PostgresDriver) UseTopClause() bool {
return false
}
// TableNames connects to the postgres database and
// retrieves all table names from the information_schema where the
// table schema is schema. It uses a whitelist and blacklist.

View file

@ -18,6 +18,10 @@ type Interface interface {
// the sql.Exec result's LastInsertId
UseLastInsertID() bool
// UseTopClause should return true if the Database is capable of using
// the SQL TOP clause
UseTopClause() bool
// Open the database connection
Open() error
// Close the database connection

View file

@ -10,6 +10,7 @@ type testMockDriver struct{}
func (m testMockDriver) TranslateColumnType(c Column) Column { return c }
func (m testMockDriver) UseLastInsertID() bool { return false }
func (m testMockDriver) UseTopClause() bool { return false }
func (m testMockDriver) Open() error { return nil }
func (m testMockDriver) Close() {}

View file

@ -325,6 +325,7 @@ func (s *State) initDriver(driverName string) error {
s.Dialect.LQ = s.Driver.LeftQuote()
s.Dialect.RQ = s.Driver.RightQuote()
s.Dialect.IndexPlaceholders = s.Driver.IndexPlaceholders()
s.Dialect.UseTopClause = s.Driver.UseTopClause()
return nil
}

View file

@ -52,6 +52,9 @@ type Dialect struct {
// Bool flag indicating whether indexed
// placeholders ($1) are used, or ? placeholders.
IndexPlaceholders bool
// Bool flag indicating whether "TOP" or "LIMIT" clause
// must be used for rows limitation
UseTopClause bool
}
type where struct {

View file

@ -46,6 +46,12 @@ func buildSelectQuery(q *Query) (*bytes.Buffer, []interface{}) {
buf.WriteString("SELECT ")
if q.dialect.UseTopClause {
if q.limit != 0 && q.offset == 0 {
fmt.Fprintf(buf, " TOP (%d)", q.limit)
}
}
if q.count {
buf.WriteString("COUNT(")
}
@ -308,11 +314,26 @@ func writeModifiers(q *Query, buf *bytes.Buffer, args *[]interface{}) {
buf.WriteString(strings.Join(q.orderBy, ", "))
}
if q.limit != 0 {
fmt.Fprintf(buf, " LIMIT %d", q.limit)
}
if q.offset != 0 {
fmt.Fprintf(buf, " OFFSET %d", q.offset)
if !q.dialect.UseTopClause {
if q.limit != 0 {
fmt.Fprintf(buf, " LIMIT %d", q.limit)
}
if q.offset != 0 {
fmt.Fprintf(buf, " OFFSET %d", q.offset)
}
} else {
// From MS SQL 2012 and above: https://technet.microsoft.com/en-us/library/ms188385(v=sql.110).aspx
// ORDER BY ...
// OFFSET N ROWS
// FETCH NEXT M ROWS ONLY
if q.offset != 0 {
fmt.Fprintf(buf, " OFFSET %d", q.offset)
if q.limit != 0 {
fmt.Fprintf(buf, " FETCH NEXT %d ROWS ONLY", q.limit)
}
}
}
if len(q.forlock) != 0 {