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 // UseLastInsertID returns a database mock LastInsertID compatibility flag
func (m *MockDriver) UseLastInsertID() bool { return false } 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 // Open mimics a database open call and returns nil for no error
func (m *MockDriver) Open() error { return nil } func (m *MockDriver) Open() error { return nil }

View file

@ -64,8 +64,13 @@ func (m *MSSQLDriver) Close() {
m.dbConn.Close() m.dbConn.Close()
} }
// UseLastInsertID returns false for postgres // UseLastInsertID returns false for mssql
func (m *MSSQLDriver) UseLastInsertID() bool { 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 return true
} }

View file

@ -81,6 +81,11 @@ func (m *MySQLDriver) UseLastInsertID() bool {
return true 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 // TableNames connects to the postgres database and
// retrieves all table names from the information_schema where the // retrieves all table names from the information_schema where the
// table schema is public. // table schema is public.

View file

@ -78,6 +78,11 @@ func (p *PostgresDriver) UseLastInsertID() bool {
return false 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 // TableNames connects to the postgres database and
// retrieves all table names from the information_schema where the // retrieves all table names from the information_schema where the
// table schema is schema. It uses a whitelist and blacklist. // 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 // the sql.Exec result's LastInsertId
UseLastInsertID() bool UseLastInsertID() bool
// UseTopClause should return true if the Database is capable of using
// the SQL TOP clause
UseTopClause() bool
// Open the database connection // Open the database connection
Open() error Open() error
// Close the database connection // 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) TranslateColumnType(c Column) Column { return c }
func (m testMockDriver) UseLastInsertID() bool { return false } func (m testMockDriver) UseLastInsertID() bool { return false }
func (m testMockDriver) UseTopClause() bool { return false }
func (m testMockDriver) Open() error { return nil } func (m testMockDriver) Open() error { return nil }
func (m testMockDriver) Close() {} 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.LQ = s.Driver.LeftQuote()
s.Dialect.RQ = s.Driver.RightQuote() s.Dialect.RQ = s.Driver.RightQuote()
s.Dialect.IndexPlaceholders = s.Driver.IndexPlaceholders() s.Dialect.IndexPlaceholders = s.Driver.IndexPlaceholders()
s.Dialect.UseTopClause = s.Driver.UseTopClause()
return nil return nil
} }

View file

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

View file

@ -46,6 +46,12 @@ func buildSelectQuery(q *Query) (*bytes.Buffer, []interface{}) {
buf.WriteString("SELECT ") buf.WriteString("SELECT ")
if q.dialect.UseTopClause {
if q.limit != 0 && q.offset == 0 {
fmt.Fprintf(buf, " TOP (%d)", q.limit)
}
}
if q.count { if q.count {
buf.WriteString("COUNT(") buf.WriteString("COUNT(")
} }
@ -308,11 +314,26 @@ func writeModifiers(q *Query, buf *bytes.Buffer, args *[]interface{}) {
buf.WriteString(strings.Join(q.orderBy, ", ")) buf.WriteString(strings.Join(q.orderBy, ", "))
} }
if q.limit != 0 { if !q.dialect.UseTopClause {
fmt.Fprintf(buf, " LIMIT %d", q.limit) if q.limit != 0 {
} fmt.Fprintf(buf, " LIMIT %d", q.limit)
if q.offset != 0 { }
fmt.Fprintf(buf, " OFFSET %d", q.offset)
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 { if len(q.forlock) != 0 {