* 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
1016 lines
26 KiB
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"
|
|
|
|
"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)
|
|
}
|
|
}
|