2017-01-03 05:31:47 +00:00
|
|
|
// Copyright 2016 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 (
|
2023-07-12 09:53:19 +00:00
|
|
|
"database/sql"
|
2017-01-03 05:31:47 +00:00
|
|
|
"errors"
|
|
|
|
"reflect"
|
2023-07-12 09:53:19 +00:00
|
|
|
"strings"
|
2017-01-03 05:31:47 +00:00
|
|
|
|
2019-06-17 05:38:13 +00:00
|
|
|
"xorm.io/builder"
|
2023-10-27 15:48:07 +00:00
|
|
|
|
2023-10-28 03:30:11 +00:00
|
|
|
"xorm.io/xorm/v2/internal/convert"
|
2023-10-27 14:27:46 +00:00
|
|
|
"xorm.io/xorm/v2/internal/utils"
|
|
|
|
"xorm.io/xorm/v2/schemas"
|
2017-01-03 05:31:47 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
tpStruct = iota
|
|
|
|
tpNonStruct
|
|
|
|
)
|
|
|
|
|
|
|
|
// Find retrieve records from table, condiBeans's non-empty fields
|
|
|
|
// are conditions. beans could be []Struct, []*Struct, map[int64]Struct
|
|
|
|
// map[int64]*Struct
|
2023-10-28 10:59:32 +00:00
|
|
|
func (session *Session) Find(rowsSlicePtr any, condiBean ...any) error {
|
2017-07-27 05:32:35 +00:00
|
|
|
if session.isAutoClose {
|
2017-01-03 05:31:47 +00:00
|
|
|
defer session.Close()
|
|
|
|
}
|
2017-08-27 07:50:43 +00:00
|
|
|
return session.find(rowsSlicePtr, condiBean...)
|
|
|
|
}
|
2017-01-03 05:31:47 +00:00
|
|
|
|
2018-02-07 13:06:13 +00:00
|
|
|
// FindAndCount find the results and also return the counts
|
2023-10-28 10:59:32 +00:00
|
|
|
func (session *Session) FindAndCount(rowsSlicePtr any, condiBean ...any) (int64, error) {
|
2018-02-07 13:06:13 +00:00
|
|
|
if session.isAutoClose {
|
|
|
|
defer session.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
session.autoResetStatement = false
|
|
|
|
err := session.find(rowsSlicePtr, condiBean...)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
|
|
|
|
if sliceValue.Kind() != reflect.Slice && sliceValue.Kind() != reflect.Map {
|
|
|
|
return 0, errors.New("needs a pointer to a slice or a map")
|
|
|
|
}
|
|
|
|
|
2024-07-11 09:47:51 +00:00
|
|
|
sliceValueLen := sliceValue.Len()
|
|
|
|
if session.statement.LimitN == nil {
|
|
|
|
return int64(sliceValueLen), nil
|
|
|
|
}
|
|
|
|
if *session.statement.LimitN > 0 && sliceValueLen == 0 && session.statement.Start <= 0 {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
if session.statement.Start >= 0 && sliceValueLen > 0 && sliceValueLen < *session.statement.LimitN {
|
|
|
|
return int64(session.statement.Start + sliceValueLen), nil
|
|
|
|
}
|
|
|
|
|
2018-02-07 13:06:13 +00:00
|
|
|
sliceElementType := sliceValue.Type().Elem()
|
2018-02-24 08:12:53 +00:00
|
|
|
if sliceElementType.Kind() == reflect.Ptr {
|
|
|
|
sliceElementType = sliceElementType.Elem()
|
|
|
|
}
|
2018-02-07 13:06:13 +00:00
|
|
|
session.autoResetStatement = true
|
|
|
|
|
2020-02-28 12:29:08 +00:00
|
|
|
if session.statement.SelectStr != "" {
|
|
|
|
session.statement.SelectStr = ""
|
2018-02-24 09:08:34 +00:00
|
|
|
}
|
2022-01-25 03:09:41 +00:00
|
|
|
if len(session.statement.ColumnMap) > 0 && !session.statement.IsDistinct {
|
2020-11-09 04:23:14 +00:00
|
|
|
session.statement.ColumnMap = []string{}
|
|
|
|
}
|
2022-05-31 03:00:28 +00:00
|
|
|
session.statement.ResetOrderBy()
|
2020-06-13 05:31:33 +00:00
|
|
|
if session.statement.LimitN != nil {
|
|
|
|
session.statement.LimitN = nil
|
|
|
|
}
|
|
|
|
if session.statement.Start > 0 {
|
|
|
|
session.statement.Start = 0
|
|
|
|
}
|
2018-02-24 09:08:34 +00:00
|
|
|
|
2020-03-24 00:48:54 +00:00
|
|
|
// session has stored the conditions so we use `unscoped` to avoid duplicated condition.
|
2021-12-14 01:00:35 +00:00
|
|
|
if sliceElementType.Kind() == reflect.Struct {
|
|
|
|
return session.Unscoped().Count(reflect.New(sliceElementType).Interface())
|
|
|
|
}
|
|
|
|
|
|
|
|
return session.Unscoped().Count()
|
2018-02-07 13:06:13 +00:00
|
|
|
}
|
|
|
|
|
2023-10-28 10:59:32 +00:00
|
|
|
func (session *Session) find(rowsSlicePtr any, condiBean ...any) error {
|
2019-07-30 08:35:36 +00:00
|
|
|
defer session.resetStatement()
|
2020-02-28 12:29:08 +00:00
|
|
|
if session.statement.LastError != nil {
|
|
|
|
return session.statement.LastError
|
2019-01-20 03:45:42 +00:00
|
|
|
}
|
|
|
|
|
2017-01-03 05:31:47 +00:00
|
|
|
sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
|
2022-05-31 03:00:28 +00:00
|
|
|
isSlice := sliceValue.Kind() == reflect.Slice
|
|
|
|
isMap := sliceValue.Kind() == reflect.Map
|
2020-03-06 08:55:12 +00:00
|
|
|
if !isSlice && !isMap {
|
2017-01-03 05:31:47 +00:00
|
|
|
return errors.New("needs a pointer to a slice or a map")
|
|
|
|
}
|
|
|
|
|
|
|
|
sliceElementType := sliceValue.Type().Elem()
|
|
|
|
|
2022-05-31 03:00:28 +00:00
|
|
|
tp := tpStruct
|
2017-07-27 05:32:35 +00:00
|
|
|
if session.statement.RefTable == nil {
|
2017-01-03 05:31:47 +00:00
|
|
|
if sliceElementType.Kind() == reflect.Ptr {
|
|
|
|
if sliceElementType.Elem().Kind() == reflect.Struct {
|
|
|
|
pv := reflect.New(sliceElementType.Elem())
|
2020-02-28 12:29:08 +00:00
|
|
|
if err := session.statement.SetRefValue(pv); err != nil {
|
2017-04-10 11:45:00 +00:00
|
|
|
return err
|
|
|
|
}
|
2017-01-03 05:31:47 +00:00
|
|
|
} else {
|
|
|
|
tp = tpNonStruct
|
|
|
|
}
|
|
|
|
} else if sliceElementType.Kind() == reflect.Struct {
|
|
|
|
pv := reflect.New(sliceElementType)
|
2020-02-28 12:29:08 +00:00
|
|
|
if err := session.statement.SetRefValue(pv); err != nil {
|
2017-04-10 11:45:00 +00:00
|
|
|
return err
|
|
|
|
}
|
2017-01-03 05:31:47 +00:00
|
|
|
} else {
|
|
|
|
tp = tpNonStruct
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-08 02:29:25 +00:00
|
|
|
var (
|
|
|
|
table = session.statement.RefTable
|
2023-07-12 02:01:56 +00:00
|
|
|
addedTableName = session.statement.NeedTableName()
|
2020-03-08 02:29:25 +00:00
|
|
|
autoCond builder.Cond
|
|
|
|
)
|
2017-01-03 05:31:47 +00:00
|
|
|
if tp == tpStruct {
|
2020-02-28 12:29:08 +00:00
|
|
|
if !session.statement.NoAutoCondition && len(condiBean) > 0 {
|
2020-04-26 11:34:05 +00:00
|
|
|
condTable, err := session.engine.tagParser.Parse(reflect.ValueOf(condiBean[0]))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
autoCond, err = session.statement.BuildConds(condTable, condiBean[0], true, true, false, true, addedTableName)
|
2017-01-03 05:31:47 +00:00
|
|
|
if err != nil {
|
2017-06-15 12:09:46 +00:00
|
|
|
return err
|
2017-01-03 05:31:47 +00:00
|
|
|
}
|
|
|
|
} else {
|
2020-02-28 12:29:08 +00:00
|
|
|
if col := table.DeletedColumn(); col != nil && !session.statement.GetUnscoped() { // tag "deleted" is enabled
|
|
|
|
autoCond = session.statement.CondDeleted(col)
|
2017-01-03 05:31:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-08 02:29:25 +00:00
|
|
|
// if it's a map with Cols but primary key not in column list, we still need the primary key
|
2020-03-06 08:55:12 +00:00
|
|
|
if isMap && !session.statement.ColumnMap.IsEmpty() {
|
|
|
|
for _, k := range session.statement.RefTable.PrimaryKeys {
|
|
|
|
session.statement.ColumnMap.Add(k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-28 12:29:08 +00:00
|
|
|
sqlStr, args, err := session.statement.GenFindSQL(autoCond)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2017-01-03 05:31:47 +00:00
|
|
|
}
|
|
|
|
|
2017-02-20 09:51:59 +00:00
|
|
|
return session.noCacheFind(table, sliceValue, sqlStr, args...)
|
2017-01-03 05:31:47 +00:00
|
|
|
}
|
|
|
|
|
2023-07-12 09:53:19 +00:00
|
|
|
type QueryedField struct {
|
|
|
|
FieldName string
|
|
|
|
LowerFieldName string
|
|
|
|
ColumnType *sql.ColumnType
|
|
|
|
TempIndex int
|
|
|
|
ColumnSchema *schemas.Column
|
|
|
|
}
|
|
|
|
|
|
|
|
type ColumnsSchema struct {
|
|
|
|
Fields []*QueryedField
|
|
|
|
FieldNames []string
|
|
|
|
Types []*sql.ColumnType
|
|
|
|
}
|
|
|
|
|
|
|
|
func (columnsSchema *ColumnsSchema) ParseTableSchema(table *schemas.Table) {
|
|
|
|
for _, field := range columnsSchema.Fields {
|
|
|
|
field.ColumnSchema = table.GetColumnIdx(field.FieldName, field.TempIndex)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ParseColumnsSchema(fieldNames []string, types []*sql.ColumnType, table *schemas.Table) *ColumnsSchema {
|
|
|
|
var columnsSchema ColumnsSchema
|
|
|
|
|
|
|
|
fields := make([]*QueryedField, 0, len(fieldNames))
|
|
|
|
|
|
|
|
for i, fieldName := range fieldNames {
|
|
|
|
field := &QueryedField{
|
|
|
|
FieldName: fieldName,
|
|
|
|
LowerFieldName: strings.ToLower(fieldName),
|
|
|
|
ColumnType: types[i],
|
|
|
|
}
|
|
|
|
fields = append(fields, field)
|
|
|
|
}
|
|
|
|
|
|
|
|
columnsSchema.Fields = fields
|
|
|
|
|
|
|
|
tempMap := make(map[string]int)
|
|
|
|
for _, field := range fields {
|
|
|
|
var idx int
|
|
|
|
var ok bool
|
|
|
|
|
|
|
|
if idx, ok = tempMap[field.LowerFieldName]; !ok {
|
|
|
|
idx = 0
|
|
|
|
} else {
|
|
|
|
idx++
|
|
|
|
}
|
|
|
|
|
|
|
|
tempMap[field.LowerFieldName] = idx
|
|
|
|
field.TempIndex = idx
|
|
|
|
}
|
|
|
|
|
|
|
|
if table != nil {
|
|
|
|
columnsSchema.ParseTableSchema(table)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &columnsSchema
|
|
|
|
}
|
|
|
|
|
2023-10-28 10:59:32 +00:00
|
|
|
func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect.Value, sqlStr string, args ...any) error {
|
2021-08-11 13:17:23 +00:00
|
|
|
elemType := containerValue.Type().Elem()
|
|
|
|
var isPointer bool
|
|
|
|
if elemType.Kind() == reflect.Ptr {
|
|
|
|
isPointer = true
|
|
|
|
elemType = elemType.Elem()
|
|
|
|
}
|
|
|
|
if elemType.Kind() == reflect.Ptr {
|
|
|
|
return errors.New("pointer to pointer is not supported")
|
|
|
|
}
|
|
|
|
|
2017-08-27 07:50:43 +00:00
|
|
|
rows, err := session.queryRows(sqlStr, args...)
|
2017-01-03 05:31:47 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-08-27 07:50:43 +00:00
|
|
|
defer rows.Close()
|
2017-01-03 05:31:47 +00:00
|
|
|
|
2017-08-27 07:50:43 +00:00
|
|
|
fields, err := rows.Columns()
|
2017-01-03 05:31:47 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-07-18 16:21:46 +00:00
|
|
|
types, err := rows.ColumnTypes()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-05-31 03:00:28 +00:00
|
|
|
newElemFunc := func(fields []string) reflect.Value {
|
2021-08-11 13:17:23 +00:00
|
|
|
return utils.New(elemType, len(fields), len(fields))
|
2017-01-03 05:31:47 +00:00
|
|
|
}
|
|
|
|
|
2020-02-24 08:53:18 +00:00
|
|
|
var containerValueSetFunc func(*reflect.Value, schemas.PK) error
|
2017-01-03 05:31:47 +00:00
|
|
|
|
2017-02-20 09:51:59 +00:00
|
|
|
if containerValue.Kind() == reflect.Slice {
|
2020-02-24 08:53:18 +00:00
|
|
|
containerValueSetFunc = func(newValue *reflect.Value, pk schemas.PK) error {
|
2017-02-21 11:08:50 +00:00
|
|
|
if isPointer {
|
|
|
|
containerValue.Set(reflect.Append(containerValue, newValue.Elem().Addr()))
|
|
|
|
} else {
|
|
|
|
containerValue.Set(reflect.Append(containerValue, newValue.Elem()))
|
2017-02-20 09:51:59 +00:00
|
|
|
}
|
2017-02-21 11:08:50 +00:00
|
|
|
return nil
|
2017-02-20 09:51:59 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
keyType := containerValue.Type().Key()
|
|
|
|
if len(table.PrimaryKeys) == 0 {
|
|
|
|
return errors.New("don't support multiple primary key's map has non-slice key type")
|
|
|
|
}
|
|
|
|
if len(table.PrimaryKeys) > 1 && keyType.Kind() != reflect.Slice {
|
|
|
|
return errors.New("don't support multiple primary key's map has non-slice key type")
|
|
|
|
}
|
|
|
|
|
2020-02-24 08:53:18 +00:00
|
|
|
containerValueSetFunc = func(newValue *reflect.Value, pk schemas.PK) error {
|
2017-02-21 11:08:50 +00:00
|
|
|
keyValue := reflect.New(keyType)
|
2021-08-11 13:17:23 +00:00
|
|
|
cols := table.PKColumns()
|
|
|
|
if len(cols) == 1 {
|
|
|
|
if err := convert.AssignValue(keyValue, pk[0]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
keyValue.Set(reflect.ValueOf(&pk))
|
2017-02-20 09:51:59 +00:00
|
|
|
}
|
2021-08-11 13:17:23 +00:00
|
|
|
|
2017-02-21 11:08:50 +00:00
|
|
|
if isPointer {
|
|
|
|
containerValue.SetMapIndex(keyValue.Elem(), newValue.Elem().Addr())
|
|
|
|
} else {
|
|
|
|
containerValue.SetMapIndex(keyValue.Elem(), newValue.Elem())
|
2017-01-03 05:31:47 +00:00
|
|
|
}
|
2017-02-21 11:08:50 +00:00
|
|
|
return nil
|
2017-01-03 05:31:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-21 11:08:50 +00:00
|
|
|
if elemType.Kind() == reflect.Struct {
|
2022-05-31 03:00:28 +00:00
|
|
|
newValue := newElemFunc(fields)
|
2021-08-11 13:17:23 +00:00
|
|
|
tb, err := session.engine.tagParser.ParseWithCache(newValue)
|
2017-04-02 10:02:47 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-07-12 09:53:19 +00:00
|
|
|
|
|
|
|
columnsSchema := ParseColumnsSchema(fields, types, tb)
|
|
|
|
|
|
|
|
err = session.rows2Beans(rows, columnsSchema, fields, types, tb, newElemFunc, containerValueSetFunc)
|
2017-09-30 01:26:13 +00:00
|
|
|
rows.Close()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return session.executeProcessors()
|
2017-01-03 05:31:47 +00:00
|
|
|
}
|
|
|
|
|
2017-08-27 07:50:43 +00:00
|
|
|
for rows.Next() {
|
2022-05-31 03:00:28 +00:00
|
|
|
newValue := newElemFunc(fields)
|
2017-01-03 05:31:47 +00:00
|
|
|
bean := newValue.Interface()
|
|
|
|
|
2017-02-21 11:08:50 +00:00
|
|
|
switch elemType.Kind() {
|
|
|
|
case reflect.Slice:
|
2022-01-06 11:43:06 +00:00
|
|
|
err = session.getSlice(rows, types, fields, bean)
|
2017-02-21 11:08:50 +00:00
|
|
|
case reflect.Map:
|
2022-01-06 11:43:06 +00:00
|
|
|
err = session.getMap(rows, types, fields, bean)
|
2017-02-21 11:08:50 +00:00
|
|
|
default:
|
2017-08-27 07:50:43 +00:00
|
|
|
err = rows.Scan(bean)
|
2017-02-21 11:08:50 +00:00
|
|
|
}
|
|
|
|
if err != nil {
|
2017-01-03 05:31:47 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-02-20 09:51:59 +00:00
|
|
|
if err := containerValueSetFunc(&newValue, nil); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-01-03 05:31:47 +00:00
|
|
|
}
|
2021-07-20 05:46:24 +00:00
|
|
|
return rows.Err()
|
2017-01-03 05:31:47 +00:00
|
|
|
}
|