performance improvement via string builder (#1036)

This commit is contained in:
Lunny Xiao 2018-07-11 08:59:00 +08:00 committed by GitHub
parent f16ce722ec
commit ad69f7d8f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 35 additions and 40 deletions

View File

@ -194,7 +194,7 @@ func (engine *Engine) Quote(value string) string {
} }
// QuoteTo quotes string and writes into the buffer // QuoteTo quotes string and writes into the buffer
func (engine *Engine) QuoteTo(buf *bytes.Buffer, value string) { func (engine *Engine) QuoteTo(buf *builder.StringBuilder, value string) {
if buf == nil { if buf == nil {
return return
} }

View File

@ -204,30 +204,28 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error
} }
cleanupProcessorsClosures(&session.beforeClosures) cleanupProcessorsClosures(&session.beforeClosures)
var sql = "INSERT INTO %s (%v%v%v) VALUES (%v)" var sql string
var statement string
if session.engine.dialect.DBType() == core.ORACLE { if session.engine.dialect.DBType() == core.ORACLE {
sql = "INSERT ALL INTO %s (%v%v%v) VALUES (%v) SELECT 1 FROM DUAL"
temp := fmt.Sprintf(") INTO %s (%v%v%v) VALUES (", temp := fmt.Sprintf(") INTO %s (%v%v%v) VALUES (",
session.engine.Quote(tableName), session.engine.Quote(tableName),
session.engine.QuoteStr(), session.engine.QuoteStr(),
strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()), strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()),
session.engine.QuoteStr()) session.engine.QuoteStr())
statement = fmt.Sprintf(sql, sql = fmt.Sprintf("INSERT ALL INTO %s (%v%v%v) VALUES (%v) SELECT 1 FROM DUAL",
session.engine.Quote(tableName), session.engine.Quote(tableName),
session.engine.QuoteStr(), session.engine.QuoteStr(),
strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()), strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()),
session.engine.QuoteStr(), session.engine.QuoteStr(),
strings.Join(colMultiPlaces, temp)) strings.Join(colMultiPlaces, temp))
} else { } else {
statement = fmt.Sprintf(sql, sql = fmt.Sprintf("INSERT INTO %s (%v%v%v) VALUES (%v)",
session.engine.Quote(tableName), session.engine.Quote(tableName),
session.engine.QuoteStr(), session.engine.QuoteStr(),
strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()), strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()),
session.engine.QuoteStr(), session.engine.QuoteStr(),
strings.Join(colMultiPlaces, "),(")) strings.Join(colMultiPlaces, "),("))
} }
res, err := session.exec(statement, args...) res, err := session.exec(sql, args...)
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@ -5,7 +5,6 @@
package xorm package xorm
import ( import (
"bytes"
"database/sql/driver" "database/sql/driver"
"encoding/json" "encoding/json"
"errors" "errors"
@ -706,10 +705,9 @@ func (statement *Statement) OrderBy(order string) *Statement {
// Desc generate `ORDER BY xx DESC` // Desc generate `ORDER BY xx DESC`
func (statement *Statement) Desc(colNames ...string) *Statement { func (statement *Statement) Desc(colNames ...string) *Statement {
var buf bytes.Buffer var buf builder.StringBuilder
fmt.Fprintf(&buf, statement.OrderStr)
if len(statement.OrderStr) > 0 { if len(statement.OrderStr) > 0 {
fmt.Fprint(&buf, ", ") fmt.Fprint(&buf, statement.OrderStr, ", ")
} }
newColNames := statement.col2NewColsWithQuote(colNames...) newColNames := statement.col2NewColsWithQuote(colNames...)
fmt.Fprintf(&buf, "%v DESC", strings.Join(newColNames, " DESC, ")) fmt.Fprintf(&buf, "%v DESC", strings.Join(newColNames, " DESC, "))
@ -719,10 +717,9 @@ func (statement *Statement) Desc(colNames ...string) *Statement {
// Asc provide asc order by query condition, the input parameters are columns. // Asc provide asc order by query condition, the input parameters are columns.
func (statement *Statement) Asc(colNames ...string) *Statement { func (statement *Statement) Asc(colNames ...string) *Statement {
var buf bytes.Buffer var buf builder.StringBuilder
fmt.Fprintf(&buf, statement.OrderStr)
if len(statement.OrderStr) > 0 { if len(statement.OrderStr) > 0 {
fmt.Fprint(&buf, ", ") fmt.Fprint(&buf, statement.OrderStr, ", ")
} }
newColNames := statement.col2NewColsWithQuote(colNames...) newColNames := statement.col2NewColsWithQuote(colNames...)
fmt.Fprintf(&buf, "%v ASC", strings.Join(newColNames, " ASC, ")) fmt.Fprintf(&buf, "%v ASC", strings.Join(newColNames, " ASC, "))
@ -749,7 +746,7 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
// Join The joinOP should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN // Join The joinOP should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
func (statement *Statement) Join(joinOP string, tablename interface{}, condition string, args ...interface{}) *Statement { func (statement *Statement) Join(joinOP string, tablename interface{}, condition string, args ...interface{}) *Statement {
var buf bytes.Buffer var buf builder.StringBuilder
if len(statement.JoinStr) > 0 { if len(statement.JoinStr) > 0 {
fmt.Fprintf(&buf, "%v %v JOIN ", statement.JoinStr, joinOP) fmt.Fprintf(&buf, "%v %v JOIN ", statement.JoinStr, joinOP)
} else { } else {
@ -783,11 +780,11 @@ func (statement *Statement) Unscoped() *Statement {
} }
func (statement *Statement) genColumnStr() string { func (statement *Statement) genColumnStr() string {
var buf bytes.Buffer
if statement.RefTable == nil { if statement.RefTable == nil {
return "" return ""
} }
var buf builder.StringBuilder
columns := statement.RefTable.Columns() columns := statement.RefTable.Columns()
for _, col := range columns { for _, col := range columns {
@ -1031,23 +1028,20 @@ func (statement *Statement) genSumSQL(bean interface{}, columns ...string) (stri
return sqlStr, append(statement.joinArgs, condArgs...), nil return sqlStr, append(statement.joinArgs, condArgs...), nil
} }
func (statement *Statement) genSelectSQL(columnStr, condSQL string, needLimit, needOrderBy bool) (a string, err error) { func (statement *Statement) genSelectSQL(columnStr, condSQL string, needLimit, needOrderBy bool) (string, error) {
var distinct string var (
distinct string
dialect = statement.Engine.Dialect()
quote = statement.Engine.Quote
fromStr = " FROM "
top, mssqlCondi, whereStr string
)
if statement.IsDistinct && !strings.HasPrefix(columnStr, "count") { if statement.IsDistinct && !strings.HasPrefix(columnStr, "count") {
distinct = "DISTINCT " distinct = "DISTINCT "
} }
var dialect = statement.Engine.Dialect()
var quote = statement.Engine.Quote
var top string
var mssqlCondi string
var buf bytes.Buffer
if len(condSQL) > 0 { if len(condSQL) > 0 {
fmt.Fprintf(&buf, " WHERE %v", condSQL) whereStr = " WHERE " + condSQL
} }
var whereStr = buf.String()
var fromStr = " FROM "
if dialect.DBType() == core.MSSQL && strings.Contains(statement.TableName(), "..") { if dialect.DBType() == core.MSSQL && strings.Contains(statement.TableName(), "..") {
fromStr += statement.TableName() fromStr += statement.TableName()
@ -1107,43 +1101,46 @@ func (statement *Statement) genSelectSQL(columnStr, condSQL string, needLimit, n
} }
} }
// !nashtsai! REVIEW Sprintf is considered slowest mean of string concatnation, better to work with builder pattern var buf builder.StringBuilder
a = fmt.Sprintf("SELECT %v%v%v%v%v", distinct, top, columnStr, fromStr, whereStr) fmt.Fprintf(&buf, "SELECT %v%v%v%v%v", distinct, top, columnStr, fromStr, whereStr)
if len(mssqlCondi) > 0 { if len(mssqlCondi) > 0 {
if len(whereStr) > 0 { if len(whereStr) > 0 {
a += " AND " + mssqlCondi fmt.Fprint(&buf, " AND ", mssqlCondi)
} else { } else {
a += " WHERE " + mssqlCondi fmt.Fprint(&buf, " WHERE ", mssqlCondi)
} }
} }
if statement.GroupByStr != "" { if statement.GroupByStr != "" {
a = fmt.Sprintf("%v GROUP BY %v", a, statement.GroupByStr) fmt.Fprint(&buf, " GROUP BY ", statement.GroupByStr)
} }
if statement.HavingStr != "" { if statement.HavingStr != "" {
a = fmt.Sprintf("%v %v", a, statement.HavingStr) fmt.Fprint(&buf, " ", statement.HavingStr)
} }
if needOrderBy && statement.OrderStr != "" { if needOrderBy && statement.OrderStr != "" {
a = fmt.Sprintf("%v ORDER BY %v", a, statement.OrderStr) fmt.Fprint(&buf, " ORDER BY ", statement.OrderStr)
} }
if needLimit { if needLimit {
if dialect.DBType() != core.MSSQL && dialect.DBType() != core.ORACLE { if dialect.DBType() != core.MSSQL && dialect.DBType() != core.ORACLE {
if statement.Start > 0 { if statement.Start > 0 {
a = fmt.Sprintf("%v LIMIT %v OFFSET %v", a, statement.LimitN, statement.Start) fmt.Fprintf(&buf, " LIMIT %v OFFSET %v", statement.LimitN, statement.Start)
} else if statement.LimitN > 0 { } else if statement.LimitN > 0 {
a = fmt.Sprintf("%v LIMIT %v", a, statement.LimitN) fmt.Fprint(&buf, " LIMIT ", statement.LimitN)
} }
} else if dialect.DBType() == core.ORACLE { } else if dialect.DBType() == core.ORACLE {
if statement.Start != 0 || statement.LimitN != 0 { if statement.Start != 0 || statement.LimitN != 0 {
a = fmt.Sprintf("SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d", columnStr, columnStr, a, statement.Start+statement.LimitN, statement.Start) oldString := buf.String()
buf.Reset()
fmt.Fprintf(&buf, "SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d",
columnStr, columnStr, oldString, statement.Start+statement.LimitN, statement.Start)
} }
} }
} }
if statement.IsForUpdate { if statement.IsForUpdate {
a = dialect.ForUpdateSql(a) return dialect.ForUpdateSql(buf.String()), nil
} }
return return buf.String(), nil
} }
func (statement *Statement) processIDParam() error { func (statement *Statement) processIDParam() error {