Merge branch 'mssqllimit' of https://github.com/go-xorm/xorm into mssqllimit

This commit is contained in:
Lunny Xiao 2014-05-06 11:26:25 +08:00
commit beecda2e23
12 changed files with 437 additions and 122 deletions

View File

@ -21,11 +21,11 @@ We appreciate any bug reports, but especially ones with self-contained
further) test cases. It's especially helpful if you can submit a pull
request with just the failing test case (you'll probably want to
pattern it after the tests in
[base_test.go](https://github.com/go-xorm/xorm/blob/master/base_test.go) AND
[benchmark_base_test.go](https://github.com/go-xorm/xorm/blob/master/benchmark_base_test.go).
[base.go](https://github.com/go-xorm/tests/blob/master/base.go) AND
[benchmark.go](https://github.com/go-xorm/tests/blob/master/benchmark.go).
If you implements a new database interface, you maybe need to add a <databasename>_test.go file.
For example, [mysql_test.go](https://github.com/go-xorm/xorm/blob/master/mysql_test.go)
For example, [mysql_test.go](https://github.com/go-xorm/tests/blob/master/mysql/mysql_test.go)
### New functionality

View File

@ -18,7 +18,7 @@ Xorm is a simple and powerful ORM for Go.
* Query Cache speed up
* Database Reverse support, See [Xorm Tool README](https://github.com/go-xorm/xorm/blob/master/xorm/README.md)
* Database Reverse support, See [Xorm Tool README](https://github.com/go-xorm/cmd/blob/master/README.md)
* Simple cascade loading support
@ -96,7 +96,7 @@ Or
* [GoWalker](http://gowalker.org/github.com/go-xorm/xorm)
* [Quick Start](https://github.com/go-xorm/xorm/blob/master/docs/QuickStartEn.md)
* [Quick Start](https://github.com/go-xorm/xorm/blob/master/docs/QuickStart.md)
# Cases
@ -123,13 +123,10 @@ Or
Please visit [Xorm on Google Groups](https://groups.google.com/forum/#!forum/xorm)
# Contributors
# Contributing
If you want to pull request, please see [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md)
* [Lunny](https://github.com/lunny)
* [Nashtsai](https://github.com/nashtsai)
# LICENSE
BSD License

View File

@ -94,7 +94,7 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
## 文档
* [快速开始](https://github.com/go-xorm/xorm/blob/master/docs/QuickStart.md)
* [快速开始](https://github.com/go-xorm/xorm/blob/master/docs/QuickStartCN.md)
* [GoWalker代码文档](http://gowalker.org/github.com/go-xorm/xorm)
@ -124,13 +124,10 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
请加入QQ群280360085 进行讨论。
# 贡献
## 贡献
如果您也想为Xorm贡献您的力量请查看 [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md)
* [Lunny](https://github.com/lunny)
* [Nashtsai](https://github.com/nashtsai)
## LICENSE
BSD License

View File

@ -58,7 +58,7 @@ engine, err = xorm.NewEngine("sqlite3", "./test.db")
defer engine.Close()
```
Generally, you can only create one engine. Engine supports run on go rutines.
You can create many engines for different databases.Generally, you just need create only one engine. Engine supports run on go routines.
xorm supports four drivers now:
@ -335,44 +335,43 @@ affected, err := engine.Insert(user, &questions)
Notice: If you want to use transaction on inserting, you should use session.Begin() before calling Insert.
<a name="60" id="60"></a>
## 5.Query and count
所有的查询条件不区分调用顺序但必须在调用GetFindCount这三个函数之前调用。同时需要注意的一点是在调用的参数中所有的字符字段名均为映射后的数据库的字段名而不是field的名字。
## 5. Chainable APIs
<a name="61" id="61"></a>
### 5.1.查询条件方法
### 5.1. Chainable APIs for Queries, Execusions and Aggregations
Queries and Aggregations is basically formed by using `Get`, `Find`, `Count` methods, with conjunction of following chainable APIs to form conditions, grouping and ordering:
查询和统计主要使用`Get`, `Find`, `Count`三个方法。在进行查询时可以使用多个方法来形成查询条件,条件函数如下:
* Id(int64)
传入一个PK字段的值作为查询条件
* Id([]interface{})
Primary Key lookup
* Where(string, …interface{})
和Where语句中的条件基本相同作为条件
As SQL conditional WHERE clause
* And(string, …interface{})
和Where函数中的条件基本相同作为条件
Conditional AND
* Or(string, …interface{})
和Where函数中的条件基本相同作为条件
Conditional OR
* Sql(string, …interface{})
执行指定的Sql语句并把结果映射到结构体
* Asc(…string)
指定字段名正序排序
Ascending ordering on 1 or more fields
* Desc(…string)
指定字段名逆序排序
Descending ordering on 1 or more fields
* OrderBy(string)
按照指定的顺序进行排序
Custom ordering
* In(string, …interface{})
某字段在一些值中
Conditional IN
* Cols(…string)
只查询或更新某些指定的字段默认是查询所有映射的字段或者根据Update的第一个参数来判断更新的字段。例如
Explicity specify query or update columns. e.g.,:
```Go
engine.Cols("age", "name").Find(&users)
// SELECT age, name FROM user
@ -380,12 +379,10 @@ engine.Cols("age", "name").Update(&user)
// UPDATE user SET age=? AND name=?
```
其中的参数"age", "name"也可以写成"age, name",两种写法均可
* Omit(...string)
和cols相反此函数指定排除某些指定的字段。注意此方法和Cols方法不可同时使用
Inverse function to Cols, to exclude specify query or update columns. Warning: Don't use with Cols()
```Go
engine.Cols("age").Update(&user)
engine.Omit("age").Update(&user)
// UPDATE user SET name = ? AND department = ?
```

View File

@ -23,7 +23,7 @@ xorm 快速入门
* [5.6.Count方法](#66)
* [5.7.Rows方法](#67)
* [6.更新数据](#70)
* [6.1.乐观锁](#71)
* [6.1.乐观锁](#71)
* [7.删除数据](#80)
* [8.执行SQL查询](#90)
* [9.执行SQL命令](#100)
@ -62,7 +62,7 @@ engine, err = xorm.NewEngine("sqlite3", "./test.db")
defer engine.Close()
```
一般如果只针对一个数据库进行操作只需要创建一个Engine即可。Engine支持在多GoRutine下使用。
你可以创建一个或多个engine, 不过一般如果操作一个数据库只需要创建一个Engine即可。Engine支持在多GoRutine下使用。
xorm当前支持五种驱动四个数据库如下
@ -419,7 +419,7 @@ engine.Cols("age", "name").Update(&user)
* Omit(...string)
和cols相反此函数指定排除某些指定的字段。注意此方法和Cols方法不可同时使用
```Go
engine.Cols("age").Update(&user)
engine.Omit("age").Update(&user)
// UPDATE user SET name = ? AND department = ?
```

109
engine.go
View File

@ -6,6 +6,7 @@ import (
"database/sql"
"errors"
"fmt"
"io"
"os"
"reflect"
"strconv"
@ -34,8 +35,7 @@ type Engine struct {
ShowErr bool
ShowDebug bool
ShowWarn bool
//Pool IConnectPool
//Filters []core.Filter
Logger ILogger // io.Writer
TZLocation *time.Location
}
@ -266,6 +266,80 @@ func (engine *Engine) DBMetas() ([]*core.Table, error) {
return tables, nil
}
func (engine *Engine) DumpAllToFile(fp string) error {
f, err := os.Create(fp)
if err != nil {
return err
}
defer f.Close()
return engine.DumpAll(f)
}
func (engine *Engine) DumpAll(w io.Writer) error {
tables, err := engine.DBMetas()
if err != nil {
return err
}
for _, table := range tables {
_, err = io.WriteString(w, engine.dialect.CreateTableSql(table, "", "", "")+"\n\n")
if err != nil {
return err
}
for _, index := range table.Indexes {
_, err = io.WriteString(w, engine.dialect.CreateIndexSql(table.Name, index)+"\n\n")
if err != nil {
return err
}
}
rows, err := engine.DB().Query("SELECT * FROM " + engine.Quote(table.Name))
if err != nil {
return err
}
cols, err := rows.Columns()
if err != nil {
return err
}
if len(cols) == 0 {
continue
}
for rows.Next() {
dest := make([]interface{}, len(cols))
err = rows.ScanSlice(&dest)
if err != nil {
return err
}
_, err = io.WriteString(w, "INSERT INTO "+engine.Quote(table.Name)+" ("+engine.Quote(strings.Join(cols, engine.Quote(", ")))+") VALUES (")
if err != nil {
return err
}
var temp string
for i, d := range dest {
col := table.GetColumn(cols[i])
if d == nil {
temp += ", NULL"
} else if col.SQLType.IsText() || col.SQLType.IsTime() {
var v = fmt.Sprintf("%s", d)
temp += ", '" + strings.Replace(v, "'", "''", -1) + "'"
} else if col.SQLType.IsBlob() /*reflect.TypeOf(d).Kind() == reflect.Slice*/ {
temp += fmt.Sprintf(", %s", engine.dialect.FormatBytes(d.([]byte)))
} else {
temp += fmt.Sprintf(", %s", d)
}
}
_, err = io.WriteString(w, temp[2:]+");\n\n")
if err != nil {
return err
}
}
}
return nil
}
// use cascade or not
func (engine *Engine) Cascade(trueOrFalse ...bool) *Session {
session := engine.NewSession()
@ -456,15 +530,6 @@ func (engine *Engine) autoMap(bean interface{}) *core.Table {
return engine.autoMapType(v)
}
/*func (engine *Engine) mapType(t reflect.Type) *core.Table {
return mappingTable(t, engine.TableMapper, engine.ColumnMapper, engine.dialect, engine.TagIdentifier)
}*/
/*
func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapper, dialect core.Dialect, tagId string) *core.Table {
table := core.NewEmptyTable()
table.Name = tableMapper.Obj2Table(t.Name())
*/
func addIndex(indexName string, table *core.Table, col *core.Column, indexType int) {
if index, ok := table.Indexes[indexName]; ok {
index.AddColumn(col.Name)
@ -524,17 +589,19 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table {
if tags[0] == "-" {
continue
}
if (strings.ToUpper(tags[0]) == "EXTENDS") &&
(fieldType.Kind() == reflect.Struct) {
if strings.ToUpper(tags[0]) == "EXTENDS" {
fieldValue = reflect.Indirect(fieldValue)
if fieldValue.Kind() == reflect.Struct {
//parentTable := mappingTable(fieldType, tableMapper, colMapper, dialect, tagId)
parentTable := engine.mapType(fieldValue)
for _, col := range parentTable.Columns() {
col.FieldName = fmt.Sprintf("%v.%v", fieldValue.Type().Name(), col.FieldName)
table.AddColumn(col)
}
//parentTable := mappingTable(fieldType, tableMapper, colMapper, dialect, tagId)
parentTable := engine.mapType(fieldValue)
for _, col := range parentTable.Columns() {
col.FieldName = fmt.Sprintf("%v.%v", fieldType.Name(), col.FieldName)
table.AddColumn(col)
continue
}
continue
//TODO: warning
}
indexNames := make(map[string]int)
@ -701,7 +768,7 @@ func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) {
session := engine.NewSession()
defer session.Close()
rows, err := session.Count(bean)
return rows > 0, err
return rows == 0, err
}
// If a table is exist

34
examples/tables.go Normal file
View File

@ -0,0 +1,34 @@
package main
import (
"fmt"
"os"
"github.com/go-xorm/xorm"
_ "github.com/mattn/go-sqlite3"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("need db path")
return
}
orm, err := xorm.NewEngine("sqlite3", os.Args[1])
if err != nil {
fmt.Println(err)
return
}
defer orm.Close()
orm.ShowSQL = true
tables, err := orm.DBMetas()
if err != nil {
fmt.Println(err)
return
}
for _, table := range tables {
fmt.Println(table.Name)
}
}

View File

@ -223,19 +223,6 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*core.Index, error) {
return indexes, nil
}
// PgSeqFilter filter SQL replace ?, ? ... to :1, :2 ...
type OracleSeqFilter struct {
}
func (s *OracleSeqFilter) Do(sql string, dialect core.Dialect, table *core.Table) string {
counts := strings.Count(sql, "?")
for i := 1; i <= counts; i++ {
newstr := ":" + fmt.Sprintf("%v", i)
sql = strings.Replace(sql, "?", newstr, 1)
}
return sql
}
func (db *oracle) Filters() []core.Filter {
return []core.Filter{&core.QuoteFilter{}, &OracleSeqFilter{}, &core.IdFilter{}}
return []core.Filter{&core.QuoteFilter{}, &core.SeqFilter{":", 1}, &core.IdFilter{}}
}

View File

@ -15,6 +15,10 @@ type BeforeDeleteProcessor interface {
BeforeDelete()
}
type BeforeSetProcessor interface {
BeforeSet(string, Cell)
}
// !nashtsai! TODO enable BeforeValidateProcessor when xorm start to support validations
//// Executed before an object is validated
//type BeforeValidateProcessor interface {

View File

@ -883,7 +883,6 @@ func (session *Session) Rows(bean interface{}) (*Rows, error) {
// are conditions. beans could be []Struct, []*Struct, map[int64]Struct
// map[int64]*Struct
func (session *Session) Iterate(bean interface{}, fun IterFunc) error {
rows, err := session.Rows(bean)
if err != nil {
return err
@ -982,24 +981,6 @@ func (session *Session) Get(bean interface{}) (bool, error) {
} else {
return false, nil
}
// resultsSlice, err := session.query(sqlStr, args...)
// if err != nil {
// return false, err
// }
// if len(resultsSlice) < 1 {
// return false, nil
// }
// err = session.scanMapIntoStruct(bean, resultsSlice[0])
// if err != nil {
// return true, err
// }
// if len(resultsSlice) == 1 {
// return true, nil
// } else {
// return true, errors.New("More than one record")
// }
}
// Count counts the records. bean's non-empty fields
@ -1441,6 +1422,8 @@ func (session *Session) getField(dataStruct *reflect.Value, key string, table *c
return fieldValue
}
type Cell *interface{}
func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}) error {
dataStruct := rValue(bean)
if dataStruct.Kind() != reflect.Struct {
@ -1449,18 +1432,24 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
table := session.Engine.autoMapType(dataStruct)
scanResultContainers := make([]interface{}, len(fields))
scanResults := make([]interface{}, len(fields))
for i := 0; i < len(fields); i++ {
var scanResultContainer interface{}
scanResultContainers[i] = &scanResultContainer
var cell interface{}
scanResults[i] = &cell
}
if err := rows.Scan(scanResultContainers...); err != nil {
if err := rows.Scan(scanResults...); err != nil {
return err
}
b, hasBeforeSet := bean.(BeforeSetProcessor)
for ii, key := range fields {
if hasBeforeSet {
b.BeforeSet(fields[ii], Cell(scanResults[ii].(*interface{})))
}
if fieldValue := session.getField(&dataStruct, key, table); fieldValue != nil {
rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii]))
rawValue := reflect.Indirect(reflect.ValueOf(scanResults[ii]))
//if row is null then ignore
if rawValue.Interface() == nil {
@ -1468,7 +1457,18 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
continue
}
if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
if fieldValue.CanAddr() {
if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
if data, err := value2Bytes(&rawValue); err == nil {
structConvert.FromDB(data)
} else {
session.Engine.LogError(err)
}
continue
}
}
if structConvert, ok := fieldValue.Interface().(core.Conversion); ok {
if data, err := value2Bytes(&rawValue); err == nil {
structConvert.FromDB(data)
} else {
@ -2451,6 +2451,16 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
}
}
}
if fieldConvert, ok := fieldValue.Interface().(core.Conversion); ok {
data, err := fieldConvert.ToDB()
if err != nil {
return 0, err
} else {
return string(data), nil
}
}
fieldType := fieldValue.Type()
k := fieldType.Kind()
if k == reflect.Ptr {
@ -2470,11 +2480,6 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
switch k {
case reflect.Bool:
return fieldValue.Bool(), nil
/*if fieldValue.Bool() {
return 1, nil
} else {
return 0, nil
}*/
case reflect.String:
return fieldValue.String(), nil
case reflect.Struct:
@ -2947,7 +2952,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
session.Statement.RefTable = table
if session.Statement.ColumnStr == "" {
colNames, args = buildConditions(session.Engine, table, bean, false, false,
colNames, args = buildUpdates(session.Engine, table, bean, false, false,
false, false, session.Statement.allUseBool, session.Statement.useAllCols,
session.Statement.mustColumnMap)
} else {
@ -3314,7 +3319,7 @@ func genCols(table *core.Table, session *Session, bean interface{}, useCol bool,
}
if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime {
args = append(args, time.Now())
args = append(args, session.Engine.NowTime(col.SQLType.Name))
} else if col.IsVersion && session.Statement.checkVersion {
args = append(args, 1)
} else {

View File

@ -1,6 +1,7 @@
package xorm
import (
"fmt"
"strings"
"github.com/go-xorm/core"
@ -44,6 +45,10 @@ func (db *sqlite3) SqlType(c *core.Column) string {
}
}
func (db *sqlite3) FormatBytes(bs []byte) string {
return fmt.Sprintf("X'%x'", bs)
}
func (db *sqlite3) SupportInsertMany() bool {
return true
}

View File

@ -253,6 +253,197 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
return results
}*/
// Auto generating conditions according a struct
func buildUpdates(engine *Engine, table *core.Table, bean interface{},
includeVersion bool, includeUpdated bool, includeNil bool,
includeAutoIncr bool, allUseBool bool, useAllCols bool,
mustColumnMap map[string]bool) ([]string, []interface{}) {
colNames := make([]string, 0)
var args = make([]interface{}, 0)
for _, col := range table.Columns() {
if !includeVersion && col.IsVersion {
continue
}
if col.IsCreated {
continue
}
if !includeUpdated && col.IsUpdated {
continue
}
if !includeAutoIncr && col.IsAutoIncrement {
continue
}
//
//fmt.Println(engine.dialect.DBType(), Text)
if engine.dialect.DBType() == core.MSSQL && col.SQLType.Name == core.Text {
continue
}
fieldValuePtr, err := col.ValueOf(bean)
if err != nil {
engine.LogError(err)
continue
}
fieldValue := *fieldValuePtr
fieldType := reflect.TypeOf(fieldValue.Interface())
requiredField := useAllCols
if b, ok := mustColumnMap[strings.ToLower(col.Name)]; ok {
if b {
requiredField = true
} else {
continue
}
}
var val interface{}
if fieldValue.CanAddr() {
if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
data, err := structConvert.ToDB()
if err != nil {
engine.LogError(err)
} else {
val = data
}
continue
}
}
if structConvert, ok := fieldValue.Interface().(core.Conversion); ok {
data, err := structConvert.ToDB()
if err != nil {
engine.LogError(err)
} else {
val = data
}
continue
}
if fieldType.Kind() == reflect.Ptr {
if fieldValue.IsNil() {
if includeNil {
args = append(args, nil)
colNames = append(colNames, fmt.Sprintf("%v=?", engine.Quote(col.Name)))
}
continue
} else if !fieldValue.IsValid() {
continue
} else {
// dereference ptr type to instance type
fieldValue = fieldValue.Elem()
fieldType = reflect.TypeOf(fieldValue.Interface())
requiredField = true
}
}
switch fieldType.Kind() {
case reflect.Bool:
if allUseBool || requiredField {
val = fieldValue.Interface()
} else {
// if a bool in a struct, it will not be as a condition because it default is false,
// please use Where() instead
continue
}
case reflect.String:
if !requiredField && fieldValue.String() == "" {
continue
}
// for MyString, should convert to string or panic
if fieldType.String() != reflect.String.String() {
val = fieldValue.String()
} else {
val = fieldValue.Interface()
}
case reflect.Int8, reflect.Int16, reflect.Int, reflect.Int32, reflect.Int64:
if !requiredField && fieldValue.Int() == 0 {
continue
}
val = fieldValue.Interface()
case reflect.Float32, reflect.Float64:
if !requiredField && fieldValue.Float() == 0.0 {
continue
}
val = fieldValue.Interface()
case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64:
if !requiredField && fieldValue.Uint() == 0 {
continue
}
val = fieldValue.Interface()
case reflect.Struct:
if fieldType == reflect.TypeOf(time.Now()) {
t := fieldValue.Interface().(time.Time)
if !requiredField && (t.IsZero() || !fieldValue.IsValid()) {
continue
}
val = engine.FormatTime(col.SQLType.Name, t)
//fmt.Println("-------", t, val, col.Name)
} else {
engine.autoMapType(fieldValue)
if table, ok := engine.Tables[fieldValue.Type()]; ok {
if len(table.PrimaryKeys) == 1 {
pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName)
if pkField.Int() != 0 {
val = pkField.Interface()
} else {
continue
}
} else {
//TODO: how to handler?
}
} else {
val = fieldValue.Interface()
}
}
case reflect.Array, reflect.Slice, reflect.Map:
if fieldValue == reflect.Zero(fieldType) {
continue
}
if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 {
continue
}
if col.SQLType.IsText() {
bytes, err := json.Marshal(fieldValue.Interface())
if err != nil {
engine.LogError(err)
continue
}
val = string(bytes)
} else if col.SQLType.IsBlob() {
var bytes []byte
var err error
if (fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice) &&
fieldType.Elem().Kind() == reflect.Uint8 {
if fieldValue.Len() > 0 {
val = fieldValue.Bytes()
} else {
continue
}
} else {
bytes, err = json.Marshal(fieldValue.Interface())
if err != nil {
engine.LogError(err)
continue
}
val = bytes
}
} else {
continue
}
default:
val = fieldValue.Interface()
}
args = append(args, val)
colNames = append(colNames, fmt.Sprintf("%v=?", engine.Quote(col.Name)))
}
return colNames, args
}
// Auto generating conditions according a struct
func buildConditions(engine *Engine, table *core.Table, bean interface{},
includeVersion bool, includeUpdated bool, includeNil bool,
@ -283,8 +474,11 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
}
fieldValue := *fieldValuePtr
fieldType := reflect.TypeOf(fieldValue.Interface())
if fieldValue.Interface() == nil {
continue
}
fieldType := reflect.TypeOf(fieldValue.Interface())
requiredField := useAllCols
if b, ok := mustColumnMap[strings.ToLower(col.Name)]; ok {
if b {
@ -353,7 +547,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
continue
}
val = engine.FormatTime(col.SQLType.Name, t)
fmt.Println("-------", t, val, col.Name)
//fmt.Println("-------", t, val, col.Name)
} else {
engine.autoMapType(fieldValue)
if table, ok := engine.Tables[fieldValue.Type()]; ok {
@ -762,11 +956,12 @@ func (statement *Statement) genCountSql(bean interface{}) (string, []interface{}
statement.ConditionStr = strings.Join(colNames, " AND ")
statement.BeanArgs = args
// count(index fieldname) > count(0) > count(*)
var id string = "0"
// for compitable on kinds of database, just use *
/*var id string = "0"
if len(table.PrimaryKeys) == 1 {
id = statement.Engine.Quote(table.PrimaryKeys[0])
}
return statement.genSelectSql(fmt.Sprintf("COUNT(%v) AS %v", id, statement.Engine.Quote("total"))), append(statement.Params, statement.BeanArgs...)
id = statement.Engine.Quote(statement.TableName()) + "." + statement.Engine.Quote(table.PrimaryKeys[0])
}*/
return statement.genSelectSql(fmt.Sprintf("COUNT(*) AS %v", statement.Engine.Quote("total"))), append(statement.Params, statement.BeanArgs...)
}
func (statement *Statement) genSelectSql(columnStr string) (a string) {
@ -779,20 +974,52 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) {
distinct = "DISTINCT "
}
// !nashtsai! REVIEW Sprintf is considered slowest mean of string concatnation, better to work with builder pattern
a = fmt.Sprintf("SELECT %v%v FROM %v", distinct, columnStr,
statement.Engine.Quote(statement.TableName()))
if statement.JoinStr != "" {
a = fmt.Sprintf("%v %v", a, statement.JoinStr)
var top string
var mssqlCondi string
var orderBy string
if statement.OrderStr != "" {
orderBy = fmt.Sprintf(" ORDER BY %v", statement.OrderStr)
}
statement.processIdParam()
var whereStr string
if statement.WhereStr != "" {
a = fmt.Sprintf("%v WHERE %v", a, statement.WhereStr)
whereStr = fmt.Sprintf(" WHERE %v", statement.WhereStr)
if statement.ConditionStr != "" {
a = fmt.Sprintf("%v AND %v", a, statement.ConditionStr)
whereStr = fmt.Sprintf("%v AND %v", whereStr, statement.ConditionStr)
}
} else if statement.ConditionStr != "" {
a = fmt.Sprintf("%v WHERE %v", a, statement.ConditionStr)
whereStr = fmt.Sprintf(" WHERE %v", statement.ConditionStr)
}
var fromStr string = " FROM " + statement.Engine.Quote(statement.TableName())
if statement.JoinStr != "" {
fromStr = fmt.Sprintf("%v %v", fromStr, statement.JoinStr)
}
if statement.Engine.dialect.DBType() == core.MSSQL {
top = fmt.Sprintf(" TOP %d", statement.LimitN)
if statement.Start > 0 {
var column string = "(id)"
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]
}
}
mssqlCondi = fmt.Sprintf("(%s NOT IN (SELECT TOP %d %s%s%s%s))",
column, statement.Start, column, fromStr, whereStr, orderBy)
}
}
// !nashtsai! REVIEW Sprintf is considered slowest mean of string concatnation, better to work with builder pattern
a = fmt.Sprintf("SELECT %v%v%v%v%v", top, distinct, columnStr,
fromStr, whereStr)
if mssqlCondi != "" {
a += " AND " + mssqlCondi
}
if statement.GroupByStr != "" {
@ -810,11 +1037,6 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) {
} else if statement.LimitN > 0 {
a = fmt.Sprintf("%v LIMIT %v", a, statement.LimitN)
}
} else {
//TODO: for mssql, should handler limit.
/*SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY id desc) as row FROM "userinfo"
) a WHERE row > [start] and row <= [start+limit] order by id desc*/
}
return