2017-09-21 21:43:27 +02:00
|
|
|
// Copyright (C) 2017 ScyllaDB
|
|
|
|
|
// Use of this source code is governed by a ALv2-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
2017-07-25 11:10:19 +02:00
|
|
|
package gocqlx
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"errors"
|
2017-07-25 12:25:59 +02:00
|
|
|
"fmt"
|
|
|
|
|
"reflect"
|
2017-07-25 11:10:19 +02:00
|
|
|
"strconv"
|
2025-09-08 06:25:00 -04:00
|
|
|
"time"
|
2017-07-25 12:25:59 +02:00
|
|
|
|
2025-11-20 16:09:09 +01:00
|
|
|
gocql "github.com/apache/cassandra-gocql-driver/v2"
|
2019-01-25 15:20:53 +01:00
|
|
|
"github.com/scylladb/go-reflectx"
|
2017-07-25 11:10:19 +02:00
|
|
|
)
|
|
|
|
|
|
2020-04-20 18:00:01 +02:00
|
|
|
// CompileNamedQueryString translates query with named parameters in a form
|
|
|
|
|
// ':<identifier>' to query with '?' placeholders and a list of parameter names.
|
|
|
|
|
// If you need to use ':' in a query, i.e. with maps or UDTs use '::' instead.
|
|
|
|
|
func CompileNamedQueryString(qs string) (stmt string, names []string, err error) {
|
|
|
|
|
return CompileNamedQuery([]byte(qs))
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-23 15:44:42 +02:00
|
|
|
// CompileNamedQuery translates query with named parameters in a form
|
|
|
|
|
// ':<identifier>' to query with '?' placeholders and a list of parameter names.
|
|
|
|
|
// If you need to use ':' in a query, i.e. with maps or UDTs use '::' instead.
|
2017-07-25 11:13:34 +02:00
|
|
|
func CompileNamedQuery(qs []byte) (stmt string, names []string, err error) {
|
2017-07-25 11:10:19 +02:00
|
|
|
// guess number of names
|
|
|
|
|
n := bytes.Count(qs, []byte(":"))
|
|
|
|
|
if n == 0 {
|
|
|
|
|
return "", nil, errors.New("expected a named query")
|
|
|
|
|
}
|
|
|
|
|
names = make([]string, 0, n)
|
|
|
|
|
rebound := make([]byte, 0, len(qs))
|
|
|
|
|
|
|
|
|
|
inName := false
|
|
|
|
|
last := len(qs) - 1
|
|
|
|
|
name := make([]byte, 0, 10)
|
|
|
|
|
|
|
|
|
|
for i, b := range qs {
|
|
|
|
|
// a ':' while we're in a name is an error
|
2019-11-08 00:03:26 +01:00
|
|
|
switch {
|
|
|
|
|
case b == ':':
|
2017-07-25 11:10:19 +02:00
|
|
|
// if this is the second ':' in a '::' escape sequence, append a ':'
|
|
|
|
|
if inName && i > 0 && qs[i-1] == ':' {
|
|
|
|
|
rebound = append(rebound, ':')
|
|
|
|
|
inName = false
|
|
|
|
|
continue
|
|
|
|
|
} else if inName {
|
|
|
|
|
err = errors.New("unexpected `:` while reading named param at " + strconv.Itoa(i))
|
2017-07-25 11:13:34 +02:00
|
|
|
return stmt, names, err
|
2017-07-25 11:10:19 +02:00
|
|
|
}
|
|
|
|
|
inName = true
|
|
|
|
|
name = []byte{}
|
|
|
|
|
// if we're in a name, and this is an allowed character, continue
|
2019-11-08 00:03:26 +01:00
|
|
|
case inName && (allowedBindRune(b) || b == '_' || b == '.') && i != last:
|
2017-07-25 11:10:19 +02:00
|
|
|
// append the byte to the name if we are in a name and not on the last byte
|
|
|
|
|
name = append(name, b)
|
|
|
|
|
// if we're in a name and it's not an allowed character, the name is done
|
2019-11-08 00:03:26 +01:00
|
|
|
case inName:
|
2017-07-25 11:10:19 +02:00
|
|
|
inName = false
|
|
|
|
|
// if this is the final byte of the string and it is part of the name, then
|
|
|
|
|
// make sure to add it to the name
|
2017-07-26 09:10:35 +02:00
|
|
|
if i == last && allowedBindRune(b) {
|
2017-07-25 11:10:19 +02:00
|
|
|
name = append(name, b)
|
|
|
|
|
}
|
|
|
|
|
// add the string representation to the names list
|
|
|
|
|
names = append(names, string(name))
|
|
|
|
|
// add a proper bindvar for the bindType
|
|
|
|
|
rebound = append(rebound, '?')
|
|
|
|
|
// add this byte to string unless it was not part of the name
|
|
|
|
|
if i != last {
|
|
|
|
|
rebound = append(rebound, b)
|
2017-07-26 09:10:35 +02:00
|
|
|
} else if !allowedBindRune(b) {
|
2017-07-25 11:10:19 +02:00
|
|
|
rebound = append(rebound, b)
|
|
|
|
|
}
|
2019-11-08 00:03:26 +01:00
|
|
|
default:
|
2017-07-25 11:10:19 +02:00
|
|
|
// this is a normal byte and should just go onto the rebound query
|
|
|
|
|
rebound = append(rebound, b)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return string(rebound), names, err
|
|
|
|
|
}
|
2017-07-25 12:25:59 +02:00
|
|
|
|
2017-07-26 09:10:35 +02:00
|
|
|
func allowedBindRune(b byte) bool {
|
|
|
|
|
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9')
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-25 12:25:59 +02:00
|
|
|
// Queryx is a wrapper around gocql.Query which adds struct binding capabilities.
|
|
|
|
|
type Queryx struct {
|
2024-06-14 13:07:21 -04:00
|
|
|
err error
|
|
|
|
|
tr Transformer
|
2017-07-25 12:25:59 +02:00
|
|
|
Mapper *reflectx.Mapper
|
2024-06-14 13:07:21 -04:00
|
|
|
*gocql.Query
|
2024-06-14 11:21:57 -04:00
|
|
|
Names []string
|
2024-06-25 13:34:50 +02:00
|
|
|
strict bool
|
2017-07-25 12:25:59 +02:00
|
|
|
}
|
|
|
|
|
|
2025-11-20 16:09:09 +01:00
|
|
|
func (q *Queryx) Release() {}
|
|
|
|
|
|
2017-07-28 10:18:38 +02:00
|
|
|
// Query creates a new Queryx from gocql.Query using a default mapper.
|
2020-04-20 17:39:17 +02:00
|
|
|
//
|
2020-05-29 18:14:43 +08:00
|
|
|
// Deprecated: Use gocqlx.Session.Query API instead.
|
2017-08-24 11:54:41 +02:00
|
|
|
func Query(q *gocql.Query, names []string) *Queryx {
|
|
|
|
|
return &Queryx{
|
2017-07-28 10:18:38 +02:00
|
|
|
Query: q,
|
|
|
|
|
Names: names,
|
|
|
|
|
Mapper: DefaultMapper,
|
2021-11-23 21:58:26 +03:00
|
|
|
tr: DefaultBindTransformer,
|
2024-06-25 13:34:50 +02:00
|
|
|
strict: DefaultStrict,
|
2017-07-25 12:25:59 +02:00
|
|
|
}
|
2017-07-28 10:18:38 +02:00
|
|
|
}
|
2017-07-25 12:25:59 +02:00
|
|
|
|
2021-11-23 21:58:26 +03:00
|
|
|
// WithBindTransformer sets the query bind transformer.
|
|
|
|
|
// The transformer is called right before binding a value to a named parameter.
|
|
|
|
|
func (q *Queryx) WithBindTransformer(tr Transformer) *Queryx {
|
|
|
|
|
q.tr = tr
|
|
|
|
|
return q
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-01 13:29:52 +02:00
|
|
|
// BindStruct binds query named parameters to values from arg using mapper. If
|
|
|
|
|
// value cannot be found error is reported.
|
2017-08-02 11:12:31 +02:00
|
|
|
func (q *Queryx) BindStruct(arg interface{}) *Queryx {
|
2021-11-23 13:54:51 +03:00
|
|
|
arglist, err := q.bindStructArgs(arg, nil)
|
2017-07-25 12:25:59 +02:00
|
|
|
if err != nil {
|
2017-08-02 11:12:31 +02:00
|
|
|
q.err = fmt.Errorf("bind error: %s", err)
|
|
|
|
|
} else {
|
|
|
|
|
q.err = nil
|
|
|
|
|
q.Bind(arglist...)
|
2017-07-25 12:25:59 +02:00
|
|
|
}
|
|
|
|
|
|
2017-08-02 11:12:31 +02:00
|
|
|
return q
|
2017-07-25 12:25:59 +02:00
|
|
|
}
|
|
|
|
|
|
2017-08-01 13:29:52 +02:00
|
|
|
// BindStructMap binds query named parameters to values from arg0 and arg1
|
|
|
|
|
// using a mapper. If value cannot be found in arg0 it's looked up in arg1
|
|
|
|
|
// before reporting an error.
|
2017-08-02 11:12:31 +02:00
|
|
|
func (q *Queryx) BindStructMap(arg0 interface{}, arg1 map[string]interface{}) *Queryx {
|
2021-11-23 13:54:51 +03:00
|
|
|
arglist, err := q.bindStructArgs(arg0, arg1)
|
2017-08-01 13:29:52 +02:00
|
|
|
if err != nil {
|
2017-08-02 11:12:31 +02:00
|
|
|
q.err = fmt.Errorf("bind error: %s", err)
|
|
|
|
|
} else {
|
|
|
|
|
q.err = nil
|
|
|
|
|
q.Bind(arglist...)
|
2017-08-01 13:29:52 +02:00
|
|
|
}
|
|
|
|
|
|
2017-08-02 11:12:31 +02:00
|
|
|
return q
|
2017-08-01 13:29:52 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-08 06:25:00 -04:00
|
|
|
// GetRequestTimeout returns time driver waits for single server response
|
|
|
|
|
// This timeout is applied to preparing statement request and for query execution requests
|
|
|
|
|
func (q *Queryx) GetRequestTimeout() time.Duration {
|
2025-11-20 16:09:09 +01:00
|
|
|
return 0
|
2025-09-08 06:25:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetRequestTimeout sets time driver waits for server to respond
|
|
|
|
|
// This timeout is applied to preparing statement request and for query execution requests
|
|
|
|
|
func (q *Queryx) SetRequestTimeout(timeout time.Duration) *Queryx {
|
2025-11-20 16:09:09 +01:00
|
|
|
// q.Query.SetRequestTimeout(timeout)
|
2025-09-08 06:25:00 -04:00
|
|
|
return q
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-10 13:24:39 -04:00
|
|
|
// SetHostID allows to define the host the query should be executed against. If the
|
|
|
|
|
// host was filtered or otherwise unavailable, then the query will error. If an empty
|
|
|
|
|
// string is sent, the default behavior, using the configured HostSelectionPolicy will
|
|
|
|
|
// be used. A hostID can be obtained from HostInfo.HostID() after calling GetHosts().
|
|
|
|
|
func (q *Queryx) SetHostID(hostID string) *Queryx {
|
|
|
|
|
q.Query.SetHostID(hostID)
|
|
|
|
|
return q
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-23 13:54:51 +03:00
|
|
|
func (q *Queryx) bindStructArgs(arg0 interface{}, arg1 map[string]interface{}) ([]interface{}, error) {
|
|
|
|
|
arglist := make([]interface{}, 0, len(q.Names))
|
2017-07-25 12:25:59 +02:00
|
|
|
|
|
|
|
|
// grab the indirected value of arg
|
2017-08-01 13:29:52 +02:00
|
|
|
v := reflect.ValueOf(arg0)
|
2024-06-14 13:07:21 -04:00
|
|
|
for v.Kind() == reflect.Ptr {
|
2017-07-25 12:25:59 +02:00
|
|
|
v = v.Elem()
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-23 13:54:51 +03:00
|
|
|
err := q.Mapper.TraversalsByNameFunc(v.Type(), q.Names, func(i int, t []int) error {
|
2017-08-01 13:29:52 +02:00
|
|
|
if len(t) != 0 {
|
2025-10-28 14:52:22 -04:00
|
|
|
val := reflectx.FieldByIndexesReadOnly(v, t)
|
2017-08-01 13:29:52 +02:00
|
|
|
arglist = append(arglist, val.Interface())
|
|
|
|
|
} else {
|
2021-11-23 13:54:51 +03:00
|
|
|
val, ok := arg1[q.Names[i]]
|
2017-08-01 13:29:52 +02:00
|
|
|
if !ok {
|
2021-11-23 13:54:51 +03:00
|
|
|
return fmt.Errorf("could not find name %q in %#v and %#v", q.Names[i], arg0, arg1)
|
2017-08-01 13:29:52 +02:00
|
|
|
}
|
|
|
|
|
arglist = append(arglist, val)
|
2017-07-25 12:25:59 +02:00
|
|
|
}
|
|
|
|
|
|
2021-11-23 21:58:26 +03:00
|
|
|
if q.tr != nil {
|
|
|
|
|
arglist[i] = q.tr(q.Names[i], arglist[i])
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-14 17:48:07 +01:00
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return arglist, err
|
2017-07-25 12:25:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BindMap binds query named parameters using map.
|
2017-08-02 11:12:31 +02:00
|
|
|
func (q *Queryx) BindMap(arg map[string]interface{}) *Queryx {
|
2021-11-23 13:54:51 +03:00
|
|
|
arglist, err := q.bindMapArgs(arg)
|
2017-07-25 12:25:59 +02:00
|
|
|
if err != nil {
|
2017-08-02 11:12:31 +02:00
|
|
|
q.err = fmt.Errorf("bind error: %s", err)
|
|
|
|
|
} else {
|
|
|
|
|
q.err = nil
|
|
|
|
|
q.Bind(arglist...)
|
2017-07-25 12:25:59 +02:00
|
|
|
}
|
|
|
|
|
|
2017-08-02 11:12:31 +02:00
|
|
|
return q
|
2017-07-25 12:25:59 +02:00
|
|
|
}
|
|
|
|
|
|
2021-11-23 13:54:51 +03:00
|
|
|
func (q *Queryx) bindMapArgs(arg map[string]interface{}) ([]interface{}, error) {
|
|
|
|
|
arglist := make([]interface{}, 0, len(q.Names))
|
2017-07-25 12:25:59 +02:00
|
|
|
|
2021-11-23 13:54:51 +03:00
|
|
|
for _, name := range q.Names {
|
2017-07-25 12:25:59 +02:00
|
|
|
val, ok := arg[name]
|
|
|
|
|
if !ok {
|
2017-09-21 21:14:28 +02:00
|
|
|
return arglist, fmt.Errorf("could not find name %q in %#v", name, arg)
|
2017-07-25 12:25:59 +02:00
|
|
|
}
|
2021-11-23 21:58:26 +03:00
|
|
|
|
|
|
|
|
if q.tr != nil {
|
|
|
|
|
val = q.tr(name, val)
|
|
|
|
|
}
|
2017-07-25 12:25:59 +02:00
|
|
|
arglist = append(arglist, val)
|
|
|
|
|
}
|
|
|
|
|
return arglist, nil
|
|
|
|
|
}
|
2017-07-28 10:18:38 +02:00
|
|
|
|
2020-04-15 17:06:29 +02:00
|
|
|
// Bind sets query arguments of query. This can also be used to rebind new query arguments
|
|
|
|
|
// to an existing query instance.
|
|
|
|
|
func (q *Queryx) Bind(v ...interface{}) *Queryx {
|
2024-06-25 13:34:50 +02:00
|
|
|
q.Query.Bind(udtWrapSlice(q.Mapper, q.strict, v)...)
|
2020-04-15 17:06:29 +02:00
|
|
|
return q
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-14 11:28:30 -04:00
|
|
|
// Scan executes the query, copies the columns of the first selected
|
|
|
|
|
// row into the values pointed at by dest and discards the rest. If no rows
|
|
|
|
|
// were selected, ErrNotFound is returned.
|
|
|
|
|
func (q *Queryx) Scan(v ...interface{}) error {
|
2024-06-25 13:34:50 +02:00
|
|
|
return q.Query.Scan(udtWrapSlice(q.Mapper, q.strict, v)...)
|
2024-06-14 11:28:30 -04:00
|
|
|
}
|
|
|
|
|
|
2017-08-02 11:12:31 +02:00
|
|
|
// Err returns any binding errors.
|
|
|
|
|
func (q *Queryx) Err() error {
|
|
|
|
|
return q.err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Exec executes the query without returning any rows.
|
|
|
|
|
func (q *Queryx) Exec() error {
|
|
|
|
|
if q.err != nil {
|
|
|
|
|
return q.err
|
|
|
|
|
}
|
|
|
|
|
return q.Query.Exec()
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 10:40:32 +02:00
|
|
|
// ExecRelease calls Exec and releases the query, a released query cannot be
|
2017-08-22 11:36:21 +02:00
|
|
|
// reused.
|
|
|
|
|
func (q *Queryx) ExecRelease() error {
|
|
|
|
|
defer q.Release()
|
|
|
|
|
return q.Exec()
|
|
|
|
|
}
|
2018-05-23 10:40:32 +02:00
|
|
|
|
2020-04-20 19:43:32 +02:00
|
|
|
// ExecCAS executes the Lightweight Transaction query, returns whether query was applied.
|
|
|
|
|
// See: https://docs.scylladb.com/using-scylla/lwt/ for more details.
|
2022-04-22 09:14:09 +02:00
|
|
|
//
|
|
|
|
|
// When using Cassandra it may be necessary to use NoSkipMetadata in order to obtain an
|
|
|
|
|
// accurate "applied" value. See the documentation of NoSkipMetaData method on this page
|
|
|
|
|
// for more details.
|
2020-04-20 19:43:32 +02:00
|
|
|
func (q *Queryx) ExecCAS() (applied bool, err error) {
|
2022-02-21 15:15:05 +01:00
|
|
|
q.NoSkipMetadata()
|
2020-04-20 19:43:32 +02:00
|
|
|
iter := q.Iter().StructOnly()
|
|
|
|
|
if err := iter.Get(&struct{}{}); err != nil {
|
|
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
|
return iter.applied, iter.Close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExecCASRelease calls ExecCAS and releases the query, a released query cannot be
|
|
|
|
|
// reused.
|
|
|
|
|
func (q *Queryx) ExecCASRelease() (bool, error) {
|
|
|
|
|
defer q.Release()
|
|
|
|
|
return q.ExecCAS()
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-06 15:33:37 +01:00
|
|
|
// Get scans first row into a destination and closes the iterator.
|
|
|
|
|
//
|
|
|
|
|
// If the destination type is a struct pointer, then Iter.StructScan will be
|
|
|
|
|
// used.
|
|
|
|
|
// If the destination is some other type, then the row must only have one column
|
|
|
|
|
// which can scan into that type.
|
|
|
|
|
// This includes types that implement gocql.Unmarshaler and gocql.UDTUnmarshaler.
|
|
|
|
|
//
|
|
|
|
|
// If you'd like to treat a type that implements gocql.Unmarshaler or
|
|
|
|
|
// gocql.UDTUnmarshaler as an ordinary struct you should call
|
|
|
|
|
// Iter().StructOnly().Get(dest) instead.
|
2018-05-23 10:40:32 +02:00
|
|
|
//
|
|
|
|
|
// If no rows were selected, ErrNotFound is returned.
|
|
|
|
|
func (q *Queryx) Get(dest interface{}) error {
|
|
|
|
|
if q.err != nil {
|
|
|
|
|
return q.err
|
|
|
|
|
}
|
2019-11-08 23:48:15 +01:00
|
|
|
return q.Iter().Get(dest)
|
2018-05-23 10:40:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetRelease calls Get and releases the query, a released query cannot be
|
|
|
|
|
// reused.
|
|
|
|
|
func (q *Queryx) GetRelease(dest interface{}) error {
|
|
|
|
|
defer q.Release()
|
|
|
|
|
return q.Get(dest)
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-20 19:43:32 +02:00
|
|
|
// GetCAS executes a lightweight transaction.
|
|
|
|
|
// If the transaction fails because the existing values did not match,
|
|
|
|
|
// the previous values will be stored in dest object.
|
|
|
|
|
// See: https://docs.scylladb.com/using-scylla/lwt/ for more details.
|
|
|
|
|
func (q *Queryx) GetCAS(dest interface{}) (applied bool, err error) {
|
2021-04-30 21:17:32 +03:00
|
|
|
if q.err != nil {
|
|
|
|
|
return false, q.err
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-21 15:15:05 +01:00
|
|
|
q.NoSkipMetadata()
|
2020-04-20 19:43:32 +02:00
|
|
|
iter := q.Iter()
|
|
|
|
|
if err := iter.Get(dest); err != nil {
|
|
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return iter.applied, iter.Close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetCASRelease calls GetCAS and releases the query, a released query cannot be
|
|
|
|
|
// reused.
|
|
|
|
|
func (q *Queryx) GetCASRelease(dest interface{}) (bool, error) {
|
|
|
|
|
defer q.Release()
|
|
|
|
|
return q.GetCAS(dest)
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 10:40:32 +02:00
|
|
|
// Select scans all rows into a destination, which must be a pointer to slice
|
2019-11-06 15:33:37 +01:00
|
|
|
// of any type, and closes the iterator.
|
|
|
|
|
//
|
|
|
|
|
// If the destination slice type is a struct, then Iter.StructScan will be used
|
|
|
|
|
// on each row.
|
|
|
|
|
// If the destination is some other type, then each row must only have one
|
|
|
|
|
// column which can scan into that type.
|
|
|
|
|
// This includes types that implement gocql.Unmarshaler and gocql.UDTUnmarshaler.
|
|
|
|
|
//
|
|
|
|
|
// If you'd like to treat a type that implements gocql.Unmarshaler or
|
|
|
|
|
// gocql.UDTUnmarshaler as an ordinary struct you should call
|
|
|
|
|
// Iter().StructOnly().Select(dest) instead.
|
2018-05-23 10:40:32 +02:00
|
|
|
//
|
|
|
|
|
// If no rows were selected, ErrNotFound is NOT returned.
|
|
|
|
|
func (q *Queryx) Select(dest interface{}) error {
|
|
|
|
|
if q.err != nil {
|
|
|
|
|
return q.err
|
|
|
|
|
}
|
2019-11-08 23:48:15 +01:00
|
|
|
return q.Iter().Select(dest)
|
2018-05-23 10:40:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SelectRelease calls Select and releases the query, a released query cannot be
|
|
|
|
|
// reused.
|
|
|
|
|
func (q *Queryx) SelectRelease(dest interface{}) error {
|
|
|
|
|
defer q.Release()
|
|
|
|
|
return q.Select(dest)
|
|
|
|
|
}
|
2018-10-23 16:17:04 +02:00
|
|
|
|
|
|
|
|
// Iter returns Iterx instance for the query. It should be used when data is too
|
|
|
|
|
// big to be loaded with Select in order to do row by row iteration.
|
|
|
|
|
// See Iterx StructScan function.
|
|
|
|
|
func (q *Queryx) Iter() *Iterx {
|
2020-04-20 17:39:17 +02:00
|
|
|
return &Iterx{
|
|
|
|
|
Iter: q.Query.Iter(),
|
|
|
|
|
Mapper: q.Mapper,
|
2024-06-25 13:34:50 +02:00
|
|
|
strict: q.strict,
|
2020-04-20 17:39:17 +02:00
|
|
|
}
|
2018-10-23 16:17:04 +02:00
|
|
|
}
|
2024-06-14 11:21:57 -04:00
|
|
|
|
2024-06-25 13:34:50 +02:00
|
|
|
// Strict forces the query and iterators to report an error if there are missing fields.
|
|
|
|
|
// By default when scanning a struct if result row has a column that cannot be mapped to
|
|
|
|
|
// any destination it is ignored. With strict error is reported.
|
|
|
|
|
func (q *Queryx) Strict() *Queryx {
|
|
|
|
|
q.strict = true
|
2024-06-14 11:21:57 -04:00
|
|
|
return q
|
|
|
|
|
}
|