2016-11-03 23:16:01 +01:00
// Copyright 2015 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 xorm
import (
"database/sql"
2020-03-22 23:12:55 +08:00
"errors"
2016-11-03 23:16:01 +01:00
"fmt"
"reflect"
2020-03-22 23:12:55 +08:00
"xorm.io/builder"
"xorm.io/xorm/core"
"xorm.io/xorm/internal/utils"
2016-11-03 23:16:01 +01:00
)
// Rows rows wrapper a rows to
type Rows struct {
2017-04-06 18:47:25 -07:00
session * Session
rows * core . Rows
beanType reflect . Type
lastError error
2016-11-03 23:16:01 +01:00
}
func newRows ( session * Session , bean interface { } ) ( * Rows , error ) {
rows := new ( Rows )
rows . session = session
rows . beanType = reflect . Indirect ( reflect . ValueOf ( bean ) ) . Type ( )
var sqlStr string
var args [ ] interface { }
2017-08-22 19:39:52 +08:00
var err error
2016-11-03 23:16:01 +01:00
2020-03-22 23:12:55 +08:00
beanValue := reflect . ValueOf ( bean )
if beanValue . Kind ( ) != reflect . Ptr {
return nil , errors . New ( "needs a pointer to a value" )
} else if beanValue . Elem ( ) . Kind ( ) == reflect . Ptr {
return nil , errors . New ( "a pointer to a pointer is not allowed" )
}
if err = rows . session . statement . SetRefBean ( bean ) ; err != nil {
2017-05-02 03:50:33 +03:00
return nil , err
}
2017-08-22 19:39:52 +08:00
if len ( session . statement . TableName ( ) ) <= 0 {
2016-11-03 23:16:01 +01:00
return nil , ErrTableNotFound
}
2017-08-22 19:39:52 +08:00
if rows . session . statement . RawSQL == "" {
2020-03-22 23:12:55 +08:00
var autoCond builder . Cond
var addedTableName = ( len ( session . statement . JoinStr ) > 0 )
var table = rows . session . statement . RefTable
if ! session . statement . NoAutoCondition {
var err error
autoCond , err = session . statement . BuildConds ( table , bean , true , true , false , true , addedTableName )
if err != nil {
return nil , err
}
} else {
// !oinume! Add "<col> IS NULL" to WHERE whatever condiBean is given.
// See https://gitea.com/xorm/xorm/issues/179
if col := table . DeletedColumn ( ) ; col != nil && ! session . statement . GetUnscoped ( ) { // tag "deleted" is enabled
var colName = session . engine . Quote ( col . Name )
if addedTableName {
var nm = session . statement . TableName ( )
if len ( session . statement . TableAlias ) > 0 {
nm = session . statement . TableAlias
}
colName = session . engine . Quote ( nm ) + "." + colName
}
autoCond = session . statement . CondDeleted ( col )
}
}
sqlStr , args , err = rows . session . statement . GenFindSQL ( autoCond )
2017-08-22 19:39:52 +08:00
if err != nil {
return nil , err
}
2016-11-03 23:16:01 +01:00
} else {
2020-03-22 23:12:55 +08:00
sqlStr = rows . session . statement . GenRawSQL ( )
2017-08-22 19:39:52 +08:00
args = rows . session . statement . RawParams
2016-11-03 23:16:01 +01:00
}
2017-09-13 13:18:22 +08:00
rows . rows , err = rows . session . queryRows ( sqlStr , args ... )
if err != nil {
rows . lastError = err
rows . Close ( )
return nil , err
2016-11-03 23:16:01 +01:00
}
return rows , nil
}
// Next move cursor to next record, return false if end has reached
func ( rows * Rows ) Next ( ) bool {
if rows . lastError == nil && rows . rows != nil {
hasNext := rows . rows . Next ( )
if ! hasNext {
rows . lastError = sql . ErrNoRows
}
return hasNext
}
return false
}
// Err returns the error, if any, that was encountered during iteration. Err may be called after an explicit or implicit Close.
func ( rows * Rows ) Err ( ) error {
return rows . lastError
}
// Scan row record to bean properties
func ( rows * Rows ) Scan ( bean interface { } ) error {
if rows . lastError != nil {
return rows . lastError
}
2019-06-23 23:22:43 +08:00
if reflect . Indirect ( reflect . ValueOf ( bean ) ) . Type ( ) != rows . beanType {
2016-11-03 23:16:01 +01:00
return fmt . Errorf ( "scan arg is incompatible type to [%v]" , rows . beanType )
}
2020-03-22 23:12:55 +08:00
if err := rows . session . statement . SetRefBean ( bean ) ; err != nil {
2017-08-22 19:39:52 +08:00
return err
}
2019-06-23 23:22:43 +08:00
fields , err := rows . rows . Columns ( )
if err != nil {
return err
}
scanResults , err := rows . session . row2Slice ( rows . rows , fields , bean )
2017-08-22 19:39:52 +08:00
if err != nil {
2017-05-02 03:50:33 +03:00
return err
}
2017-04-06 18:47:25 -07:00
2020-03-22 23:12:55 +08:00
dataStruct := utils . ReflectValue ( bean )
2019-06-23 23:22:43 +08:00
_ , err = rows . session . slice2Bean ( scanResults , fields , bean , & dataStruct , rows . session . statement . RefTable )
2017-10-02 00:52:35 +08:00
if err != nil {
return err
}
return rows . session . executeProcessors ( )
2016-11-03 23:16:01 +01:00
}
// Close session if session.IsAutoClose is true, and claimed any opened resources
func ( rows * Rows ) Close ( ) error {
2017-08-22 19:39:52 +08:00
if rows . session . isAutoClose {
2016-11-03 23:16:01 +01:00
defer rows . session . Close ( )
}
2019-06-23 23:22:43 +08:00
if rows . rows != nil {
return rows . rows . Close ( )
2016-11-03 23:16:01 +01:00
}
2019-06-23 23:22:43 +08:00
2016-11-03 23:16:01 +01:00
return rows . lastError
}