Files
gocqlx/iterx_test.go
Dmitry Kropachev 38001d64ac Update gocql version to v1.16.1 (#353)
* Update gocql version to v1.16.1

1. Update gocql to v1.16.1
2. Update golang to 1.25, since new gocql version requres it

* Update golangci to 2.5.0

It is needed since 1.64.8 does not support golang 1.25.
1. Update golangci to 2.5.0
2. Migrate from golangci config v1 to v2
3. Integrate fieldaligment to golangci
4. Drop fieldaligment from Makefile
5. Address complaints
2025-10-28 14:52:22 -04:00

1016 lines
26 KiB
Go

// Copyright (C) 2017 ScyllaDB
// Use of this source code is governed by a ALv2-style
// license that can be found in the LICENSE file.
//go:build all || integration
// +build all integration
package gocqlx_test
import (
"math/big"
"reflect"
"strings"
"testing"
"time"
"github.com/gocql/gocql"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"gopkg.in/inf.v0"
"github.com/scylladb/gocqlx/v3"
"github.com/scylladb/gocqlx/v3/gocqlxtest"
"github.com/scylladb/gocqlx/v3/qb"
)
type FullName struct {
FirstName string
LastName string
}
func (n FullName) MarshalCQL(info gocql.TypeInfo) ([]byte, error) {
return []byte(n.FirstName + " " + n.LastName), nil
}
func (n *FullName) UnmarshalCQL(info gocql.TypeInfo, data []byte) error {
t := strings.SplitN(string(data), " ", 2)
n.FirstName, n.LastName = t[0], t[1]
return nil
}
type FullNameUDT struct {
gocqlx.UDT
FullName
}
type FullNamePtrUDT struct {
gocqlx.UDT
*FullName
}
func diff(t *testing.T, expected, got interface{}) {
t.Helper()
if d := cmp.Diff(expected, got, diffOpts); d != "" {
t.Errorf("got %+v expected %+v, diff: %s", got, expected, d)
}
}
var diffOpts = cmpopts.IgnoreUnexported(big.Int{}, inf.Dec{})
func TestIterxUDT(t *testing.T) {
session := gocqlxtest.CreateSession(t)
t.Cleanup(func() {
session.Close()
})
if err := session.ExecStmt(`CREATE TYPE gocqlx_test.UDTTest_Full (first text, second text)`); err != nil {
t.Fatal("create type:", err)
}
if err := session.ExecStmt(`CREATE TABLE gocqlx_test.udt_table (
testuuid timeuuid PRIMARY KEY,
testudt gocqlx_test.UDTTest_Full
)`); err != nil {
t.Fatal("create table:", err)
}
type Full struct {
First string
Second string
}
type Part struct {
First string
}
type Extra struct {
First string
Second string
Third string
}
type FullUDT struct {
gocqlx.UDT
Full
}
type PartUDT struct {
gocqlx.UDT
Part
}
type ExtraUDT struct {
gocqlx.UDT
Extra
}
type FullUDTPtr struct {
gocqlx.UDT
*Full
}
type PartUDTPtr struct {
gocqlx.UDT
*Part
}
type ExtraUDTPtr struct {
gocqlx.UDT
*Extra
}
full := FullUDT{
Full: Full{
First: "John",
Second: "Doe",
},
}
makeStruct := func(testuuid gocql.UUID, insert interface{}) interface{} {
b := reflect.New(reflect.StructOf([]reflect.StructField{
{
Name: "TestUUID",
Type: reflect.TypeOf(gocql.UUID{}),
},
{
Name: "TestUDT",
Type: reflect.TypeOf(insert),
},
})).Interface()
reflect.ValueOf(b).Elem().FieldByName("TestUUID").Set(reflect.ValueOf(testuuid))
reflect.ValueOf(b).Elem().FieldByName("TestUDT").Set(reflect.ValueOf(insert))
return b
}
tcases := []struct {
name string
insert interface{}
expected interface{}
expectedOnDB FullUDT
}{
{
name: "exact-match",
insert: full,
expectedOnDB: full,
expected: full,
},
{
name: "exact-match-ptr",
insert: FullUDTPtr{
Full: &Full{
First: "John",
Second: "Doe",
},
},
expectedOnDB: full,
expected: FullUDTPtr{
Full: &Full{
First: "John",
Second: "Doe",
},
},
},
{
name: "extra-field",
insert: ExtraUDT{
Extra: Extra{
First: "John",
Second: "Doe",
Third: "Smith",
},
},
expectedOnDB: full,
expected: ExtraUDT{
Extra: Extra{
First: "John",
Second: "Doe",
Third: "", // Since the UDT has only 2 fields, the third field should be empty
},
},
},
{
name: "extra-field-ptr",
insert: ExtraUDTPtr{
Extra: &Extra{
First: "John",
Second: "Doe",
Third: "Smith",
},
},
expectedOnDB: full,
expected: ExtraUDTPtr{
Extra: &Extra{
First: "John",
Second: "Doe",
Third: "", // Since the UDT has only 2 fields, the third field should be empty
},
},
},
{
name: "absent-field",
insert: PartUDT{
Part: Part{
First: "John",
},
},
expectedOnDB: FullUDT{
Full: Full{
First: "John",
Second: "",
},
},
expected: PartUDT{
Part: Part{
First: "John",
},
},
},
{
name: "absent-field-ptr",
insert: PartUDTPtr{
Part: &Part{
First: "John",
},
},
expectedOnDB: FullUDT{
Full: Full{
First: "John",
Second: "",
},
},
expected: PartUDTPtr{
Part: &Part{
First: "John",
},
},
},
}
const insertStmt = `INSERT INTO udt_table (testuuid, testudt) VALUES (?, ?)`
const deleteStmt = `DELETE FROM udt_table WHERE testuuid = ?`
for _, tc := range tcases {
t.Run(tc.name, func(t *testing.T) {
testuuid := gocql.TimeUUID()
if reflect.TypeOf(tc.insert) != reflect.TypeOf(tc.expected) {
t.Fatalf("insert and expectedOnDB must have the same type")
}
t.Cleanup(func() {
_ = session.Query(deleteStmt, nil).Bind(testuuid).ExecRelease()
})
t.Run("insert-bind", func(t *testing.T) {
if err := session.Query(insertStmt, nil).Bind(
testuuid,
tc.insert,
).ExecRelease(); err != nil {
t.Fatal(err.Error())
}
// Make sure the UDT was inserted correctly
v := FullUDT{}
if err := session.Query(`SELECT testudt FROM udt_table where testuuid = ?`, nil).Bind(testuuid).Get(&v); err != nil {
t.Fatal(err.Error())
}
diff(t, tc.expectedOnDB, v)
})
t.Run("scan", func(t *testing.T) {
v := reflect.New(reflect.TypeOf(tc.expected)).Interface()
if err := session.Query(`SELECT testudt FROM udt_table where testuuid = ?`, nil).Bind(testuuid).Scan(v); err != nil {
t.Fatal(err.Error())
}
diff(t, tc.expected, reflect.ValueOf(v).Elem().Interface())
})
t.Run("get", func(t *testing.T) {
v := reflect.New(reflect.TypeOf(tc.expected)).Interface()
if err := session.Query(`SELECT testudt FROM udt_table where testuuid = ?`, nil).Bind(testuuid).Get(v); err != nil {
t.Fatal(err.Error())
}
diff(t, tc.expected, reflect.ValueOf(v).Elem().Interface())
})
t.Run("delete", func(t *testing.T) {
if err := session.Query(deleteStmt, nil).Bind(
testuuid,
).ExecRelease(); err != nil {
t.Fatal(err.Error())
}
})
t.Run("insert-bind-struct", func(t *testing.T) {
b := makeStruct(testuuid, tc.insert)
if err := session.Query(insertStmt, []string{"test_uuid", "test_udt"}).BindStruct(b).ExecRelease(); err != nil {
t.Fatal(err.Error())
}
// Make sure the UDT was inserted correctly
v := reflect.New(reflect.TypeOf(tc.expectedOnDB)).Interface()
if err := session.Query(`SELECT testudt FROM udt_table where testuuid = ?`, nil).Bind(testuuid).Get(v); err != nil {
t.Fatal(err.Error())
}
diff(t, &tc.expectedOnDB, v)
})
t.Run("insert-bind-struct-map", func(t *testing.T) {
t.Run("empty-map", func(t *testing.T) {
b := makeStruct(testuuid, tc.insert)
if err := session.Query(insertStmt, []string{"test_uuid", "test_udt"}).
BindStructMap(b, nil).ExecRelease(); err != nil {
t.Fatal(err.Error())
}
// Make sure the UDT was inserted correctly
v := reflect.New(reflect.TypeOf(tc.expectedOnDB)).Interface()
if err := session.Query(`SELECT testudt FROM udt_table where testuuid = ?`, nil).Bind(testuuid).Get(v); err != nil {
t.Fatal(err.Error())
}
diff(t, &tc.expectedOnDB, v)
})
t.Run("empty-struct", func(t *testing.T) {
if err := session.Query(insertStmt, []string{"test_uuid", "test_udt"}).
BindStructMap(struct{}{}, map[string]interface{}{
"test_uuid": testuuid,
"test_udt": tc.insert,
}).ExecRelease(); err != nil {
t.Fatal(err.Error())
}
// Make sure the UDT was inserted correctly
v := reflect.New(reflect.TypeOf(tc.expectedOnDB)).Interface()
if err := session.Query(`SELECT testudt FROM udt_table where testuuid = ?`, nil).Bind(testuuid).Get(v); err != nil {
t.Fatal(err.Error())
}
diff(t, &tc.expectedOnDB, v)
})
})
t.Run("insert-bind-map", func(t *testing.T) {
if err := session.Query(insertStmt, []string{"test_uuid", "test_udt"}).
BindMap(map[string]interface{}{
"test_uuid": testuuid,
"test_udt": tc.insert,
}).ExecRelease(); err != nil {
t.Fatal(err.Error())
}
// Make sure the UDT was inserted correctly
v := reflect.New(reflect.TypeOf(tc.expectedOnDB)).Interface()
if err := session.Query(`SELECT testudt FROM udt_table where testuuid = ?`, nil).Bind(testuuid).Get(v); err != nil {
t.Fatal(err.Error())
}
diff(t, &tc.expectedOnDB, v)
})
})
}
}
func TestIterxStruct(t *testing.T) {
session := gocqlxtest.CreateSession(t)
defer session.Close()
if err := session.ExecStmt(`CREATE TYPE gocqlx_test.FullName (first_Name text, last_name text)`); err != nil {
t.Fatal("create type:", err)
}
if err := session.ExecStmt(`CREATE TABLE gocqlx_test.struct_table (
testuuid timeuuid PRIMARY KEY,
testtimestamp timestamp,
testvarchar varchar,
testbigint bigint,
testblob blob,
testbool boolean,
testfloat float,
testdouble double,
testint int,
testdecimal decimal,
testlist list<text>,
testset set<int>,
testmap map<varchar, varchar>,
testvarint varint,
testinet inet,
testcustom text,
testudt gocqlx_test.FullName,
testptrudt gocqlx_test.FullName
)`); err != nil {
t.Fatal("create table:", err)
}
type StructTable struct {
Testuuid gocql.UUID
Testvarchar string
Testbigint int64
Testtimestamp time.Time
Testblob []byte
Testbool bool
Testfloat float32
Testdouble float64
Testint int
Testdecimal *inf.Dec
Testlist []string
Testset []int
Testmap map[string]string
Testvarint *big.Int
Testinet string
Testcustom FullName
Testudt FullNameUDT
Testptrudt FullNamePtrUDT
}
bigInt := new(big.Int)
if _, ok := bigInt.SetString("830169365738487321165427203929228", 10); !ok {
t.Fatal("failed setting bigint by string")
}
m := StructTable{
Testuuid: gocql.TimeUUID(),
Testvarchar: "Test VarChar",
Testbigint: time.Now().Unix(),
Testtimestamp: time.Now().Truncate(time.Millisecond).UTC(),
Testblob: []byte("test blob"),
Testbool: true,
Testfloat: float32(4.564),
Testdouble: float64(4.815162342),
Testint: 2343,
Testdecimal: inf.NewDec(100, 0),
Testlist: []string{"quux", "foo", "bar", "baz", "quux"},
Testset: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
Testmap: map[string]string{"field1": "val1", "field2": "val2", "field3": "val3"},
Testvarint: bigInt,
Testinet: "213.212.2.19",
Testcustom: FullName{FirstName: "John", LastName: "Doe"},
Testudt: FullNameUDT{FullName: FullName{FirstName: "John", LastName: "Doe"}},
Testptrudt: FullNamePtrUDT{FullName: &FullName{FirstName: "John", LastName: "Doe"}},
}
const insertStmt = `INSERT INTO struct_table (
testuuid, testtimestamp, testvarchar, testbigint, testblob, testbool, testfloat, testdouble,
testint, testdecimal, testlist, testset, testmap, testvarint, testinet, testcustom, testudt, testptrudt
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
if err := session.Query(insertStmt, nil).Bind(
m.Testuuid,
m.Testtimestamp,
m.Testvarchar,
m.Testbigint,
m.Testblob,
m.Testbool,
m.Testfloat,
m.Testdouble,
m.Testint,
m.Testdecimal,
m.Testlist,
m.Testset,
m.Testmap,
m.Testvarint,
m.Testinet,
m.Testcustom,
m.Testudt,
m.Testptrudt).ExecRelease(); err != nil {
t.Fatal("insert:", err)
}
const stmt = `SELECT * FROM struct_table`
t.Run("get", func(t *testing.T) {
var v StructTable
if err := session.Query(stmt, nil).Get(&v); err != nil {
t.Fatal("Get() failed:", err)
}
if diff := cmp.Diff(m, v, diffOpts); diff != "" {
t.Fatalf("Get()=%+v expected %+v, diff: %s", v, m, diff)
}
})
t.Run("select", func(t *testing.T) {
var v []StructTable
if err := session.Query(stmt, nil).Select(&v); err != nil {
t.Fatal("Select() failed:", err)
}
if len(v) != 1 {
t.Fatalf("Select()=%+v expected 1 row got %d", v, len(v))
}
if diff := cmp.Diff(m, v[0], diffOpts); diff != "" {
t.Fatalf("Select()[0]=%+v expected %+v, diff: %s", v[0], m, diff)
}
})
t.Run("select ptr", func(t *testing.T) {
var v []*StructTable
if err := session.Query(stmt, nil).Select(&v); err != nil {
t.Fatal("Select() failed:", err)
}
if len(v) != 1 {
t.Fatalf("Select()=%+v expected 1 row got %d", v, len(v))
}
if diff := cmp.Diff(&m, v[0], diffOpts); diff != "" {
t.Fatalf("Select()[0]=%+v expected %+v, diff: %s", v[0], &m, diff)
}
})
t.Run("struct scan", func(t *testing.T) {
var (
v StructTable
n int
)
iter := session.Query(stmt, nil).Iter()
for iter.StructScan(&v) {
n++
}
if err := iter.Close(); err != nil {
t.Fatal("StructScan() failed:", err)
}
if n != 1 {
t.Fatalf("StructScan() expected 1 row got %d", n)
}
if diff := cmp.Diff(m, v, diffOpts); diff != "" {
t.Fatalf("StructScan()=%+v expected %+v, diff: %s", v, m, diff)
}
})
}
func TestIterxScannable(t *testing.T) {
session := gocqlxtest.CreateSession(t)
defer session.Close()
if err := session.ExecStmt(`CREATE TABLE gocqlx_test.scannable_table (testfullname text PRIMARY KEY)`); err != nil {
t.Fatal("create table:", err)
}
m := FullName{"John", "Doe"}
if err := session.Query(`INSERT INTO scannable_table (testfullname) values (?)`, nil).Bind(m).Exec(); err != nil {
t.Fatal("insert:", err)
}
const stmt = `SELECT testfullname FROM scannable_table`
t.Run("get", func(t *testing.T) {
var v FullName
if err := session.Query(stmt, nil).Get(&v); err != nil {
t.Fatal("Get() failed:", err)
}
if diff := cmp.Diff(m, v); diff != "" {
t.Fatalf("Get()=%+v expected %+v, diff: %s", v, m, diff)
}
})
t.Run("select", func(t *testing.T) {
var v []FullName
if err := session.Query(stmt, nil).Select(&v); err != nil {
t.Fatal("Select() failed:", err)
}
if len(v) != 1 {
t.Fatalf("Select()=%+v expected 1 row got %d", v, len(v))
}
if diff := cmp.Diff(m, v[0]); diff != "" {
t.Fatalf("Select()[0]=%+v expected %+v, diff: %s", v[0], m, diff)
}
})
t.Run("select ptr", func(t *testing.T) {
var v []*FullName
if err := session.Query(stmt, nil).Select(&v); err != nil {
t.Fatal("Select() failed:", err)
}
if len(v) != 1 {
t.Fatalf("Select()=%+v expected 1 row got %d", v, len(v))
}
if diff := cmp.Diff(&m, v[0]); diff != "" {
t.Fatalf("Select()[0]=%+v expected %+v, diff: %s", v[0], &m, diff)
}
})
}
func TestIterxStructOnly(t *testing.T) {
session := gocqlxtest.CreateSession(t)
defer session.Close()
if err := session.ExecStmt(`CREATE TABLE gocqlx_test.struct_only_table (first_name text, last_name text, PRIMARY KEY (first_name, last_name))`); err != nil {
t.Fatal("create table:", err)
}
m := FullName{"John", "Doe"}
if err := session.Query(`INSERT INTO struct_only_table (first_name, last_name) values (?, ?)`, nil).Bind(m.FirstName, m.LastName).Exec(); err != nil {
t.Fatal("insert:", err)
}
const stmt = `SELECT first_name, last_name FROM struct_only_table`
t.Run("get", func(t *testing.T) {
var v FullName
if err := session.Query(stmt, nil).Iter().StructOnly().Get(&v); err != nil {
t.Fatal("Get() failed:", err)
}
if diff := cmp.Diff(m, v); diff != "" {
t.Fatalf("Get()=%+v expected %+v, diff: %s", v, m, diff)
}
})
t.Run("select", func(t *testing.T) {
var v []FullName
if err := session.Query(stmt, nil).Iter().StructOnly().Select(&v); err != nil {
t.Fatal("Select() failed:", err)
}
if len(v) != 1 {
t.Fatalf("Select()=%+v expected 1 row got %d", v, len(v))
}
if diff := cmp.Diff(m, v[0]); diff != "" {
t.Fatalf("Select()[0]=%+v expected %+v, diff: %s", v[0], m, diff)
}
})
t.Run("select ptr", func(t *testing.T) {
var v []*FullName
if err := session.Query(stmt, nil).Iter().StructOnly().Select(&v); err != nil {
t.Fatal("Select() failed:", err)
}
if len(v) != 1 {
t.Fatalf("Select()=%+v expected 1 row got %d", v, len(v))
}
if diff := cmp.Diff(&m, v[0]); diff != "" {
t.Fatalf("Select()[0]=%+v expected %+v, diff: %s", v[0], &m, diff)
}
})
const golden = "expected 1 column in result"
t.Run("get error", func(t *testing.T) {
var v FullName
err := session.Query(stmt, nil).Get(&v)
if err == nil || !strings.HasPrefix(err.Error(), golden) {
t.Fatalf("Get() error=%q expected %s", err, golden)
}
})
t.Run("select error", func(t *testing.T) {
var v []FullName
err := session.Query(stmt, nil).Select(&v)
if err == nil || !strings.HasPrefix(err.Error(), golden) {
t.Fatalf("Select() error=%q expected %s", err, golden)
}
})
}
func TestIterxStructOnlyUDT(t *testing.T) {
session := gocqlxtest.CreateSession(t)
defer session.Close()
if err := session.ExecStmt(`CREATE TABLE gocqlx_test.struct_only_udt_table (first_name text, last_name text, PRIMARY KEY (first_name, last_name))`); err != nil {
t.Fatal("create table:", err)
}
m := FullNameUDT{
FullName: FullName{
FirstName: "John",
LastName: "Doe",
},
}
if err := session.Query(`INSERT INTO struct_only_udt_table (first_name, last_name) values (?, ?)`, nil).Bind(m.FirstName, m.LastName).Exec(); err != nil {
t.Fatal("insert:", err)
}
const stmt = `SELECT first_name, last_name FROM struct_only_udt_table`
t.Run("get", func(t *testing.T) {
var v FullNameUDT
if err := session.Query(stmt, nil).Iter().StructOnly().Get(&v); err != nil {
t.Fatal("Get() failed:", err)
}
if diff := cmp.Diff(m, v); diff != "" {
t.Fatalf("Get()=%+v expected %+v, diff: %s", v, m, diff)
}
})
t.Run("select", func(t *testing.T) {
var v []FullNameUDT
if err := session.Query(stmt, nil).Iter().StructOnly().Select(&v); err != nil {
t.Fatal("Select() failed:", err)
}
if len(v) != 1 {
t.Fatalf("Select()=%+v expected 1 row got %d", v, len(v))
}
if diff := cmp.Diff(m, v[0]); diff != "" {
t.Fatalf("Select()[0]=%+v expected %+v, diff: %s", v[0], m, diff)
}
})
t.Run("select ptr", func(t *testing.T) {
var v []*FullNameUDT
if err := session.Query(stmt, nil).Iter().StructOnly().Select(&v); err != nil {
t.Fatal("Select() failed:", err)
}
if len(v) != 1 {
t.Fatalf("Select()=%+v expected 1 row got %d", v, len(v))
}
if diff := cmp.Diff(&m, v[0]); diff != "" {
t.Fatalf("Select()[0]=%+v expected %+v, diff: %s", v[0], &m, diff)
}
})
const golden = "expected 1 column in result"
t.Run("get error", func(t *testing.T) {
var v FullNameUDT
err := session.Query(stmt, nil).Get(&v)
if err == nil || !strings.HasPrefix(err.Error(), golden) {
t.Fatalf("Get() error=%q expected %s", err, golden)
}
})
t.Run("select error", func(t *testing.T) {
var v []FullNameUDT
err := session.Query(stmt, nil).Select(&v)
if err == nil || !strings.HasPrefix(err.Error(), golden) {
t.Fatalf("Select() error=%q expected %s", err, golden)
}
})
}
func TestIterxStrict(t *testing.T) {
session := gocqlxtest.CreateSession(t)
defer session.Close()
if err := session.ExecStmt(`CREATE TABLE gocqlx_test.strict_table (testtext text PRIMARY KEY, testtextunbound text)`); err != nil {
t.Fatal("create table:", err)
}
if err := session.Query(`INSERT INTO strict_table (testtext, testtextunbound) values (?, ?)`, nil).Bind("test", "test").Exec(); err != nil {
t.Fatal("insert:", err)
}
type StrictTable struct {
Testtext string
}
m := StrictTable{
Testtext: "test",
}
const (
stmt = `SELECT * FROM strict_table`
golden = "missing destination name \"testtextunbound\" in gocqlx_test.StrictTable"
)
t.Run("get strict", func(t *testing.T) {
var v StrictTable
err := session.Query(stmt, nil).Strict().Get(&v)
if err == nil || !strings.HasPrefix(err.Error(), golden) {
t.Fatalf("Get() error=%q expected %s", err, golden)
}
})
t.Run("select strict", func(t *testing.T) {
var v []StrictTable
err := session.Query(stmt, nil).Strict().Select(&v)
if err == nil || !strings.HasPrefix(err.Error(), golden) {
t.Fatalf("Select() error=%q expected %s", err, golden)
}
if cap(v) > 0 {
t.Fatalf("Select() effect alloc cap=%d expected 0", cap(v))
}
})
t.Run("get", func(t *testing.T) {
var v StrictTable
err := session.Query(stmt, nil).Get(&v)
if err != nil {
t.Fatal("Get() failed:", err)
}
if diff := cmp.Diff(m, v); diff != "" {
t.Fatalf("Get()=%+v expected %+v, diff: %s", v, m, diff)
}
})
t.Run("select", func(t *testing.T) {
var v []StrictTable
err := session.Query(stmt, nil).Select(&v)
if err != nil {
t.Fatal("Select() failed:", err)
}
if len(v) != 1 {
t.Fatalf("Select()=%+v expected 1 row got %d", v, len(v))
}
if diff := cmp.Diff(m, v[0]); diff != "" {
t.Fatalf("Select()[0]=%+v expected %+v, diff: %s", v[0], m, diff)
}
})
t.Run("select default", func(t *testing.T) {
var v []StrictTable
err := session.Query(stmt, nil).Iter().Select(&v)
if err != nil {
t.Fatal("Select() failed:", err)
}
if len(v) != 1 {
t.Fatalf("Select()=%+v expected 1 row got %d", v, len(v))
}
if diff := cmp.Diff(m, v[0]); diff != "" {
t.Fatalf("Select()[0]=%+v expected %+v, diff: %s", v[0], m, diff)
}
})
}
func TestIterxNotFound(t *testing.T) {
session := gocqlxtest.CreateSession(t)
defer session.Close()
if err := session.ExecStmt(`CREATE TABLE gocqlx_test.not_found_table (testtext text PRIMARY KEY)`); err != nil {
t.Fatal("create table:", err)
}
type NotFoundTable struct {
Testtext string
}
t.Run("get cql error", func(t *testing.T) {
var v NotFoundTable
err := session.Query(`SELECT * FROM not_found_table WRONG`, nil).RetryPolicy(nil).Get(&v)
if err == nil || !strings.Contains(err.Error(), "WRONG") {
t.Fatalf("Get() error=%q", err)
}
})
t.Run("get", func(t *testing.T) {
var v NotFoundTable
err := session.Query(`SELECT * FROM not_found_table`, nil).Get(&v)
if err != gocql.ErrNotFound {
t.Fatalf("Get() error=%q expected %s", err, gocql.ErrNotFound)
}
})
t.Run("select cql error", func(t *testing.T) {
var v []NotFoundTable
err := session.Query(`SELECT * FROM not_found_table WRONG`, nil).RetryPolicy(nil).Select(&v)
if err == nil || !strings.Contains(err.Error(), "WRONG") {
t.Fatalf("Get() error=%q", err)
}
})
t.Run("select", func(t *testing.T) {
var v []NotFoundTable
err := session.Query(`SELECT * FROM not_found_table`, nil).Select(&v)
if err != nil {
t.Fatalf("Select() error=%q expected %s", err, gocql.ErrNotFound)
}
if cap(v) > 0 {
t.Fatalf("Select() effect alloc cap=%d expected 0", cap(v))
}
})
}
func TestIterxErrorOnNil(t *testing.T) {
session := gocqlxtest.CreateSession(t)
defer session.Close()
if err := session.ExecStmt(`CREATE TABLE gocqlx_test.nil_table (testtext text PRIMARY KEY)`); err != nil {
t.Fatal("create table:", err)
}
const (
stmt = "SELECT * FROM not_found_table WRONG"
golden = "expected a pointer but got <nil>"
)
t.Run("get", func(t *testing.T) {
err := session.Query(stmt, nil).Get(nil)
if err == nil || err.Error() != golden {
t.Fatalf("Get() error=%q expected %q", err, golden)
}
})
t.Run("select", func(t *testing.T) {
err := session.Query(stmt, nil).Select(nil)
if err == nil || err.Error() != golden {
t.Fatalf("Select() error=%q expected %q", err, golden)
}
})
t.Run("struct scan", func(t *testing.T) {
iter := session.Query(stmt, nil).Iter()
iter.StructScan(nil)
err := iter.Close()
if err == nil || err.Error() != golden {
t.Fatalf("StructScan() error=%q expected %q", err, golden)
}
})
}
func TestIterxPaging(t *testing.T) {
session := gocqlxtest.CreateSession(t)
defer session.Close()
if err := session.ExecStmt(`CREATE TABLE gocqlx_test.paging_table (id int PRIMARY KEY, val int)`); err != nil {
t.Fatal("create table:", err)
}
if err := session.ExecStmt(`CREATE INDEX id_val_index ON gocqlx_test.paging_table (val)`); err != nil {
t.Fatal("create index:", err)
}
q := session.Query(qb.Insert("gocqlx_test.paging_table").Columns("id", "val").ToCql())
for i := 0; i < 5000; i++ {
if err := q.Bind(i, i).Exec(); err != nil {
t.Fatal(err)
}
}
type Paging struct {
ID int
Val int
}
stmt, names := qb.Select("gocqlx_test.paging_table").
Where(qb.Lt("val")).
AllowFiltering().
Columns("id", "val").ToCql()
iter := session.Query(stmt, names).Bind(100).PageSize(10).Iter()
defer func() {
_ = iter.Close()
}()
var cnt int
for {
p := &Paging{}
if !iter.StructScan(p) {
break
}
cnt++
}
if cnt != 100 {
t.Fatal("expected 100", "got", cnt)
}
}
func TestIterxCAS(t *testing.T) {
session := gocqlxtest.CreateSession(t)
defer session.Close()
const (
id = 0
baseSalary = 1000
minSalary = 2000
)
john := struct {
ID int
Salary int
}{ID: id, Salary: baseSalary}
if err := session.ExecStmt(`CREATE TABLE gocqlx_test.cas_table (id int PRIMARY KEY, salary int)`); err != nil {
t.Fatal("create table:", err)
}
insert := session.Query(qb.Insert("cas_table").Columns("id", "salary").Unique().ToCql())
applied, err := insert.BindStruct(john).ExecCAS()
if err != nil {
t.Fatal("ExecCAS() failed:", err)
}
if !applied {
t.Error("ExecCAS() expected first insert success")
}
applied, err = insert.BindStruct(john).ExecCAS()
if err != nil {
t.Fatal("ExecCAS() failed:", err)
}
if applied {
t.Error("ExecCAS() Expected second insert to not be applied")
}
update := session.Query(qb.Update("cas_table").
SetNamed("salary", "min_salary").
Where(qb.Eq("id")).
If(qb.LtNamed("salary", "min_salary")).
ToCql(),
)
applied, err = update.BindStructMap(john, qb.M{"min_salary": minSalary}).GetCAS(&john)
if err != nil {
t.Fatal("GetCAS() failed:", err)
}
if !applied {
t.Error("GetCAS() expected update to be applied")
}
if john.Salary != baseSalary {
t.Error("GetCAS()=%=v expected to have pre-image", john)
}
applied, err = update.BindStructMap(john, qb.M{"min_salary": minSalary * 2}).GetCAS(&john)
if err != nil {
t.Fatal(err)
}
if !applied {
t.Error("GetCAS() expected update to be applied")
}
if john.Salary != minSalary {
t.Error("GetCAS()=%=v expected to have pre-image", john)
}
}