Files
gocqlx/iterx_test.go

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"
gocql "github.com/apache/cassandra-gocql-driver/v2"
"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)
}
}