Delete tests
This commit is contained in:
212
batchx_test.go
212
batchx_test.go
@@ -1,212 +0,0 @@
|
|||||||
// 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 (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
gocql "github.com/apache/cassandra-gocql-driver/v2"
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
|
|
||||||
"github.com/scylladb/gocqlx/v3"
|
|
||||||
"github.com/scylladb/gocqlx/v3/gocqlxtest"
|
|
||||||
"github.com/scylladb/gocqlx/v3/qb"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBatch(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
cluster := gocqlxtest.CreateCluster()
|
|
||||||
if err := gocqlxtest.CreateKeyspace(cluster, "batch_test"); err != nil {
|
|
||||||
t.Fatal("create keyspace:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
session, err := gocqlx.WrapSession(cluster.CreateSession())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create session:", err)
|
|
||||||
}
|
|
||||||
t.Cleanup(func() {
|
|
||||||
session.Close()
|
|
||||||
})
|
|
||||||
|
|
||||||
basicCreateAndPopulateKeyspace(t, session, "batch_test")
|
|
||||||
|
|
||||||
song := Song{
|
|
||||||
ID: mustParseUUID("60fc234a-8481-4343-93bb-72ecab404863"),
|
|
||||||
Title: "La Petite Tonkinoise",
|
|
||||||
Album: "Bye Bye Blackbird",
|
|
||||||
Artist: "Joséphine Baker",
|
|
||||||
Tags: []string{"jazz"},
|
|
||||||
Data: []byte("music"),
|
|
||||||
}
|
|
||||||
playlist := PlaylistItem{
|
|
||||||
ID: mustParseUUID("6a6255d9-680f-4cb5-b9a2-27cf4a810344"),
|
|
||||||
Title: "La Petite Tonkinoise",
|
|
||||||
Album: "Bye Bye Blackbird",
|
|
||||||
Artist: "Joséphine Baker",
|
|
||||||
SongID: mustParseUUID("60fc234a-8481-4343-93bb-72ecab404863"),
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("batch inserts", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tcases := []struct {
|
|
||||||
name string
|
|
||||||
methodSong func(*gocqlx.Batch, *gocqlx.Queryx, Song) error
|
|
||||||
methodPlaylist func(*gocqlx.Batch, *gocqlx.Queryx, PlaylistItem) error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "BindStruct",
|
|
||||||
methodSong: func(b *gocqlx.Batch, q *gocqlx.Queryx, song Song) error {
|
|
||||||
return b.BindStruct(q, song)
|
|
||||||
},
|
|
||||||
methodPlaylist: func(b *gocqlx.Batch, q *gocqlx.Queryx, playlist PlaylistItem) error {
|
|
||||||
return b.BindStruct(q, playlist)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "BindMap",
|
|
||||||
methodSong: func(b *gocqlx.Batch, q *gocqlx.Queryx, song Song) error {
|
|
||||||
return b.BindMap(q, map[string]interface{}{
|
|
||||||
"id": song.ID,
|
|
||||||
"title": song.Title,
|
|
||||||
"album": song.Album,
|
|
||||||
"artist": song.Artist,
|
|
||||||
"tags": song.Tags,
|
|
||||||
"data": song.Data,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
methodPlaylist: func(b *gocqlx.Batch, q *gocqlx.Queryx, playlist PlaylistItem) error {
|
|
||||||
return b.BindMap(q, map[string]interface{}{
|
|
||||||
"id": playlist.ID,
|
|
||||||
"title": playlist.Title,
|
|
||||||
"album": playlist.Album,
|
|
||||||
"artist": playlist.Artist,
|
|
||||||
"song_id": playlist.SongID,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Bind",
|
|
||||||
methodSong: func(b *gocqlx.Batch, q *gocqlx.Queryx, song Song) error {
|
|
||||||
return b.Bind(q, song.ID, song.Title, song.Album, song.Artist, song.Tags, song.Data)
|
|
||||||
},
|
|
||||||
methodPlaylist: func(b *gocqlx.Batch, q *gocqlx.Queryx, playlist PlaylistItem) error {
|
|
||||||
return b.Bind(q, playlist.ID, playlist.Title, playlist.Album, playlist.Artist, playlist.SongID)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "BindStructMap",
|
|
||||||
methodSong: func(b *gocqlx.Batch, q *gocqlx.Queryx, song Song) error {
|
|
||||||
in := map[string]interface{}{
|
|
||||||
"title": song.Title,
|
|
||||||
"album": song.Album,
|
|
||||||
}
|
|
||||||
return b.BindStructMap(q, struct {
|
|
||||||
ID gocql.UUID
|
|
||||||
Artist string
|
|
||||||
Tags []string
|
|
||||||
Data []byte
|
|
||||||
}{
|
|
||||||
ID: song.ID,
|
|
||||||
Artist: song.Artist,
|
|
||||||
Tags: song.Tags,
|
|
||||||
Data: song.Data,
|
|
||||||
}, in)
|
|
||||||
},
|
|
||||||
methodPlaylist: func(b *gocqlx.Batch, q *gocqlx.Queryx, playlist PlaylistItem) error {
|
|
||||||
in := map[string]interface{}{
|
|
||||||
"title": playlist.Title,
|
|
||||||
"album": playlist.Album,
|
|
||||||
}
|
|
||||||
return b.BindStructMap(q, struct {
|
|
||||||
ID gocql.UUID
|
|
||||||
Artist string
|
|
||||||
SongID gocql.UUID
|
|
||||||
}{
|
|
||||||
ID: playlist.ID,
|
|
||||||
Artist: playlist.Artist,
|
|
||||||
SongID: playlist.SongID,
|
|
||||||
},
|
|
||||||
in,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tcase := range tcases {
|
|
||||||
t.Run(tcase.name, func(t *testing.T) {
|
|
||||||
insertSong := qb.Insert("batch_test.songs").
|
|
||||||
Columns("id", "title", "album", "artist", "tags", "data").Query(session)
|
|
||||||
insertPlaylist := qb.Insert("batch_test.playlists").
|
|
||||||
Columns("id", "title", "album", "artist", "song_id").Query(session)
|
|
||||||
selectSong := qb.Select("batch_test.songs").Where(qb.Eq("id")).Query(session)
|
|
||||||
selectPlaylist := qb.Select("batch_test.playlists").Where(qb.Eq("id")).Query(session)
|
|
||||||
deleteSong := qb.Delete("batch_test.songs").Where(qb.Eq("id")).Query(session)
|
|
||||||
deletePlaylist := qb.Delete("batch_test.playlists").Where(qb.Eq("id")).Query(session)
|
|
||||||
|
|
||||||
b := session.NewBatch(gocql.LoggedBatch)
|
|
||||||
|
|
||||||
if err = tcase.methodSong(b, insertSong, song); err != nil {
|
|
||||||
t.Fatal("insert song:", err)
|
|
||||||
}
|
|
||||||
if err = tcase.methodPlaylist(b, insertPlaylist, playlist); err != nil {
|
|
||||||
t.Fatal("insert playList:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := session.ExecuteBatch(b); err != nil {
|
|
||||||
t.Fatal("batch execution:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify song was inserted
|
|
||||||
var gotSong Song
|
|
||||||
if err := selectSong.BindStruct(song).Get(&gotSong); err != nil {
|
|
||||||
t.Fatal("select song:", err)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(gotSong, song); diff != "" {
|
|
||||||
t.Errorf("expected %v song, got %v, diff: %q", song, gotSong, diff)
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify playlist item was inserted
|
|
||||||
var gotPlayList PlaylistItem
|
|
||||||
if err := selectPlaylist.BindStruct(playlist).Get(&gotPlayList); err != nil {
|
|
||||||
t.Fatal("select playList:", err)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(gotPlayList, playlist); diff != "" {
|
|
||||||
t.Errorf("expected %v playList, got %v, diff: %q", playlist, gotPlayList, diff)
|
|
||||||
}
|
|
||||||
if err = deletePlaylist.BindStruct(playlist).Exec(); err != nil {
|
|
||||||
t.Error("delete playlist:", err)
|
|
||||||
}
|
|
||||||
if err = deleteSong.BindStruct(song).Exec(); err != nil {
|
|
||||||
t.Error("delete song:", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBatchAllWrapped(t *testing.T) {
|
|
||||||
var (
|
|
||||||
gocqlType = reflect.TypeOf((*gocql.Batch)(nil))
|
|
||||||
gocqlxType = reflect.TypeOf((*gocqlx.Batch)(nil))
|
|
||||||
)
|
|
||||||
|
|
||||||
for i := 0; i < gocqlType.NumMethod(); i++ {
|
|
||||||
m, ok := gocqlxType.MethodByName(gocqlType.Method(i).Name)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("Batch missing method %s", gocqlType.Method(i).Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
for j := 0; j < m.Type.NumOut(); j++ {
|
|
||||||
if m.Type.Out(j) == gocqlType {
|
|
||||||
t.Errorf("Batch method %s not wrapped", m.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,230 +0,0 @@
|
|||||||
// 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 (
|
|
||||||
"encoding/json"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/scylladb/gocqlx/v3"
|
|
||||||
"github.com/scylladb/gocqlx/v3/gocqlxtest"
|
|
||||||
"github.com/scylladb/gocqlx/v3/qb"
|
|
||||||
)
|
|
||||||
|
|
||||||
type benchPerson struct {
|
|
||||||
ID int `json:"id"`
|
|
||||||
FirstName string `json:"first_name"`
|
|
||||||
LastName string `json:"last_name"`
|
|
||||||
Email []string `json:"email"`
|
|
||||||
Gender string `json:"gender"`
|
|
||||||
IPAddress string `json:"ip_address"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var benchPersonSchema = `
|
|
||||||
CREATE TABLE IF NOT EXISTS gocqlx_test.bench_person (
|
|
||||||
id int,
|
|
||||||
first_name text,
|
|
||||||
last_name text,
|
|
||||||
email list<text>,
|
|
||||||
gender text,
|
|
||||||
ip_address text,
|
|
||||||
PRIMARY KEY(id)
|
|
||||||
)`
|
|
||||||
|
|
||||||
var benchPersonCols = []string{"id", "first_name", "last_name", "email", "gender", "ip_address"}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Insert
|
|
||||||
//
|
|
||||||
|
|
||||||
// BenchmarkBaseGocqlInsert performs standard insert.
|
|
||||||
func BenchmarkBaseGocqlInsert(b *testing.B) {
|
|
||||||
people := loadFixtures()
|
|
||||||
session := gocqlxtest.CreateSession(b)
|
|
||||||
defer session.Close()
|
|
||||||
|
|
||||||
if err := session.ExecStmt(benchPersonSchema); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt, _ := qb.Insert("gocqlx_test.bench_person").Columns(benchPersonCols...).ToCql()
|
|
||||||
q := session.Session.Query(stmt)
|
|
||||||
defer q.Release()
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
p := people[i%len(people)]
|
|
||||||
if err := q.Bind(p.ID, p.FirstName, p.LastName, p.Email, p.Gender, p.IPAddress).Exec(); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkGocqlInsert performs insert with struct binding.
|
|
||||||
func BenchmarkGocqlxInsert(b *testing.B) {
|
|
||||||
people := loadFixtures()
|
|
||||||
session := gocqlxtest.CreateSession(b)
|
|
||||||
defer session.Close()
|
|
||||||
|
|
||||||
if err := session.ExecStmt(benchPersonSchema); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt, names := qb.Insert("gocqlx_test.bench_person").Columns(benchPersonCols...).ToCql()
|
|
||||||
q := session.Query(stmt, names)
|
|
||||||
defer q.Release()
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
p := people[i%len(people)]
|
|
||||||
if err := q.BindStruct(p).Exec(); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Get
|
|
||||||
//
|
|
||||||
|
|
||||||
// BenchmarkBaseGocqlGet performs standard scan.
|
|
||||||
func BenchmarkBaseGocqlGet(b *testing.B) {
|
|
||||||
people := loadFixtures()
|
|
||||||
session := gocqlxtest.CreateSession(b)
|
|
||||||
defer session.Close()
|
|
||||||
|
|
||||||
initTable(b, session, people)
|
|
||||||
|
|
||||||
stmt, _ := qb.Select("gocqlx_test.bench_person").Columns(benchPersonCols...).Where(qb.Eq("id")).Limit(1).ToCql()
|
|
||||||
q := session.Session.Query(stmt)
|
|
||||||
defer q.Release()
|
|
||||||
|
|
||||||
var p benchPerson
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
q.Bind(people[i%len(people)].ID)
|
|
||||||
if err := q.Scan(&p.ID, &p.FirstName, &p.LastName, &p.Email, &p.Gender, &p.IPAddress); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkGocqlxGet performs get.
|
|
||||||
func BenchmarkGocqlxGet(b *testing.B) {
|
|
||||||
people := loadFixtures()
|
|
||||||
session := gocqlxtest.CreateSession(b)
|
|
||||||
defer session.Close()
|
|
||||||
|
|
||||||
initTable(b, session, people)
|
|
||||||
|
|
||||||
stmt, names := qb.Select("gocqlx_test.bench_person").Columns(benchPersonCols...).Where(qb.Eq("id")).Limit(1).ToCql()
|
|
||||||
q := session.Query(stmt, names)
|
|
||||||
defer q.Release()
|
|
||||||
|
|
||||||
var p benchPerson
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
q.Bind(people[i%len(people)].ID)
|
|
||||||
if err := q.Get(&p); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Select
|
|
||||||
//
|
|
||||||
|
|
||||||
// BenchmarkBaseGocqlSelect performs standard loop scan with a slice of
|
|
||||||
// pointers.
|
|
||||||
func BenchmarkBaseGocqlSelect(b *testing.B) {
|
|
||||||
people := loadFixtures()
|
|
||||||
session := gocqlxtest.CreateSession(b)
|
|
||||||
defer session.Close()
|
|
||||||
|
|
||||||
initTable(b, session, people)
|
|
||||||
|
|
||||||
stmt, _ := qb.Select("gocqlx_test.bench_person").Columns(benchPersonCols...).Limit(100).ToCql()
|
|
||||||
q := session.Session.Query(stmt)
|
|
||||||
defer q.Release()
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
iter := q.Iter()
|
|
||||||
v := make([]*benchPerson, 100)
|
|
||||||
p := new(benchPerson)
|
|
||||||
for iter.Scan(&p.ID, &p.FirstName, &p.LastName, &p.Email, &p.Gender, &p.IPAddress) {
|
|
||||||
v = append(v, p)
|
|
||||||
p = new(benchPerson)
|
|
||||||
}
|
|
||||||
if err := iter.Close(); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
_ = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkGocqlSelect performs select to a slice pointers.
|
|
||||||
func BenchmarkGocqlxSelect(b *testing.B) {
|
|
||||||
people := loadFixtures()
|
|
||||||
session := gocqlxtest.CreateSession(b)
|
|
||||||
defer session.Close()
|
|
||||||
|
|
||||||
initTable(b, session, people)
|
|
||||||
|
|
||||||
stmt, names := qb.Select("gocqlx_test.bench_person").Columns(benchPersonCols...).Limit(100).ToCql()
|
|
||||||
q := session.Query(stmt, names)
|
|
||||||
defer q.Release()
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
var v []*benchPerson
|
|
||||||
if err := q.Select(&v); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadFixtures() []*benchPerson {
|
|
||||||
f, err := os.Open("testdata/people.json")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := f.Close(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
var v []*benchPerson
|
|
||||||
if err := json.NewDecoder(f).Decode(&v); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func initTable(b *testing.B, session gocqlx.Session, people []*benchPerson) {
|
|
||||||
b.Helper()
|
|
||||||
|
|
||||||
if err := session.ExecStmt(benchPersonSchema); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt, names := qb.Insert("gocqlx_test.bench_person").Columns(benchPersonCols...).ToCql()
|
|
||||||
q := session.Query(stmt, names)
|
|
||||||
|
|
||||||
for _, p := range people {
|
|
||||||
if err := q.BindStruct(p).Exec(); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
978
example_test.go
978
example_test.go
@@ -1,978 +0,0 @@
|
|||||||
// 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 (
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
gocql "github.com/apache/cassandra-gocql-driver/v2"
|
|
||||||
"golang.org/x/sync/errgroup"
|
|
||||||
"gopkg.in/inf.v0"
|
|
||||||
|
|
||||||
"github.com/scylladb/gocqlx/v3"
|
|
||||||
"github.com/scylladb/gocqlx/v3/gocqlxtest"
|
|
||||||
"github.com/scylladb/gocqlx/v3/qb"
|
|
||||||
"github.com/scylladb/gocqlx/v3/table"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Running examples locally:
|
|
||||||
// make run-scylla
|
|
||||||
// make run-examples
|
|
||||||
func TestExample(t *testing.T) {
|
|
||||||
cluster := gocqlxtest.CreateCluster()
|
|
||||||
|
|
||||||
session, err := gocqlx.WrapSession(cluster.CreateSession())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create session:", err)
|
|
||||||
}
|
|
||||||
defer session.Close()
|
|
||||||
|
|
||||||
_ = session.ExecStmt(`DROP KEYSPACE examples`)
|
|
||||||
|
|
||||||
basicCreateAndPopulateKeyspace(t, session, "examples")
|
|
||||||
createAndPopulateKeyspaceAllTypes(t, session)
|
|
||||||
basicReadScyllaVersion(t, session)
|
|
||||||
|
|
||||||
datatypesBlob(t, session)
|
|
||||||
datatypesUserDefinedType(t, session)
|
|
||||||
datatypesUserDefinedTypeWrapper(t, session)
|
|
||||||
datatypesJSON(t, session)
|
|
||||||
|
|
||||||
pagingForwardPaging(t, session)
|
|
||||||
pagingEfficientFullTableScan(t, session)
|
|
||||||
|
|
||||||
lwtLock(t, session)
|
|
||||||
unsetEmptyValues(t, session)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Song struct {
|
|
||||||
ID gocql.UUID
|
|
||||||
Title string
|
|
||||||
Album string
|
|
||||||
Artist string
|
|
||||||
Tags []string
|
|
||||||
Data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type PlaylistItem struct {
|
|
||||||
ID gocql.UUID
|
|
||||||
Title string
|
|
||||||
Album string
|
|
||||||
Artist string
|
|
||||||
SongID gocql.UUID
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example shows how to use query builders and table models to build
|
|
||||||
// queries. It uses "BindStruct" function for parameter binding and "Select"
|
|
||||||
// function for loading data to a slice.
|
|
||||||
func basicCreateAndPopulateKeyspace(t *testing.T, session gocqlx.Session, keyspace string) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
err := session.ExecStmt(fmt.Sprintf(
|
|
||||||
`CREATE KEYSPACE IF NOT EXISTS %s WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}`,
|
|
||||||
keyspace,
|
|
||||||
))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create keyspace:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = session.ExecStmt(fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s.songs (
|
|
||||||
id uuid PRIMARY KEY,
|
|
||||||
title text,
|
|
||||||
album text,
|
|
||||||
artist text,
|
|
||||||
tags set<text>,
|
|
||||||
data blob)`, keyspace))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create table:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = session.ExecStmt(fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s.playlists (
|
|
||||||
id uuid,
|
|
||||||
title text,
|
|
||||||
album text,
|
|
||||||
artist text,
|
|
||||||
song_id uuid,
|
|
||||||
PRIMARY KEY (id, title, album, artist))`, keyspace))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create table:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
playlistMetadata := table.Metadata{
|
|
||||||
Name: fmt.Sprintf("%s.playlists", keyspace),
|
|
||||||
Columns: []string{"id", "title", "album", "artist", "song_id"},
|
|
||||||
PartKey: []string{"id"},
|
|
||||||
SortKey: []string{"title", "album", "artist", "song_id"},
|
|
||||||
}
|
|
||||||
playlistTable := table.New(playlistMetadata)
|
|
||||||
|
|
||||||
// Insert song using query builder.
|
|
||||||
insertSong := qb.Insert(fmt.Sprintf("%s.songs", keyspace)).
|
|
||||||
Columns("id", "title", "album", "artist", "tags", "data").Query(session)
|
|
||||||
|
|
||||||
insertSong.BindStruct(Song{
|
|
||||||
ID: mustParseUUID("756716f7-2e54-4715-9f00-91dcbea6cf50"),
|
|
||||||
Title: "La Petite Tonkinoise",
|
|
||||||
Album: "Bye Bye Blackbird",
|
|
||||||
Artist: "Joséphine Baker",
|
|
||||||
Tags: []string{"jazz", "2013"},
|
|
||||||
Data: []byte("music"),
|
|
||||||
})
|
|
||||||
if err := insertSong.ExecRelease(); err != nil {
|
|
||||||
t.Fatal("ExecRelease() failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert playlist using table model.
|
|
||||||
insertPlaylist := playlistTable.InsertQuery(session)
|
|
||||||
|
|
||||||
insertPlaylist.BindStruct(PlaylistItem{
|
|
||||||
ID: mustParseUUID("2cc9ccb7-6221-4ccb-8387-f22b6a1b354d"),
|
|
||||||
Title: "La Petite Tonkinoise",
|
|
||||||
Album: "Bye Bye Blackbird",
|
|
||||||
Artist: "Joséphine Baker",
|
|
||||||
SongID: mustParseUUID("756716f7-2e54-4715-9f00-91dcbea6cf50"),
|
|
||||||
})
|
|
||||||
if err := insertPlaylist.ExecRelease(); err != nil {
|
|
||||||
t.Fatal("ExecRelease() failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query and displays data.
|
|
||||||
queryPlaylist := playlistTable.SelectQuery(session)
|
|
||||||
|
|
||||||
queryPlaylist.BindStruct(&PlaylistItem{
|
|
||||||
ID: mustParseUUID("2cc9ccb7-6221-4ccb-8387-f22b6a1b354d"),
|
|
||||||
})
|
|
||||||
|
|
||||||
var items []*PlaylistItem
|
|
||||||
if err := queryPlaylist.Select(&items); err != nil {
|
|
||||||
t.Fatal("Select() failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, i := range items {
|
|
||||||
t.Logf("%+v", *i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example shows how to use query builders and table models to build
|
|
||||||
// queries with all types. It uses "BindStruct" function for parameter binding and "Select"
|
|
||||||
// function for loading data to a slice.
|
|
||||||
func createAndPopulateKeyspaceAllTypes(t *testing.T, session gocqlx.Session) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
err := session.ExecStmt(`CREATE KEYSPACE IF NOT EXISTS examples WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create keyspace:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// generated with schemagen
|
|
||||||
type CheckTypesStruct struct {
|
|
||||||
AsciI string
|
|
||||||
BigInt int64
|
|
||||||
BloB []byte
|
|
||||||
BooleaN bool
|
|
||||||
DatE time.Time
|
|
||||||
DecimaL inf.Dec
|
|
||||||
DoublE float64
|
|
||||||
DuratioN gocql.Duration
|
|
||||||
FloaT float32
|
|
||||||
ID [16]byte
|
|
||||||
InT int32
|
|
||||||
IneT string
|
|
||||||
ListInt []int32
|
|
||||||
MapIntText map[int32]string
|
|
||||||
SetInt []int32
|
|
||||||
SmallInt int16
|
|
||||||
TexT string
|
|
||||||
TimE time.Duration
|
|
||||||
TimestamP time.Time
|
|
||||||
TimeuuiD [16]byte
|
|
||||||
TinyInt int8
|
|
||||||
VarChar string
|
|
||||||
VarInt int64
|
|
||||||
}
|
|
||||||
|
|
||||||
err = session.ExecStmt(`CREATE TABLE IF NOT EXISTS examples.check_types (
|
|
||||||
asci_i ascii,
|
|
||||||
big_int bigint,
|
|
||||||
blo_b blob,
|
|
||||||
boolea_n boolean,
|
|
||||||
dat_e date,
|
|
||||||
decima_l decimal,
|
|
||||||
doubl_e double,
|
|
||||||
duratio_n duration,
|
|
||||||
floa_t float,
|
|
||||||
ine_t inet,
|
|
||||||
in_t int,
|
|
||||||
small_int smallint,
|
|
||||||
tex_t text,
|
|
||||||
tim_e time,
|
|
||||||
timestam_p timestamp,
|
|
||||||
timeuui_d timeuuid,
|
|
||||||
tiny_int tinyint,
|
|
||||||
id uuid PRIMARY KEY,
|
|
||||||
var_char varchar,
|
|
||||||
var_int varint,
|
|
||||||
map_int_text map<int, text>,
|
|
||||||
list_int list<int>,
|
|
||||||
set_int set<int>)`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create table:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// generated with schemagen
|
|
||||||
checkTypesTable := table.New(table.Metadata{
|
|
||||||
Name: "examples.check_types",
|
|
||||||
Columns: []string{
|
|
||||||
"asci_i",
|
|
||||||
"big_int",
|
|
||||||
"blo_b",
|
|
||||||
"boolea_n",
|
|
||||||
"dat_e",
|
|
||||||
"decima_l",
|
|
||||||
"doubl_e",
|
|
||||||
"duratio_n",
|
|
||||||
"floa_t",
|
|
||||||
"id",
|
|
||||||
"in_t",
|
|
||||||
"ine_t",
|
|
||||||
"list_int",
|
|
||||||
"map_int_text",
|
|
||||||
"set_int",
|
|
||||||
"small_int",
|
|
||||||
"tex_t",
|
|
||||||
"tim_e",
|
|
||||||
"timestam_p",
|
|
||||||
"timeuui_d",
|
|
||||||
"tiny_int",
|
|
||||||
"var_char",
|
|
||||||
"var_int",
|
|
||||||
},
|
|
||||||
PartKey: []string{"id"},
|
|
||||||
SortKey: []string{},
|
|
||||||
})
|
|
||||||
|
|
||||||
// Insert song using query builder.
|
|
||||||
insertCheckTypes := qb.Insert("examples.check_types").
|
|
||||||
Columns("asci_i", "big_int", "blo_b", "boolea_n", "dat_e", "decima_l", "doubl_e", "duratio_n", "floa_t",
|
|
||||||
"ine_t", "in_t", "small_int", "tex_t", "tim_e", "timestam_p", "timeuui_d", "tiny_int", "id", "var_char",
|
|
||||||
"var_int", "map_int_text", "list_int", "set_int").Query(session)
|
|
||||||
|
|
||||||
var byteID [16]byte
|
|
||||||
id := []byte("756716f7-2e54-4715-9f00-91dcbea6cf50")
|
|
||||||
copy(byteID[:], id)
|
|
||||||
|
|
||||||
date := time.Date(2021, time.December, 11, 10, 23, 0, 0, time.UTC)
|
|
||||||
var double float64 = 1.2 //nolint:staticcheck // type needs to be enforces
|
|
||||||
var float float32 = 1.3
|
|
||||||
var integer int32 = 123
|
|
||||||
listInt := []int32{1, 2, 3}
|
|
||||||
mapIntStr := map[int32]string{
|
|
||||||
1: "a",
|
|
||||||
2: "b",
|
|
||||||
}
|
|
||||||
setInt := []int32{2, 4, 6}
|
|
||||||
var smallInt int16 = 12
|
|
||||||
var tinyInt int8 = 14
|
|
||||||
var varInt int64 = 20
|
|
||||||
|
|
||||||
insertCheckTypes.BindStruct(CheckTypesStruct{
|
|
||||||
AsciI: "test qscci",
|
|
||||||
BigInt: 9223372036854775806, // MAXINT64 - 1,
|
|
||||||
BloB: []byte("this is blob test"),
|
|
||||||
BooleaN: false,
|
|
||||||
DatE: date,
|
|
||||||
DecimaL: *inf.NewDec(1, 1),
|
|
||||||
DoublE: double,
|
|
||||||
DuratioN: gocql.Duration{Months: 1, Days: 1, Nanoseconds: 86400},
|
|
||||||
FloaT: float,
|
|
||||||
ID: byteID,
|
|
||||||
InT: integer,
|
|
||||||
IneT: "127.0.0.1",
|
|
||||||
ListInt: listInt,
|
|
||||||
MapIntText: mapIntStr,
|
|
||||||
SetInt: setInt,
|
|
||||||
SmallInt: smallInt,
|
|
||||||
TexT: "text example",
|
|
||||||
TimE: 86400000000,
|
|
||||||
TimestamP: date,
|
|
||||||
TimeuuiD: gocql.TimeUUID(),
|
|
||||||
TinyInt: tinyInt,
|
|
||||||
VarChar: "test varchar",
|
|
||||||
VarInt: varInt,
|
|
||||||
})
|
|
||||||
if err := insertCheckTypes.ExecRelease(); err != nil {
|
|
||||||
t.Fatal("ExecRelease() failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query and displays data.
|
|
||||||
queryCheckTypes := checkTypesTable.SelectQuery(session)
|
|
||||||
|
|
||||||
queryCheckTypes.BindStruct(&CheckTypesStruct{
|
|
||||||
ID: byteID,
|
|
||||||
})
|
|
||||||
|
|
||||||
var items []*CheckTypesStruct
|
|
||||||
if err := queryCheckTypes.Select(&items); err != nil {
|
|
||||||
t.Fatal("Select() failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, i := range items {
|
|
||||||
t.Logf("%+v", *i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example shows how to load a single value using "Get" function.
|
|
||||||
// Get can also work with UDTs and types that implement gocql marshalling functions.
|
|
||||||
func basicReadScyllaVersion(t *testing.T, session gocqlx.Session) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
var releaseVersion string
|
|
||||||
|
|
||||||
err := session.Query("SELECT release_version FROM system.local", nil).Get(&releaseVersion)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Get() failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Scylla version is: %s", releaseVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This examples shows how to bind data from a map using "BindMap" function,
|
|
||||||
// override field name mapping using the "db" tags, with the default mechanism of
|
|
||||||
// handling situations where driver returns more coluns that we are ready to
|
|
||||||
// consume.
|
|
||||||
func datatypesBlob(t *testing.T, session gocqlx.Session) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
err := session.ExecStmt(`CREATE KEYSPACE IF NOT EXISTS examples WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create keyspace:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = session.ExecStmt(`CREATE TABLE IF NOT EXISTS examples.blobs(k int PRIMARY KEY, b blob, m map<text, blob>)`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create table:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// One way to get a byte buffer is to allocate it and fill it yourself:
|
|
||||||
var buf [16]byte
|
|
||||||
for i := range buf {
|
|
||||||
buf[i] = 0xff
|
|
||||||
}
|
|
||||||
|
|
||||||
insert := qb.Insert("examples.blobs").Columns("k", "b", "m").Query(session)
|
|
||||||
insert.BindMap(qb.M{
|
|
||||||
"k": 1,
|
|
||||||
"b": buf[:],
|
|
||||||
"m": map[string][]byte{"test": buf[:]},
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := insert.ExecRelease(); err != nil {
|
|
||||||
t.Fatal("ExecRelease() failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
row := &struct {
|
|
||||||
Buffer []byte `db:"b"`
|
|
||||||
Mapping map[string][]byte `db:"m"`
|
|
||||||
}{}
|
|
||||||
q := qb.Select("examples.blobs").Where(qb.EqLit("k", "1")).Query(session)
|
|
||||||
|
|
||||||
// By default missing UDT fields are treated as null instead of failing
|
|
||||||
if err := q.Iter().Get(row); err != nil {
|
|
||||||
t.Fatal("Get() failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("%+v", row.Buffer)
|
|
||||||
t.Logf("%+v", row.Mapping)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Coordinates struct {
|
|
||||||
gocqlx.UDT
|
|
||||||
X int
|
|
||||||
Y int
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example shows how to add User Defined Type marshalling capabilities by
|
|
||||||
// adding a single line - embedding gocqlx.UDT.
|
|
||||||
func datatypesUserDefinedType(t *testing.T, session gocqlx.Session) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
err := session.ExecStmt(`CREATE KEYSPACE IF NOT EXISTS examples WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create keyspace:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = session.ExecStmt(`CREATE TYPE IF NOT EXISTS examples.coordinates(x int, y int)`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create type:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = session.ExecStmt(`CREATE TABLE IF NOT EXISTS examples.udts(k int PRIMARY KEY, c coordinates)`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create table:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
coordinates1 := Coordinates{X: 12, Y: 34}
|
|
||||||
coordinates2 := Coordinates{X: 56, Y: 78}
|
|
||||||
|
|
||||||
insert := qb.Insert("examples.udts").Columns("k", "c").Query(session)
|
|
||||||
insert.BindMap(qb.M{
|
|
||||||
"k": 1,
|
|
||||||
"c": coordinates1,
|
|
||||||
})
|
|
||||||
if err := insert.Exec(); err != nil {
|
|
||||||
t.Fatal("Exec() failed:", err)
|
|
||||||
}
|
|
||||||
insert.BindMap(qb.M{
|
|
||||||
"k": 2,
|
|
||||||
"c": coordinates2,
|
|
||||||
})
|
|
||||||
if err := insert.Exec(); err != nil {
|
|
||||||
t.Fatal("Exec() failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var coordinates []Coordinates
|
|
||||||
q := qb.Select("examples.udts").Columns("c").Query(session)
|
|
||||||
if err := q.Select(&coordinates); err != nil {
|
|
||||||
t.Fatal("Select() failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range coordinates {
|
|
||||||
t.Logf("%+v", c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type coordinates struct {
|
|
||||||
X int
|
|
||||||
Y int
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example shows how to add User Defined Type marshalling capabilities to
|
|
||||||
// types that we cannot modify, like library or transfer objects, without
|
|
||||||
// rewriting them in runtime.
|
|
||||||
func datatypesUserDefinedTypeWrapper(t *testing.T, session gocqlx.Session) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
err := session.ExecStmt(`CREATE KEYSPACE IF NOT EXISTS examples WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create keyspace:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = session.ExecStmt(`CREATE TYPE IF NOT EXISTS examples.coordinates(x int, y int)`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create type:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = session.ExecStmt(`CREATE TABLE IF NOT EXISTS examples.udts_wrapper(k int PRIMARY KEY, c coordinates)`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create table:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Embed coordinates within CoordinatesUDT
|
|
||||||
c1 := &coordinates{X: 12, Y: 34}
|
|
||||||
c2 := &coordinates{X: 56, Y: 78}
|
|
||||||
|
|
||||||
type CoordinatesUDT struct {
|
|
||||||
gocqlx.UDT
|
|
||||||
*coordinates
|
|
||||||
}
|
|
||||||
|
|
||||||
coordinates1 := CoordinatesUDT{coordinates: c1}
|
|
||||||
coordinates2 := CoordinatesUDT{coordinates: c2}
|
|
||||||
|
|
||||||
insert := qb.Insert("examples.udts_wrapper").Columns("k", "c").Query(session)
|
|
||||||
insert.BindMap(qb.M{
|
|
||||||
"k": 1,
|
|
||||||
"c": coordinates1,
|
|
||||||
})
|
|
||||||
if err := insert.Exec(); err != nil {
|
|
||||||
t.Fatal("Exec() failed:", err)
|
|
||||||
}
|
|
||||||
insert.BindMap(qb.M{
|
|
||||||
"k": 2,
|
|
||||||
"c": coordinates2,
|
|
||||||
})
|
|
||||||
if err := insert.Exec(); err != nil {
|
|
||||||
t.Fatal("Exec() failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var coordinates []Coordinates
|
|
||||||
q := qb.Select("examples.udts_wrapper").Columns("c").Query(session)
|
|
||||||
if err := q.Select(&coordinates); err != nil {
|
|
||||||
t.Fatal("Select() failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range coordinates {
|
|
||||||
t.Logf("%+v", c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example shows how to use query builder to work with
|
|
||||||
func datatypesJSON(t *testing.T, session gocqlx.Session) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
err := session.ExecStmt(`CREATE KEYSPACE IF NOT EXISTS examples WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create keyspace:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = session.ExecStmt(`CREATE TABLE IF NOT EXISTS examples.querybuilder_json(id int PRIMARY KEY, name text, specs map<text, text>)`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create table:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
insert := qb.Insert("examples.querybuilder_json").Json().Query(session)
|
|
||||||
|
|
||||||
insert.Bind(`{ "id": 1, "name": "Mouse", "specs": { "color": "silver" } }`)
|
|
||||||
if err := insert.Exec(); err != nil {
|
|
||||||
t.Fatal("Exec() failed:", err)
|
|
||||||
}
|
|
||||||
insert.Bind(`{ "id": 2, "name": "Keyboard", "specs": { "layout": "qwerty" } }`)
|
|
||||||
if err := insert.Exec(); err != nil {
|
|
||||||
t.Fatal("Exec() failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// fromJson lets you provide individual columns as JSON:
|
|
||||||
insertFromJSON := qb.Insert("examples.querybuilder_json").
|
|
||||||
Columns("id", "name").
|
|
||||||
FuncColumn("specs", qb.Fn("fromJson", "json")).
|
|
||||||
Query(session)
|
|
||||||
|
|
||||||
insertFromJSON.BindMap(qb.M{
|
|
||||||
"id": 3,
|
|
||||||
"name": "Screen",
|
|
||||||
"json": `{ "size": "24-inch" }`,
|
|
||||||
})
|
|
||||||
if err := insertFromJSON.Exec(); err != nil {
|
|
||||||
t.Fatal("Exec() failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reading the whole row as a JSON object:
|
|
||||||
q := qb.Select("examples.querybuilder_json").
|
|
||||||
Json().
|
|
||||||
Where(qb.EqLit("id", "1")).
|
|
||||||
Query(session)
|
|
||||||
|
|
||||||
var jsonString string
|
|
||||||
|
|
||||||
if err := q.Get(&jsonString); err != nil {
|
|
||||||
t.Fatal("Get() failed:", err)
|
|
||||||
}
|
|
||||||
t.Logf("Entry #1 as JSON: %s", jsonString)
|
|
||||||
|
|
||||||
// Extracting a particular column as JSON:
|
|
||||||
q = qb.Select("examples.querybuilder_json").
|
|
||||||
Columns("id", "toJson(specs) AS json_specs").
|
|
||||||
Where(qb.EqLit("id", "2")).
|
|
||||||
Query(session)
|
|
||||||
|
|
||||||
row := &struct {
|
|
||||||
ID int
|
|
||||||
JSONSpecs string
|
|
||||||
}{}
|
|
||||||
if err := q.Get(row); err != nil {
|
|
||||||
t.Fatal("Get() failed:", err)
|
|
||||||
}
|
|
||||||
t.Logf("Entry #%d's specs as JSON: %s", row.ID, row.JSONSpecs)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Video struct {
|
|
||||||
UserID int
|
|
||||||
UserName string
|
|
||||||
Added time.Time
|
|
||||||
VideoID int
|
|
||||||
Title string
|
|
||||||
}
|
|
||||||
|
|
||||||
func pagingFillTable(t *testing.T, insert *gocqlx.Queryx) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
// 3 users
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
// 49 videos each
|
|
||||||
for j := 0; j < 49; j++ {
|
|
||||||
insert.BindStruct(Video{
|
|
||||||
UserID: i,
|
|
||||||
UserName: fmt.Sprint("user ", i),
|
|
||||||
Added: time.Unix(int64(j)*100, 0),
|
|
||||||
VideoID: i*100 + j,
|
|
||||||
Title: fmt.Sprint("video ", i*100+j),
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := insert.Exec(); err != nil {
|
|
||||||
t.Fatal("Exec() failed:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example shows how to use stateful paging and how "Select" function
|
|
||||||
// can be used to fetch single page only.
|
|
||||||
func pagingForwardPaging(t *testing.T, session gocqlx.Session) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
err := session.ExecStmt(`CREATE KEYSPACE IF NOT EXISTS examples WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create keyspace:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = session.ExecStmt(`CREATE TABLE IF NOT EXISTS examples.paging_forward_paging(
|
|
||||||
user_id int,
|
|
||||||
user_name text,
|
|
||||||
added timestamp,
|
|
||||||
video_id int,
|
|
||||||
title text,
|
|
||||||
PRIMARY KEY (user_id, added, video_id)
|
|
||||||
) WITH CLUSTERING ORDER BY (added DESC, video_id ASC)`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create table:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
videoMetadata := table.Metadata{
|
|
||||||
Name: "examples.paging_forward_paging",
|
|
||||||
Columns: []string{"user_id", "user_name", "added", "video_id", "title"},
|
|
||||||
PartKey: []string{"user_id"},
|
|
||||||
SortKey: []string{"added", "video_id"},
|
|
||||||
}
|
|
||||||
videoTable := table.New(videoMetadata)
|
|
||||||
|
|
||||||
pagingFillTable(t, videoTable.InsertQuery(session))
|
|
||||||
|
|
||||||
// Query and displays data. Iterate over videos of user "1" 10 entries per request.
|
|
||||||
|
|
||||||
const itemsPerPage = 10
|
|
||||||
|
|
||||||
getUserVideos := func(userID int, page []byte) (userVideos []Video, nextPage []byte, err error) {
|
|
||||||
q := videoTable.SelectQuery(session).Bind(userID)
|
|
||||||
defer q.Release()
|
|
||||||
q.PageState(page)
|
|
||||||
q.PageSize(itemsPerPage)
|
|
||||||
|
|
||||||
iter := q.Iter()
|
|
||||||
return userVideos, iter.PageState(), iter.Select(&userVideos)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
userVideos []Video
|
|
||||||
nextPage []byte
|
|
||||||
)
|
|
||||||
|
|
||||||
for i := 1; ; i++ {
|
|
||||||
userVideos, nextPage, err = getUserVideos(1, nextPage)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("load page %d: %s", i, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Page %d:", i)
|
|
||||||
for _, v := range userVideos {
|
|
||||||
t.Logf("%+v", v)
|
|
||||||
}
|
|
||||||
if len(nextPage) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example shows how to efficiently process all rows in a table using
|
|
||||||
// the "token" function. It implements idea from blog post [1]:
|
|
||||||
// As a bonus we use "CompileNamedQueryString" to get named parameters out of
|
|
||||||
// CQL query placeholders like in Python or Java driver.
|
|
||||||
//
|
|
||||||
// [1] https://www.scylladb.com/2017/02/13/efficient-full-table-scans-with-scylla-1-6/.
|
|
||||||
func pagingEfficientFullTableScan(t *testing.T, session gocqlx.Session) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
err := session.ExecStmt(`CREATE KEYSPACE IF NOT EXISTS examples WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create keyspace:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = session.ExecStmt(`CREATE TABLE IF NOT EXISTS examples.paging_efficient_full_table_scan(
|
|
||||||
user_id int,
|
|
||||||
user_name text,
|
|
||||||
added timestamp,
|
|
||||||
video_id int,
|
|
||||||
title text,
|
|
||||||
PRIMARY KEY (user_id, added, video_id)
|
|
||||||
) WITH CLUSTERING ORDER BY (added DESC, video_id ASC)`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create table:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
videoMetadata := table.Metadata{
|
|
||||||
Name: "examples.paging_efficient_full_table_scan",
|
|
||||||
Columns: []string{"user_id", "user_name", "added", "video_id", "title"},
|
|
||||||
PartKey: []string{"user_id"},
|
|
||||||
SortKey: []string{"added", "video_id"},
|
|
||||||
}
|
|
||||||
videoTable := table.New(videoMetadata)
|
|
||||||
|
|
||||||
pagingFillTable(t, videoTable.InsertQuery(session))
|
|
||||||
|
|
||||||
// Calculate optimal number of workers for the cluster:
|
|
||||||
var (
|
|
||||||
nodesInCluster = 1
|
|
||||||
coresInNode = 1
|
|
||||||
smudgeFactor = 3
|
|
||||||
)
|
|
||||||
workers := nodesInCluster * coresInNode * smudgeFactor
|
|
||||||
|
|
||||||
t.Logf("Workers %d", workers)
|
|
||||||
|
|
||||||
type tokenRange struct {
|
|
||||||
Start int64
|
|
||||||
End int64
|
|
||||||
}
|
|
||||||
buf := make(chan tokenRange)
|
|
||||||
|
|
||||||
// sequencer pushes token ranges to buf
|
|
||||||
sequencer := func() error {
|
|
||||||
span := int64(math.MaxInt64 / (50 * workers))
|
|
||||||
|
|
||||||
tr := tokenRange{math.MinInt64, math.MinInt64 + span}
|
|
||||||
for tr.End > tr.Start {
|
|
||||||
buf <- tr
|
|
||||||
tr.Start = tr.End
|
|
||||||
tr.End += span
|
|
||||||
}
|
|
||||||
|
|
||||||
tr.End = math.MaxInt64
|
|
||||||
buf <- tr
|
|
||||||
close(buf)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// worker queries a token ranges generated by sequencer
|
|
||||||
worker := func() error {
|
|
||||||
const cql = `SELECT * FROM examples.paging_efficient_full_table_scan WHERE
|
|
||||||
token(user_id) >= :start AND
|
|
||||||
token(user_id) < :end`
|
|
||||||
|
|
||||||
stmt, names, err := gocqlx.CompileNamedQueryString(cql)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
q := session.Query(stmt, names)
|
|
||||||
defer q.Release()
|
|
||||||
|
|
||||||
var v Video
|
|
||||||
for {
|
|
||||||
tr, ok := <-buf
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
iter := q.BindStruct(tr).Iter()
|
|
||||||
for iter.StructScan(&v) {
|
|
||||||
t.Logf("%+v:", v)
|
|
||||||
}
|
|
||||||
if err := iter.Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query and displays data.
|
|
||||||
|
|
||||||
var wg errgroup.Group
|
|
||||||
wg.Go(sequencer)
|
|
||||||
for i := 0; i < workers; i++ {
|
|
||||||
wg.Go(worker)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := wg.Wait(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example shows how to use Lightweight Transactions (LWT) aka.
|
|
||||||
// Compare-And-Set (CAS) functions.
|
|
||||||
// See: https://docs.scylladb.com/using-scylla/lwt/ for more details.
|
|
||||||
func lwtLock(t *testing.T, session gocqlx.Session) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
err := session.ExecStmt(`CREATE KEYSPACE IF NOT EXISTS examples WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create keyspace:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Lock struct {
|
|
||||||
Name string
|
|
||||||
Owner string
|
|
||||||
TTL int64
|
|
||||||
}
|
|
||||||
|
|
||||||
err = session.ExecStmt(`CREATE TABLE examples.lock (name text PRIMARY KEY, owner text)`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create table:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
extend := func(lock Lock) bool {
|
|
||||||
q := qb.Update("examples.lock").
|
|
||||||
Set("owner").
|
|
||||||
Where(qb.Eq("name")).
|
|
||||||
If(qb.Eq("owner")).
|
|
||||||
TTLNamed("ttl").
|
|
||||||
Query(session).
|
|
||||||
SerialConsistency(gocql.Serial).
|
|
||||||
BindStruct(lock)
|
|
||||||
|
|
||||||
applied, err := q.ExecCASRelease()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("ExecCASRelease() failed:", err)
|
|
||||||
}
|
|
||||||
return applied
|
|
||||||
}
|
|
||||||
|
|
||||||
acquire := func(lock Lock) (applied bool) {
|
|
||||||
var prev Lock
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
t.Logf("Acquire %+v applied %v owner %+v)", lock, applied, prev)
|
|
||||||
}()
|
|
||||||
|
|
||||||
q := qb.Insert("examples.lock").
|
|
||||||
Columns("name", "owner").
|
|
||||||
TTLNamed("ttl").
|
|
||||||
Unique().
|
|
||||||
Query(session).
|
|
||||||
SerialConsistency(gocql.Serial).
|
|
||||||
BindStruct(lock)
|
|
||||||
|
|
||||||
applied, err = q.GetCASRelease(&prev)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("GetCASRelease() failed:", err)
|
|
||||||
}
|
|
||||||
if applied {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if prev.Owner == lock.Owner {
|
|
||||||
return extend(lock)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
resource = "acme"
|
|
||||||
ttl = time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
l1 := Lock{
|
|
||||||
Name: resource,
|
|
||||||
Owner: "1",
|
|
||||||
TTL: qb.TTL(ttl),
|
|
||||||
}
|
|
||||||
|
|
||||||
l2 := Lock{
|
|
||||||
Name: resource,
|
|
||||||
Owner: "2",
|
|
||||||
TTL: qb.TTL(ttl),
|
|
||||||
}
|
|
||||||
|
|
||||||
if !acquire(l1) {
|
|
||||||
t.Fatal("l1 failed to acquire lock")
|
|
||||||
}
|
|
||||||
if acquire(l2) {
|
|
||||||
t.Fatal("unexpectedly l2 acquired lock")
|
|
||||||
}
|
|
||||||
if !acquire(l1) {
|
|
||||||
t.Fatal("l1 failed to extend lock")
|
|
||||||
}
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
if !acquire(l2) {
|
|
||||||
t.Fatal("l2 failed to acquire lock")
|
|
||||||
}
|
|
||||||
if acquire(l1) {
|
|
||||||
t.Fatal("unexpectedly l1 acquired lock")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example shows how to reuse the same insert statement with
|
|
||||||
// partially filled parameters without generating tombstones for empty columns.
|
|
||||||
func unsetEmptyValues(t *testing.T, session gocqlx.Session) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
err := session.ExecStmt(`CREATE KEYSPACE IF NOT EXISTS examples WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create keyspace:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Operation struct {
|
|
||||||
ID string
|
|
||||||
ClientID string
|
|
||||||
Type string
|
|
||||||
PaymentID string
|
|
||||||
Fee *inf.Dec
|
|
||||||
}
|
|
||||||
err = session.ExecStmt(`CREATE TABLE IF NOT EXISTS examples.operations (
|
|
||||||
id text PRIMARY KEY,
|
|
||||||
client_id text,
|
|
||||||
type text,
|
|
||||||
payment_id text,
|
|
||||||
fee decimal)`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("create table:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
insertOperation := qb.Insert("examples.operations").
|
|
||||||
Columns("id", "client_id", "type", "payment_id", "fee")
|
|
||||||
|
|
||||||
// Insert operation with empty paymentID.
|
|
||||||
insertQuery := insertOperation.Query(session).
|
|
||||||
WithBindTransformer(gocqlx.UnsetEmptyTransformer).
|
|
||||||
BindStruct(Operation{
|
|
||||||
ID: "1",
|
|
||||||
ClientID: "42",
|
|
||||||
Type: "Transfer",
|
|
||||||
Fee: inf.NewDec(1, 1),
|
|
||||||
})
|
|
||||||
if err := insertQuery.ExecRelease(); err != nil {
|
|
||||||
t.Fatal("ExecRelease() failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set default transformer to avoid setting it for each query.
|
|
||||||
gocqlx.DefaultBindTransformer = gocqlx.UnsetEmptyTransformer
|
|
||||||
defer func() {
|
|
||||||
gocqlx.DefaultBindTransformer = nil
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Insert operation with empty fee.
|
|
||||||
insertQuery = insertOperation.Query(session).
|
|
||||||
BindStruct(Operation{
|
|
||||||
ID: "2",
|
|
||||||
ClientID: "42",
|
|
||||||
Type: "Input",
|
|
||||||
PaymentID: "1",
|
|
||||||
})
|
|
||||||
if err := insertQuery.ExecRelease(); err != nil {
|
|
||||||
t.Fatal("ExecRelease() failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query and displays data.
|
|
||||||
var ops []*Operation
|
|
||||||
if err := qb.Select("examples.operations").Query(session).Select(&ops); err != nil {
|
|
||||||
t.Fatal("Select() failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, op := range ops {
|
|
||||||
t.Logf("%+v", *op)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustParseUUID(s string) gocql.UUID {
|
|
||||||
u, err := gocql.ParseUUID(s)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
1015
iterx_test.go
1015
iterx_test.go
File diff suppressed because it is too large
Load Diff
@@ -1,14 +0,0 @@
|
|||||||
// Copyright (C) 2017 ScyllaDB
|
|
||||||
// Use of this source code is governed by a ALv2-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package qb
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func BenchmarkBatchBuilder(b *testing.B) {
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
Batch().Add(mockBuilder{"INSERT INTO cycling.cyclist_name (id,user_uuid,firstname) VALUES (?,?,?) ", []string{"id", "user_uuid", "firstname"}}).ToCql()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
// Copyright (C) 2017 ScyllaDB
|
|
||||||
// Use of this source code is governed by a ALv2-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package qb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
)
|
|
||||||
|
|
||||||
type mockBuilder struct {
|
|
||||||
stmt string
|
|
||||||
names []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b mockBuilder) ToCql() (stmt string, names []string) {
|
|
||||||
return b.stmt, b.names
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBatchBuilder(t *testing.T) {
|
|
||||||
m := mockBuilder{"INSERT INTO cycling.cyclist_name (id,user_uuid,firstname) VALUES (?,?,?) ", []string{"id", "user_uuid", "firstname"}}
|
|
||||||
|
|
||||||
table := []struct {
|
|
||||||
B *BatchBuilder
|
|
||||||
N []string
|
|
||||||
S string
|
|
||||||
}{
|
|
||||||
// Basic test for Batch
|
|
||||||
{
|
|
||||||
B: Batch().Add(m),
|
|
||||||
S: "BEGIN BATCH INSERT INTO cycling.cyclist_name (id,user_uuid,firstname) VALUES (?,?,?) ; APPLY BATCH ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname"},
|
|
||||||
},
|
|
||||||
// Add statement
|
|
||||||
{
|
|
||||||
B: Batch().
|
|
||||||
AddWithPrefix("a", m).
|
|
||||||
AddWithPrefix("b", m),
|
|
||||||
S: "BEGIN BATCH INSERT INTO cycling.cyclist_name (id,user_uuid,firstname) VALUES (?,?,?) ; " +
|
|
||||||
"INSERT INTO cycling.cyclist_name (id,user_uuid,firstname) VALUES (?,?,?) ; APPLY BATCH ",
|
|
||||||
N: []string{"a.id", "a.user_uuid", "a.firstname", "b.id", "b.user_uuid", "b.firstname"},
|
|
||||||
},
|
|
||||||
// Add UNLOGGED
|
|
||||||
{
|
|
||||||
B: Batch().UnLogged(),
|
|
||||||
S: "BEGIN UNLOGGED BATCH APPLY BATCH ",
|
|
||||||
},
|
|
||||||
// Add COUNTER
|
|
||||||
{
|
|
||||||
B: Batch().Counter(),
|
|
||||||
S: "BEGIN COUNTER BATCH APPLY BATCH ",
|
|
||||||
},
|
|
||||||
// Add TTL
|
|
||||||
{
|
|
||||||
B: Batch().TTL(time.Second),
|
|
||||||
S: "BEGIN BATCH USING TTL 1 APPLY BATCH ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
B: Batch().TTLNamed("ttl"),
|
|
||||||
S: "BEGIN BATCH USING TTL ? APPLY BATCH ",
|
|
||||||
N: []string{"ttl"},
|
|
||||||
},
|
|
||||||
// Add TIMESTAMP
|
|
||||||
{
|
|
||||||
B: Batch().Timestamp(time.Date(2005, 5, 5, 0, 0, 0, 0, time.UTC)),
|
|
||||||
S: "BEGIN BATCH USING TIMESTAMP 1115251200000000 APPLY BATCH ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
B: Batch().TimestampNamed("ts"),
|
|
||||||
S: "BEGIN BATCH USING TIMESTAMP ? APPLY BATCH ",
|
|
||||||
N: []string{"ts"},
|
|
||||||
},
|
|
||||||
// Add TIMEOUT
|
|
||||||
{
|
|
||||||
B: Batch().Timeout(time.Second),
|
|
||||||
S: "BEGIN BATCH USING TIMEOUT 1s APPLY BATCH ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
B: Batch().TimeoutNamed("to"),
|
|
||||||
S: "BEGIN BATCH USING TIMEOUT ? APPLY BATCH ",
|
|
||||||
N: []string{"to"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range table {
|
|
||||||
stmt, names := test.B.ToCql()
|
|
||||||
if diff := cmp.Diff(test.S, stmt); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(test.N, names); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
// Copyright (C) 2017 ScyllaDB
|
|
||||||
// Use of this source code is governed by a ALv2-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package qb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func BenchmarkCmp(b *testing.B) {
|
|
||||||
buf := bytes.Buffer{}
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
buf.Reset()
|
|
||||||
c := cmps{
|
|
||||||
Eq("id"),
|
|
||||||
Lt("user_uuid"),
|
|
||||||
LtOrEq("firstname"),
|
|
||||||
Gt("stars"),
|
|
||||||
}
|
|
||||||
c.writeCql(&buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
// Copyright (C) 2017 ScyllaDB
|
|
||||||
// Use of this source code is governed by a ALv2-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package qb
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func BenchmarkDeleteBuilder(b *testing.B) {
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
Delete("cycling.cyclist_name").Columns("id", "user_uuid", "firstname", "stars").Where(Eq("id")).ToCql()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
// Copyright (C) 2017 ScyllaDB
|
|
||||||
// Use of this source code is governed by a ALv2-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package qb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDeleteBuilder(t *testing.T) {
|
|
||||||
w := EqNamed("id", "expr")
|
|
||||||
|
|
||||||
table := []struct {
|
|
||||||
B *DeleteBuilder
|
|
||||||
N []string
|
|
||||||
S string
|
|
||||||
}{
|
|
||||||
// Basic test for delete
|
|
||||||
{
|
|
||||||
B: Delete("cycling.cyclist_name").Where(w),
|
|
||||||
S: "DELETE FROM cycling.cyclist_name WHERE id=? ",
|
|
||||||
N: []string{"expr"},
|
|
||||||
},
|
|
||||||
// Change table name
|
|
||||||
{
|
|
||||||
B: Delete("cycling.cyclist_name").Where(w).From("Foobar"),
|
|
||||||
S: "DELETE FROM Foobar WHERE id=? ",
|
|
||||||
N: []string{"expr"},
|
|
||||||
},
|
|
||||||
// Add column
|
|
||||||
{
|
|
||||||
B: Delete("cycling.cyclist_name").Where(w).Columns("stars"),
|
|
||||||
S: "DELETE stars FROM cycling.cyclist_name WHERE id=? ",
|
|
||||||
N: []string{"expr"},
|
|
||||||
},
|
|
||||||
// Add WHERE
|
|
||||||
{
|
|
||||||
B: Delete("cycling.cyclist_name").Where(w, Gt("firstname")),
|
|
||||||
S: "DELETE FROM cycling.cyclist_name WHERE id=? AND firstname>? ",
|
|
||||||
N: []string{"expr", "firstname"},
|
|
||||||
},
|
|
||||||
// Add a tuple column
|
|
||||||
{
|
|
||||||
B: Delete("cycling.cyclist_name").Where(EqTuple("id", 2)).Columns("stars"),
|
|
||||||
S: "DELETE stars FROM cycling.cyclist_name WHERE id=(?,?) ",
|
|
||||||
N: []string{"id[0]", "id[1]"},
|
|
||||||
},
|
|
||||||
// Add WHERE for tuple column
|
|
||||||
{
|
|
||||||
B: Delete("cycling.cyclist_name").Where(w, GtTuple("firstname", 2)),
|
|
||||||
S: "DELETE FROM cycling.cyclist_name WHERE id=? AND firstname>(?,?) ",
|
|
||||||
N: []string{"expr", "firstname[0]", "firstname[1]"},
|
|
||||||
},
|
|
||||||
// Add WHERE for all tuple columns
|
|
||||||
{
|
|
||||||
B: Delete("cycling.cyclist_name").Where(EqTuple("id", 2), GtTuple("firstname", 2)),
|
|
||||||
S: "DELETE FROM cycling.cyclist_name WHERE id=(?,?) AND firstname>(?,?) ",
|
|
||||||
N: []string{"id[0]", "id[1]", "firstname[0]", "firstname[1]"},
|
|
||||||
},
|
|
||||||
// Add IF
|
|
||||||
{
|
|
||||||
B: Delete("cycling.cyclist_name").Where(w).If(Gt("firstname")),
|
|
||||||
S: "DELETE FROM cycling.cyclist_name WHERE id=? IF firstname>? ",
|
|
||||||
N: []string{"expr", "firstname"},
|
|
||||||
},
|
|
||||||
// Add TIMESTAMP
|
|
||||||
{
|
|
||||||
B: Delete("cycling.cyclist_name").Where(w).Timestamp(time.Date(2005, 5, 5, 0, 0, 0, 0, time.UTC)),
|
|
||||||
S: "DELETE FROM cycling.cyclist_name USING TIMESTAMP 1115251200000000 WHERE id=? ",
|
|
||||||
N: []string{"expr"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
B: Delete("cycling.cyclist_name").Where(w).TimestampNamed("ts"),
|
|
||||||
S: "DELETE FROM cycling.cyclist_name USING TIMESTAMP ? WHERE id=? ",
|
|
||||||
N: []string{"ts", "expr"},
|
|
||||||
},
|
|
||||||
// Add TIMEOUT
|
|
||||||
{
|
|
||||||
B: Delete("cycling.cyclist_name").Where(w).Timeout(time.Second),
|
|
||||||
S: "DELETE FROM cycling.cyclist_name USING TIMEOUT 1s WHERE id=? ",
|
|
||||||
N: []string{"expr"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
B: Delete("cycling.cyclist_name").Where(w).TimeoutNamed("to"),
|
|
||||||
S: "DELETE FROM cycling.cyclist_name USING TIMEOUT ? WHERE id=? ",
|
|
||||||
N: []string{"to", "expr"},
|
|
||||||
},
|
|
||||||
// Add IF EXISTS
|
|
||||||
{
|
|
||||||
B: Delete("cycling.cyclist_name").Where(w).Existing(),
|
|
||||||
S: "DELETE FROM cycling.cyclist_name WHERE id=? IF EXISTS ",
|
|
||||||
N: []string{"expr"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range table {
|
|
||||||
stmt, names := test.B.ToCql()
|
|
||||||
if diff := cmp.Diff(test.S, stmt); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(test.N, names); diff != "" {
|
|
||||||
t.Error(diff, names)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
// Copyright (C) 2017 ScyllaDB
|
|
||||||
// Use of this source code is governed by a ALv2-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package qb
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func BenchmarkInsertBuilder(b *testing.B) {
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname", "stars").ToCql()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
// Copyright (C) 2017 ScyllaDB
|
|
||||||
// Use of this source code is governed by a ALv2-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package qb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestInsertBuilder(t *testing.T) {
|
|
||||||
table := []struct {
|
|
||||||
B *InsertBuilder
|
|
||||||
N []string
|
|
||||||
S string
|
|
||||||
}{
|
|
||||||
// Basic test for insert
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname"),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name (id,user_uuid,firstname) VALUES (?,?,?) ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname"},
|
|
||||||
},
|
|
||||||
// Basic test for insert JSON
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").Json(),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name JSON ?",
|
|
||||||
N: nil,
|
|
||||||
},
|
|
||||||
// Change table name
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").Into("Foobar"),
|
|
||||||
S: "INSERT INTO Foobar (id,user_uuid,firstname) VALUES (?,?,?) ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname"},
|
|
||||||
},
|
|
||||||
// Add columns
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").Columns("stars"),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name (id,user_uuid,firstname,stars) VALUES (?,?,?,?) ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname", "stars"},
|
|
||||||
},
|
|
||||||
// Add a named column
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").NamedColumn("stars", "stars_name"),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name (id,user_uuid,firstname,stars) VALUES (?,?,?,?) ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname", "stars_name"},
|
|
||||||
},
|
|
||||||
// Add a literal column
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").LitColumn("stars", "stars_lit"),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name (id,user_uuid,firstname,stars) VALUES (?,?,?,stars_lit) ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname"},
|
|
||||||
},
|
|
||||||
// Add TTL
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").TTL(time.Second),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name (id,user_uuid,firstname) VALUES (?,?,?) USING TTL 1 ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").TTLNamed("ttl"),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name (id,user_uuid,firstname) VALUES (?,?,?) USING TTL ? ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname", "ttl"},
|
|
||||||
},
|
|
||||||
// Add TIMESTAMP
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").Timestamp(time.Date(2005, 5, 5, 0, 0, 0, 0, time.UTC)),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name (id,user_uuid,firstname) VALUES (?,?,?) USING TIMESTAMP 1115251200000000 ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").TimestampNamed("ts"),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name (id,user_uuid,firstname) VALUES (?,?,?) USING TIMESTAMP ? ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname", "ts"},
|
|
||||||
},
|
|
||||||
// Add TIMESTAMP
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").Timestamp(time.Date(2005, 5, 5, 0, 0, 0, 0, time.UTC)),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name (id,user_uuid,firstname) VALUES (?,?,?) USING TIMESTAMP 1115251200000000 ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").TimestampNamed("ts"),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name (id,user_uuid,firstname) VALUES (?,?,?) USING TIMESTAMP ? ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname", "ts"},
|
|
||||||
},
|
|
||||||
// Add TIMESTAMP
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").Timestamp(time.Date(2005, 5, 5, 0, 0, 0, 0, time.UTC)),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name (id,user_uuid,firstname) VALUES (?,?,?) USING TIMESTAMP 1115251200000000 ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").TimestampNamed("ts"),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name (id,user_uuid,firstname) VALUES (?,?,?) USING TIMESTAMP ? ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname", "ts"},
|
|
||||||
},
|
|
||||||
// Add TIMEOUT
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").Timeout(time.Second),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name (id,user_uuid,firstname) VALUES (?,?,?) USING TIMEOUT 1s ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").TimeoutNamed("to"),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name (id,user_uuid,firstname) VALUES (?,?,?) USING TIMEOUT ? ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname", "to"},
|
|
||||||
},
|
|
||||||
// Add TupleColumn
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").TupleColumn("id", 2),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name (id) VALUES ((?,?)) ",
|
|
||||||
N: []string{"id[0]", "id[1]"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").TupleColumn("id", 2).Columns("user_uuid"),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name (id,user_uuid) VALUES ((?,?),?) ",
|
|
||||||
N: []string{"id[0]", "id[1]", "user_uuid"},
|
|
||||||
},
|
|
||||||
// Add IF NOT EXISTS
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").Unique(),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name (id,user_uuid,firstname) VALUES (?,?,?) IF NOT EXISTS ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname"},
|
|
||||||
},
|
|
||||||
// Add FuncColumn
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").FuncColumn("id", Now()),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name (id) VALUES (now()) ",
|
|
||||||
N: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
B: Insert("cycling.cyclist_name").FuncColumn("id", Now()).Columns("user_uuid"),
|
|
||||||
S: "INSERT INTO cycling.cyclist_name (id,user_uuid) VALUES (now(),?) ",
|
|
||||||
N: []string{"user_uuid"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range table {
|
|
||||||
stmt, names := test.B.ToCql()
|
|
||||||
if diff := cmp.Diff(test.S, stmt); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(test.N, names); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
// Copyright (C) 2017 ScyllaDB
|
|
||||||
// Use of this source code is governed by a ALv2-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package qb
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func BenchmarkSelectBuilder(b *testing.B) {
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
Select("cycling.cyclist_name").
|
|
||||||
Columns("id", "user_uuid", "firstname", "surname", "stars").
|
|
||||||
Where(Eq("id")).
|
|
||||||
ToCql()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSelectBuildAssign(b *testing.B) {
|
|
||||||
b.ResetTimer()
|
|
||||||
cols := []string{
|
|
||||||
"id", "user_uuid", "firstname",
|
|
||||||
"surname", "stars",
|
|
||||||
}
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
Select("cycling.cyclist_name").
|
|
||||||
Columns(cols...).
|
|
||||||
Where(Eq("id")).
|
|
||||||
ToCql()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
// Copyright (C) 2017 ScyllaDB
|
|
||||||
// Use of this source code is governed by a ALv2-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package qb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSelectBuilder(t *testing.T) {
|
|
||||||
w := EqNamed("id", "expr")
|
|
||||||
|
|
||||||
table := []struct {
|
|
||||||
B *SelectBuilder
|
|
||||||
N []string
|
|
||||||
S string
|
|
||||||
}{
|
|
||||||
// Basic test for select *
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name"),
|
|
||||||
S: "SELECT * FROM cycling.cyclist_name ",
|
|
||||||
},
|
|
||||||
// Basic test for select columns
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Columns("id", "user_uuid", "firstname"),
|
|
||||||
S: "SELECT id,user_uuid,firstname FROM cycling.cyclist_name ",
|
|
||||||
},
|
|
||||||
// Add a SELECT AS column
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Columns("id", "user_uuid", As("firstname", "name")),
|
|
||||||
S: "SELECT id,user_uuid,firstname AS name FROM cycling.cyclist_name ",
|
|
||||||
},
|
|
||||||
// Basic test for select columns as JSON
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").Json(),
|
|
||||||
S: "SELECT JSON id,user_uuid,firstname FROM cycling.cyclist_name ",
|
|
||||||
},
|
|
||||||
// Add a SELECT AS column as JSON
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Columns("id", "user_uuid", As("firstname", "name")).Json(),
|
|
||||||
S: "SELECT JSON id,user_uuid,firstname AS name FROM cycling.cyclist_name ",
|
|
||||||
},
|
|
||||||
// Add a SELECT AS column 2
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").
|
|
||||||
Columns(As("firstname", "name"), "id", As("user_uuid", "user")),
|
|
||||||
S: "SELECT firstname AS name,id,user_uuid AS user FROM cycling.cyclist_name ",
|
|
||||||
},
|
|
||||||
// Basic test for select distinct
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Distinct("id"),
|
|
||||||
S: "SELECT DISTINCT id FROM cycling.cyclist_name ",
|
|
||||||
},
|
|
||||||
// Change table name
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").From("Foobar"),
|
|
||||||
S: "SELECT * FROM Foobar ",
|
|
||||||
},
|
|
||||||
// Add WHERE
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Where(w, Gt("firstname")),
|
|
||||||
S: "SELECT * FROM cycling.cyclist_name WHERE id=? AND firstname>? ",
|
|
||||||
N: []string{"expr", "firstname"},
|
|
||||||
},
|
|
||||||
// Add WHERE with tuple
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Where(EqTuple("id", 2), Gt("firstname")),
|
|
||||||
S: "SELECT * FROM cycling.cyclist_name WHERE id=(?,?) AND firstname>? ",
|
|
||||||
N: []string{"id[0]", "id[1]", "firstname"},
|
|
||||||
},
|
|
||||||
// Add WHERE with only tuples
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Where(EqTuple("id", 2), GtTuple("firstname", 2)),
|
|
||||||
S: "SELECT * FROM cycling.cyclist_name WHERE id=(?,?) AND firstname>(?,?) ",
|
|
||||||
N: []string{"id[0]", "id[1]", "firstname[0]", "firstname[1]"},
|
|
||||||
},
|
|
||||||
// Add TIMEOUT
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Where(w, Gt("firstname")).Timeout(time.Second),
|
|
||||||
S: "SELECT * FROM cycling.cyclist_name WHERE id=? AND firstname>? USING TIMEOUT 1s ",
|
|
||||||
N: []string{"expr", "firstname"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Where(w, Gt("firstname")).TimeoutNamed("to"),
|
|
||||||
S: "SELECT * FROM cycling.cyclist_name WHERE id=? AND firstname>? USING TIMEOUT ? ",
|
|
||||||
N: []string{"expr", "firstname", "to"},
|
|
||||||
},
|
|
||||||
// Add GROUP BY
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Columns("MAX(stars) as max_stars").GroupBy("id"),
|
|
||||||
S: "SELECT id,MAX(stars) as max_stars FROM cycling.cyclist_name GROUP BY id ",
|
|
||||||
},
|
|
||||||
// Add GROUP BY
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").GroupBy("id"),
|
|
||||||
S: "SELECT id FROM cycling.cyclist_name GROUP BY id ",
|
|
||||||
},
|
|
||||||
// Add GROUP BY two columns
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").GroupBy("id", "user_uuid"),
|
|
||||||
S: "SELECT id,user_uuid FROM cycling.cyclist_name GROUP BY id,user_uuid ",
|
|
||||||
},
|
|
||||||
// Add ORDER BY
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Where(w).OrderBy("firstname", ASC),
|
|
||||||
S: "SELECT * FROM cycling.cyclist_name WHERE id=? ORDER BY firstname ASC ",
|
|
||||||
N: []string{"expr"},
|
|
||||||
},
|
|
||||||
// Add ORDER BY
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Where(w).OrderBy("firstname", DESC),
|
|
||||||
S: "SELECT * FROM cycling.cyclist_name WHERE id=? ORDER BY firstname DESC ",
|
|
||||||
N: []string{"expr"},
|
|
||||||
},
|
|
||||||
// Add ORDER BY two columns
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Where(w).OrderBy("firstname", ASC).OrderBy("lastname", DESC),
|
|
||||||
S: "SELECT * FROM cycling.cyclist_name WHERE id=? ORDER BY firstname ASC,lastname DESC ",
|
|
||||||
N: []string{"expr"},
|
|
||||||
},
|
|
||||||
// Add LIMIT
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Where(w).Limit(10),
|
|
||||||
S: "SELECT * FROM cycling.cyclist_name WHERE id=? LIMIT 10 ",
|
|
||||||
N: []string{"expr"},
|
|
||||||
},
|
|
||||||
// Add named LIMIT
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Where(w).LimitNamed("limit"),
|
|
||||||
S: "SELECT * FROM cycling.cyclist_name WHERE id=? LIMIT ? ",
|
|
||||||
N: []string{"expr", "limit"},
|
|
||||||
},
|
|
||||||
// Add PER PARTITION LIMIT
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Where(w).LimitPerPartition(10),
|
|
||||||
S: "SELECT * FROM cycling.cyclist_name WHERE id=? PER PARTITION LIMIT 10 ",
|
|
||||||
N: []string{"expr"},
|
|
||||||
},
|
|
||||||
// Add named PER PARTITION LIMIT
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Where(w).LimitPerPartitionNamed("partition_limit"),
|
|
||||||
S: "SELECT * FROM cycling.cyclist_name WHERE id=? PER PARTITION LIMIT ? ",
|
|
||||||
N: []string{"expr", "partition_limit"},
|
|
||||||
},
|
|
||||||
// Add PER PARTITION LIMIT and LIMIT
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Where(w).LimitPerPartition(2).Limit(10),
|
|
||||||
S: "SELECT * FROM cycling.cyclist_name WHERE id=? PER PARTITION LIMIT 2 LIMIT 10 ",
|
|
||||||
N: []string{"expr"},
|
|
||||||
},
|
|
||||||
// Add named PER PARTITION LIMIT and LIMIT
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Where(w).LimitPerPartitionNamed("partition_limit").LimitNamed("limit"),
|
|
||||||
S: "SELECT * FROM cycling.cyclist_name WHERE id=? PER PARTITION LIMIT ? LIMIT ? ",
|
|
||||||
N: []string{"expr", "partition_limit", "limit"},
|
|
||||||
},
|
|
||||||
// Add ALLOW FILTERING
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Where(w).AllowFiltering(),
|
|
||||||
S: "SELECT * FROM cycling.cyclist_name WHERE id=? ALLOW FILTERING ",
|
|
||||||
N: []string{"expr"},
|
|
||||||
},
|
|
||||||
// Add ALLOW FILTERING and BYPASS CACHE
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Where(w).AllowFiltering().BypassCache(),
|
|
||||||
S: "SELECT * FROM cycling.cyclist_name WHERE id=? ALLOW FILTERING BYPASS CACHE ",
|
|
||||||
N: []string{"expr"},
|
|
||||||
},
|
|
||||||
// Add BYPASS CACHE
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Where(w).BypassCache(),
|
|
||||||
S: "SELECT * FROM cycling.cyclist_name WHERE id=? BYPASS CACHE ",
|
|
||||||
N: []string{"expr"},
|
|
||||||
},
|
|
||||||
// Add COUNT all
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").CountAll().Where(Gt("stars")),
|
|
||||||
S: "SELECT count(*) FROM cycling.cyclist_name WHERE stars>? ",
|
|
||||||
N: []string{"stars"},
|
|
||||||
},
|
|
||||||
// Add COUNT with GROUP BY
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Count("stars").GroupBy("id"),
|
|
||||||
S: "SELECT id,count(stars) FROM cycling.cyclist_name GROUP BY id ",
|
|
||||||
},
|
|
||||||
// Add Min
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Min("stars"),
|
|
||||||
S: "SELECT min(stars) FROM cycling.cyclist_name ",
|
|
||||||
},
|
|
||||||
// Add Sum
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Sum("*"),
|
|
||||||
S: "SELECT sum(*) FROM cycling.cyclist_name ",
|
|
||||||
},
|
|
||||||
// Add Avg
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Avg("stars"),
|
|
||||||
S: "SELECT avg(stars) FROM cycling.cyclist_name ",
|
|
||||||
},
|
|
||||||
// Add Max
|
|
||||||
{
|
|
||||||
B: Select("cycling.cyclist_name").Max("stars"),
|
|
||||||
S: "SELECT max(stars) FROM cycling.cyclist_name ",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range table {
|
|
||||||
stmt, names := test.B.ToCql()
|
|
||||||
if diff := cmp.Diff(test.S, stmt); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(test.N, names); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
136
qb/token_test.go
136
qb/token_test.go
@@ -1,136 +0,0 @@
|
|||||||
// Copyright (C) 2017 ScyllaDB
|
|
||||||
// Use of this source code is governed by a ALv2-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package qb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestToken(t *testing.T) {
|
|
||||||
table := []struct {
|
|
||||||
C Cmp
|
|
||||||
S string
|
|
||||||
N []string
|
|
||||||
}{
|
|
||||||
// Basic comparators
|
|
||||||
{
|
|
||||||
C: Token("a", "b").Eq(),
|
|
||||||
S: "token(a,b)=token(?,?)",
|
|
||||||
N: []string{"a", "b"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
C: Token("a", "b").Lt(),
|
|
||||||
S: "token(a,b)<token(?,?)",
|
|
||||||
N: []string{"a", "b"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
C: Token("a", "b").LtOrEq(),
|
|
||||||
S: "token(a,b)<=token(?,?)",
|
|
||||||
N: []string{"a", "b"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
C: Token("a", "b").Gt(),
|
|
||||||
S: "token(a,b)>token(?,?)",
|
|
||||||
N: []string{"a", "b"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
C: Token("a", "b").GtOrEq(),
|
|
||||||
S: "token(a,b)>=token(?,?)",
|
|
||||||
N: []string{"a", "b"},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Custom bind names
|
|
||||||
{
|
|
||||||
C: Token("a", "b").EqNamed("c", "d"),
|
|
||||||
S: "token(a,b)=token(?,?)",
|
|
||||||
N: []string{"c", "d"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
C: Token("a", "b").LtNamed("c", "d"),
|
|
||||||
S: "token(a,b)<token(?,?)",
|
|
||||||
N: []string{"c", "d"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
C: Token("a", "b").LtOrEqNamed("c", "d"),
|
|
||||||
S: "token(a,b)<=token(?,?)",
|
|
||||||
N: []string{"c", "d"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
C: Token("a", "b").GtNamed("c", "d"),
|
|
||||||
S: "token(a,b)>token(?,?)",
|
|
||||||
N: []string{"c", "d"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
C: Token("a", "b").GtOrEqNamed("c", "d"),
|
|
||||||
S: "token(a,b)>=token(?,?)",
|
|
||||||
N: []string{"c", "d"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
C: Token("a", "b").EqValue(),
|
|
||||||
S: "token(a,b)=?",
|
|
||||||
N: []string{"token"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
C: Token("a", "b").EqValueNamed("c"),
|
|
||||||
S: "token(a,b)=?",
|
|
||||||
N: []string{"c"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
C: Token("a", "b").LtValue(),
|
|
||||||
S: "token(a,b)<?",
|
|
||||||
N: []string{"token"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
C: Token("a", "b").LtValueNamed("c"),
|
|
||||||
S: "token(a,b)<?",
|
|
||||||
N: []string{"c"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
C: Token("a", "b").LtOrEqValue(),
|
|
||||||
S: "token(a,b)<=?",
|
|
||||||
N: []string{"token"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
C: Token("a", "b").LtOrEqValueNamed("c"),
|
|
||||||
S: "token(a,b)<=?",
|
|
||||||
N: []string{"c"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
C: Token("a", "b").GtValue(),
|
|
||||||
S: "token(a,b)>?",
|
|
||||||
N: []string{"token"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
C: Token("a", "b").GtValueNamed("c"),
|
|
||||||
S: "token(a,b)>?",
|
|
||||||
N: []string{"c"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
C: Token("a", "b").GtOrEqValue(),
|
|
||||||
S: "token(a,b)>=?",
|
|
||||||
N: []string{"token"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
C: Token("a", "b").GtOrEqValueNamed("c"),
|
|
||||||
S: "token(a,b)>=?",
|
|
||||||
N: []string{"c"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := bytes.Buffer{}
|
|
||||||
for _, test := range table {
|
|
||||||
buf.Reset()
|
|
||||||
name := test.C.writeCql(&buf)
|
|
||||||
if diff := cmp.Diff(test.S, buf.String()); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(test.N, name); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
// Copyright (C) 2017 ScyllaDB
|
|
||||||
// Use of this source code is governed by a ALv2-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package qb
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func BenchmarkUpdateBuilder(b *testing.B) {
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
Update("cycling.cyclist_name").Set("id", "user_uuid", "firstname", "stars").Where(Eq("id")).ToCql()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
// Copyright (C) 2017 ScyllaDB
|
|
||||||
// Use of this source code is governed by a ALv2-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package qb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestUpdateBuilder(t *testing.T) {
|
|
||||||
w := EqNamed("id", "expr")
|
|
||||||
|
|
||||||
table := []struct {
|
|
||||||
B *UpdateBuilder
|
|
||||||
N []string
|
|
||||||
S string
|
|
||||||
}{
|
|
||||||
// Basic test for update
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").Set("id", "user_uuid", "firstname").Where(w),
|
|
||||||
S: "UPDATE cycling.cyclist_name SET id=?,user_uuid=?,firstname=? WHERE id=? ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname", "expr"},
|
|
||||||
},
|
|
||||||
// Change table name
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").Set("id", "user_uuid", "firstname").Where(w).Table("Foobar"),
|
|
||||||
S: "UPDATE Foobar SET id=?,user_uuid=?,firstname=? WHERE id=? ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname", "expr"},
|
|
||||||
},
|
|
||||||
// Add SET
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").Set("id", "user_uuid", "firstname").Where(w).Set("stars"),
|
|
||||||
S: "UPDATE cycling.cyclist_name SET id=?,user_uuid=?,firstname=?,stars=? WHERE id=? ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname", "stars", "expr"},
|
|
||||||
},
|
|
||||||
// Add SET literal
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").SetLit("user_uuid", "literal_uuid").Where(w).Set("stars"),
|
|
||||||
S: "UPDATE cycling.cyclist_name SET user_uuid=literal_uuid,stars=? WHERE id=? ",
|
|
||||||
N: []string{"stars", "expr"},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Add SET tuple
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").SetTuple("id", 2).Set("user_uuid", "firstname").Where(EqTuple("id", 2)),
|
|
||||||
S: "UPDATE cycling.cyclist_name SET id=(?,?),user_uuid=?,firstname=? WHERE id=(?,?) ",
|
|
||||||
N: []string{"id[0]", "id[1]", "user_uuid", "firstname", "id[0]", "id[1]"},
|
|
||||||
},
|
|
||||||
// Add SET SetFunc
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").SetFunc("user_uuid", Fn("someFunc", "param_0", "param_1")).Where(w).Set("stars"),
|
|
||||||
S: "UPDATE cycling.cyclist_name SET user_uuid=someFunc(?,?),stars=? WHERE id=? ",
|
|
||||||
N: []string{"param_0", "param_1", "stars", "expr"},
|
|
||||||
},
|
|
||||||
// Add SET Add
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").Add("total").Where(w),
|
|
||||||
S: "UPDATE cycling.cyclist_name SET total=total+? WHERE id=? ",
|
|
||||||
N: []string{"total", "expr"},
|
|
||||||
},
|
|
||||||
// Add SET AddNamed
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").AddNamed("total", "inc").Where(w),
|
|
||||||
S: "UPDATE cycling.cyclist_name SET total=total+? WHERE id=? ",
|
|
||||||
N: []string{"inc", "expr"},
|
|
||||||
},
|
|
||||||
// Add SET AddLit
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").AddLit("total", "1").Where(w),
|
|
||||||
S: "UPDATE cycling.cyclist_name SET total=total+1 WHERE id=? ",
|
|
||||||
N: []string{"expr"},
|
|
||||||
},
|
|
||||||
// Add SET Remove
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").Remove("total").Where(w),
|
|
||||||
S: "UPDATE cycling.cyclist_name SET total=total-? WHERE id=? ",
|
|
||||||
N: []string{"total", "expr"},
|
|
||||||
},
|
|
||||||
// Add SET RemoveNamed
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").RemoveNamed("total", "dec").Where(w),
|
|
||||||
S: "UPDATE cycling.cyclist_name SET total=total-? WHERE id=? ",
|
|
||||||
N: []string{"dec", "expr"},
|
|
||||||
},
|
|
||||||
// Add SET RemoveLit
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").RemoveLit("total", "1").Where(w),
|
|
||||||
S: "UPDATE cycling.cyclist_name SET total=total-1 WHERE id=? ",
|
|
||||||
N: []string{"expr"},
|
|
||||||
},
|
|
||||||
// Add WHERE
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").Set("id", "user_uuid", "firstname").Where(w, Gt("firstname")),
|
|
||||||
S: "UPDATE cycling.cyclist_name SET id=?,user_uuid=?,firstname=? WHERE id=? AND firstname>? ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname", "expr", "firstname"},
|
|
||||||
},
|
|
||||||
// Add IF
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").Set("id", "user_uuid", "firstname").Where(w).If(Gt("firstname")),
|
|
||||||
S: "UPDATE cycling.cyclist_name SET id=?,user_uuid=?,firstname=? WHERE id=? IF firstname>? ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname", "expr", "firstname"},
|
|
||||||
},
|
|
||||||
// Add TTL
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").Set("id", "user_uuid", "firstname").Where(w).TTL(time.Second),
|
|
||||||
S: "UPDATE cycling.cyclist_name USING TTL 1 SET id=?,user_uuid=?,firstname=? WHERE id=? ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname", "expr"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").Set("id", "user_uuid", "firstname").Where(w).TTLNamed("ttl"),
|
|
||||||
S: "UPDATE cycling.cyclist_name USING TTL ? SET id=?,user_uuid=?,firstname=? WHERE id=? ",
|
|
||||||
N: []string{"ttl", "id", "user_uuid", "firstname", "expr"},
|
|
||||||
},
|
|
||||||
// Add TIMESTAMP
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").Set("id", "user_uuid", "firstname").Where(w).Timestamp(time.Date(2005, 5, 5, 0, 0, 0, 0, time.UTC)),
|
|
||||||
S: "UPDATE cycling.cyclist_name USING TIMESTAMP 1115251200000000 SET id=?,user_uuid=?,firstname=? WHERE id=? ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname", "expr"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").Set("id", "user_uuid", "firstname").Where(w).TimestampNamed("ts"),
|
|
||||||
S: "UPDATE cycling.cyclist_name USING TIMESTAMP ? SET id=?,user_uuid=?,firstname=? WHERE id=? ",
|
|
||||||
N: []string{"ts", "id", "user_uuid", "firstname", "expr"},
|
|
||||||
},
|
|
||||||
// Add TIMEOUT
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").Set("id", "user_uuid", "firstname").Where(w).Timeout(time.Second),
|
|
||||||
S: "UPDATE cycling.cyclist_name USING TIMEOUT 1s SET id=?,user_uuid=?,firstname=? WHERE id=? ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname", "expr"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").Set("id", "user_uuid", "firstname").Where(w).TimeoutNamed("to"),
|
|
||||||
S: "UPDATE cycling.cyclist_name USING TIMEOUT ? SET id=?,user_uuid=?,firstname=? WHERE id=? ",
|
|
||||||
N: []string{"to", "id", "user_uuid", "firstname", "expr"},
|
|
||||||
},
|
|
||||||
// Add IF EXISTS
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").Set("id", "user_uuid", "firstname").Where(w).Existing(),
|
|
||||||
S: "UPDATE cycling.cyclist_name SET id=?,user_uuid=?,firstname=? WHERE id=? IF EXISTS ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname", "expr"},
|
|
||||||
},
|
|
||||||
// Add SET column
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").SetNamed("firstname", "name"),
|
|
||||||
S: "UPDATE cycling.cyclist_name SET firstname=? ",
|
|
||||||
N: []string{"name"},
|
|
||||||
},
|
|
||||||
// Add AddFunc
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").AddFunc("timestamp", Now()),
|
|
||||||
S: "UPDATE cycling.cyclist_name SET timestamp=timestamp+now() ",
|
|
||||||
N: nil,
|
|
||||||
},
|
|
||||||
// Add RemoveFunc
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").RemoveFunc("timestamp", Now()),
|
|
||||||
S: "UPDATE cycling.cyclist_name SET timestamp=timestamp-now() ",
|
|
||||||
N: nil,
|
|
||||||
},
|
|
||||||
// Add ALLOW FILTERING
|
|
||||||
{
|
|
||||||
B: Update("cycling.cyclist_name").Set("id", "user_uuid", "firstname").Where(w).AllowFiltering(),
|
|
||||||
S: "UPDATE cycling.cyclist_name SET id=?,user_uuid=?,firstname=? WHERE id=? ALLOW FILTERING ",
|
|
||||||
N: []string{"id", "user_uuid", "firstname", "expr"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range table {
|
|
||||||
stmt, names := test.B.ToCql()
|
|
||||||
if diff := cmp.Diff(test.S, stmt); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(test.N, names); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
160
qb/using_test.go
160
qb/using_test.go
@@ -1,160 +0,0 @@
|
|||||||
// Copyright (C) 2017 ScyllaDB
|
|
||||||
// Use of this source code is governed by a ALv2-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package qb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTTL(t *testing.T) {
|
|
||||||
if TTL(time.Second*86400) != 86400 {
|
|
||||||
t.Fatal("wrong ttl")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTimestamp(t *testing.T) {
|
|
||||||
if Timestamp(time.Unix(0, 0).Add(time.Microsecond*123456789)) != 123456789 {
|
|
||||||
t.Fatal("wrong timestamp")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsing(t *testing.T) {
|
|
||||||
table := []struct {
|
|
||||||
B *using
|
|
||||||
N []string
|
|
||||||
S string
|
|
||||||
}{
|
|
||||||
// TTL
|
|
||||||
{
|
|
||||||
B: new(using).TTL(time.Second),
|
|
||||||
S: "USING TTL 1 ",
|
|
||||||
},
|
|
||||||
// TTLNamed
|
|
||||||
{
|
|
||||||
B: new(using).TTLNamed("ttl"),
|
|
||||||
S: "USING TTL ? ",
|
|
||||||
N: []string{"ttl"},
|
|
||||||
},
|
|
||||||
// Timestamp
|
|
||||||
{
|
|
||||||
B: new(using).Timestamp(time.Date(2005, 5, 5, 0, 0, 0, 0, time.UTC)),
|
|
||||||
S: "USING TIMESTAMP 1115251200000000 ",
|
|
||||||
},
|
|
||||||
// TimestampNamed
|
|
||||||
{
|
|
||||||
B: new(using).TimestampNamed("ts"),
|
|
||||||
S: "USING TIMESTAMP ? ",
|
|
||||||
N: []string{"ts"},
|
|
||||||
},
|
|
||||||
// Timeout
|
|
||||||
{
|
|
||||||
B: new(using).Timeout(time.Second),
|
|
||||||
S: "USING TIMEOUT 1s ",
|
|
||||||
},
|
|
||||||
// Timeout faction
|
|
||||||
{
|
|
||||||
B: new(using).Timeout(time.Second + 100*time.Millisecond),
|
|
||||||
S: "USING TIMEOUT 1s100ms ",
|
|
||||||
},
|
|
||||||
// TimeoutNamed
|
|
||||||
{
|
|
||||||
B: new(using).TimeoutNamed("to"),
|
|
||||||
S: "USING TIMEOUT ? ",
|
|
||||||
N: []string{"to"},
|
|
||||||
},
|
|
||||||
// TTL Timestamp
|
|
||||||
{
|
|
||||||
B: new(using).TTL(time.Second).Timestamp(time.Date(2005, 5, 5, 0, 0, 0, 0, time.UTC)),
|
|
||||||
S: "USING TTL 1 AND TIMESTAMP 1115251200000000 ",
|
|
||||||
},
|
|
||||||
// TTL TimestampNamed
|
|
||||||
{
|
|
||||||
B: new(using).TTL(time.Second).TimestampNamed("ts"),
|
|
||||||
S: "USING TTL 1 AND TIMESTAMP ? ",
|
|
||||||
N: []string{"ts"},
|
|
||||||
},
|
|
||||||
// TTLNamed TimestampNamed
|
|
||||||
{
|
|
||||||
B: new(using).TTLNamed("ttl").TimestampNamed("ts"),
|
|
||||||
S: "USING TTL ? AND TIMESTAMP ? ",
|
|
||||||
N: []string{"ttl", "ts"},
|
|
||||||
},
|
|
||||||
// TTLNamed Timestamp
|
|
||||||
{
|
|
||||||
B: new(using).TTLNamed("ttl").Timestamp(time.Date(2005, 5, 5, 0, 0, 0, 0, time.UTC)),
|
|
||||||
S: "USING TTL ? AND TIMESTAMP 1115251200000000 ",
|
|
||||||
N: []string{"ttl"},
|
|
||||||
},
|
|
||||||
// TTL Timeout
|
|
||||||
{
|
|
||||||
B: new(using).TTL(time.Second).Timeout(time.Second),
|
|
||||||
S: "USING TTL 1 AND TIMEOUT 1s ",
|
|
||||||
},
|
|
||||||
// TTL TimeoutNamed
|
|
||||||
{
|
|
||||||
B: new(using).TTL(time.Second).TimeoutNamed("to"),
|
|
||||||
S: "USING TTL 1 AND TIMEOUT ? ",
|
|
||||||
N: []string{"to"},
|
|
||||||
},
|
|
||||||
// TTL Timestamp Timeout
|
|
||||||
{
|
|
||||||
B: new(using).TTL(time.Second).Timestamp(time.Date(2005, 5, 5, 0, 0, 0, 0, time.UTC)).Timeout(time.Second),
|
|
||||||
S: "USING TTL 1 AND TIMESTAMP 1115251200000000 AND TIMEOUT 1s ",
|
|
||||||
},
|
|
||||||
// TTL with no duration
|
|
||||||
{
|
|
||||||
B: new(using).TTL(0 * time.Second),
|
|
||||||
S: "USING TTL 0 ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
B: new(using).TTL(-1 * time.Second),
|
|
||||||
S: "USING TTL 0 ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// TODO patch this maybe in the future
|
|
||||||
B: new(using).TTL(-2 * time.Second),
|
|
||||||
S: "USING TTL -2 ",
|
|
||||||
},
|
|
||||||
// TTL TTLNamed
|
|
||||||
{
|
|
||||||
B: new(using).TTL(time.Second).TTLNamed("ttl"),
|
|
||||||
S: "USING TTL ? ",
|
|
||||||
N: []string{"ttl"},
|
|
||||||
},
|
|
||||||
// TTLNamed TTL
|
|
||||||
{
|
|
||||||
B: new(using).TTLNamed("ttl").TTL(time.Second),
|
|
||||||
S: "USING TTL 1 ",
|
|
||||||
},
|
|
||||||
// Timestamp TimestampNamed
|
|
||||||
{
|
|
||||||
B: new(using).Timestamp(time.Date(2005, 5, 5, 0, 0, 0, 0, time.UTC)).TimestampNamed("ts"),
|
|
||||||
S: "USING TIMESTAMP ? ",
|
|
||||||
N: []string{"ts"},
|
|
||||||
},
|
|
||||||
// TimestampNamed Timestamp
|
|
||||||
{
|
|
||||||
B: new(using).TimestampNamed("ts").Timestamp(time.Date(2005, 5, 5, 0, 0, 0, 0, time.UTC)),
|
|
||||||
S: "USING TIMESTAMP 1115251200000000 ",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range table {
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
names := test.B.writeCql(buf)
|
|
||||||
stmt := buf.String()
|
|
||||||
|
|
||||||
if diff := cmp.Diff(test.S, stmt); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(test.N, names); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
package qb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFormatDuration(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
input time.Duration
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Zero duration",
|
|
||||||
input: 0,
|
|
||||||
expected: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 500 * time.Millisecond,
|
|
||||||
expected: "500ms",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 10 * time.Second,
|
|
||||||
expected: "10s",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 3 * time.Minute,
|
|
||||||
expected: "3m",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: (2 * time.Minute) + (30 * time.Second),
|
|
||||||
expected: "2m30s",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: (15 * time.Second) + (250 * time.Millisecond),
|
|
||||||
expected: "15s250ms",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: (1 * time.Minute) + (45 * time.Second) + (123 * time.Millisecond),
|
|
||||||
expected: "1m45s123ms",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: (5 * time.Minute) + (1 * time.Second) + (999 * time.Millisecond),
|
|
||||||
expected: "5m1s999ms",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: (2 * time.Second) + (1500 * time.Millisecond), // 3 seconds, 500ms
|
|
||||||
expected: "3s500ms",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
actual := formatDuration(tt.input)
|
|
||||||
if actual != tt.expected {
|
|
||||||
t.Errorf("got %q, want %q", actual, tt.expected)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
233
queryx_test.go
233
queryx_test.go
@@ -1,233 +0,0 @@
|
|||||||
// Copyright (C) 2017 ScyllaDB
|
|
||||||
// Use of this source code is governed by a ALv2-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package gocqlx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
gocql "github.com/apache/cassandra-gocql-driver/v2"
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCompileQuery(t *testing.T) {
|
|
||||||
table := []struct {
|
|
||||||
Q, R string
|
|
||||||
V []string
|
|
||||||
}{
|
|
||||||
// Basic test for named parameters, invalid char ',' terminating
|
|
||||||
{
|
|
||||||
Q: `INSERT INTO foo (a,b,c,d) VALUES (:name, :age, :first, :last)`,
|
|
||||||
R: `INSERT INTO foo (a,b,c,d) VALUES (?, ?, ?, ?)`,
|
|
||||||
V: []string{"name", "age", "first", "last"},
|
|
||||||
},
|
|
||||||
// This query tests a named parameter ending the string as well as numbers
|
|
||||||
{
|
|
||||||
Q: `SELECT * FROM a WHERE first_name=:name1 AND last_name=:name2`,
|
|
||||||
R: `SELECT * FROM a WHERE first_name=? AND last_name=?`,
|
|
||||||
V: []string{"name1", "name2"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Q: `SELECT "::foo" FROM a WHERE first_name=:name1 AND last_name=:name2`,
|
|
||||||
R: `SELECT ":foo" FROM a WHERE first_name=? AND last_name=?`,
|
|
||||||
V: []string{"name1", "name2"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Q: `SELECT 'a::b::c' || first_name, '::::ABC::_::' FROM person WHERE first_name=:first_name AND last_name=:last_name`,
|
|
||||||
R: `SELECT 'a:b:c' || first_name, '::ABC:_:' FROM person WHERE first_name=? AND last_name=?`,
|
|
||||||
V: []string{"first_name", "last_name"},
|
|
||||||
},
|
|
||||||
/* This unicode awareness test sadly fails, because of our byte-wise worldview.
|
|
||||||
* We could certainly iterate by Rune instead, though it's a great deal slower,
|
|
||||||
* it's probably the RightWay(tm)
|
|
||||||
{
|
|
||||||
Q: `INSERT INTO foo (a,b,c,d) VALUES (:あ, :b, :キコ, :名前)`,
|
|
||||||
R: `INSERT INTO foo (a,b,c,d) VALUES (?, ?, ?, ?)`,
|
|
||||||
},
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range table {
|
|
||||||
qr, names, err := CompileNamedQuery([]byte(test.Q))
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
if qr != test.R {
|
|
||||||
t.Error("expected", test.R, "got", qr)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(names, test.V); diff != "" {
|
|
||||||
t.Error("names mismatch", diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestQueryxBindStruct(t *testing.T) {
|
|
||||||
v := &struct {
|
|
||||||
Name string
|
|
||||||
Age int
|
|
||||||
First string
|
|
||||||
Last string
|
|
||||||
}{
|
|
||||||
Name: "name",
|
|
||||||
Age: 30,
|
|
||||||
First: "first",
|
|
||||||
Last: "last",
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("simple", func(t *testing.T) {
|
|
||||||
names := []string{"name", "age", "first", "last"}
|
|
||||||
args, err := Query(nil, names).bindStructArgs(v, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if diff := cmp.Diff(args, []interface{}{"name", 30, "first", "last"}); diff != "" {
|
|
||||||
t.Error("args mismatch", diff)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("with transformer", func(t *testing.T) {
|
|
||||||
tr := func(name string, val interface{}) interface{} {
|
|
||||||
if name == "age" {
|
|
||||||
return 42
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
names := []string{"name", "age", "first", "last"}
|
|
||||||
args, err := Query(nil, names).WithBindTransformer(tr).bindStructArgs(v, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if diff := cmp.Diff(args, []interface{}{"name", 42, "first", "last"}); diff != "" {
|
|
||||||
t.Error("args mismatch", diff)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("error", func(t *testing.T) {
|
|
||||||
names := []string{"name", "age", "first", "not_found"}
|
|
||||||
_, err := Query(nil, names).bindStructArgs(v, nil)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("unexpected error")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("fallback", func(t *testing.T) {
|
|
||||||
names := []string{"name", "age", "first", "not_found"}
|
|
||||||
m := map[string]interface{}{
|
|
||||||
"not_found": "last",
|
|
||||||
}
|
|
||||||
args, err := Query(nil, names).bindStructArgs(v, m)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if diff := cmp.Diff(args, []interface{}{"name", 30, "first", "last"}); diff != "" {
|
|
||||||
t.Error("args mismatch", diff)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("fallback with transformer", func(t *testing.T) {
|
|
||||||
tr := func(name string, val interface{}) interface{} {
|
|
||||||
if name == "not_found" {
|
|
||||||
return "map_found"
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
names := []string{"name", "age", "first", "not_found"}
|
|
||||||
m := map[string]interface{}{
|
|
||||||
"not_found": "last",
|
|
||||||
}
|
|
||||||
args, err := Query(nil, names).WithBindTransformer(tr).bindStructArgs(v, m)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if diff := cmp.Diff(args, []interface{}{"name", 30, "first", "map_found"}); diff != "" {
|
|
||||||
t.Error("args mismatch", diff)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("fallback error", func(t *testing.T) {
|
|
||||||
names := []string{"name", "age", "first", "not_found", "really_not_found"}
|
|
||||||
m := map[string]interface{}{
|
|
||||||
"not_found": "last",
|
|
||||||
}
|
|
||||||
_, err := Query(nil, names).bindStructArgs(v, m)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("unexpected error")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestQueryxBindMap(t *testing.T) {
|
|
||||||
v := map[string]interface{}{
|
|
||||||
"name": "name",
|
|
||||||
"age": 30,
|
|
||||||
"first": "first",
|
|
||||||
"last": "last",
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("simple", func(t *testing.T) {
|
|
||||||
names := []string{"name", "age", "first", "last"}
|
|
||||||
args, err := Query(nil, names).bindMapArgs(v)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if diff := cmp.Diff(args, []interface{}{"name", 30, "first", "last"}); diff != "" {
|
|
||||||
t.Error("args mismatch", diff)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("with transformer", func(t *testing.T) {
|
|
||||||
tr := func(name string, val interface{}) interface{} {
|
|
||||||
if name == "age" {
|
|
||||||
return 42
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
names := []string{"name", "age", "first", "last"}
|
|
||||||
args, err := Query(nil, names).WithBindTransformer(tr).bindMapArgs(v)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if diff := cmp.Diff(args, []interface{}{"name", 42, "first", "last"}); diff != "" {
|
|
||||||
t.Error("args mismatch", diff)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("error", func(t *testing.T) {
|
|
||||||
names := []string{"name", "first", "not_found"}
|
|
||||||
_, err := Query(nil, names).bindMapArgs(v)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("unexpected error")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestQueryxAllWrapped(t *testing.T) {
|
|
||||||
var (
|
|
||||||
gocqlQueryPtr = reflect.TypeOf((*gocql.Query)(nil))
|
|
||||||
queryxPtr = reflect.TypeOf((*Queryx)(nil))
|
|
||||||
)
|
|
||||||
|
|
||||||
for i := 0; i < gocqlQueryPtr.NumMethod(); i++ {
|
|
||||||
m, ok := queryxPtr.MethodByName(gocqlQueryPtr.Method(i).Name)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("Queryx missing method %s", gocqlQueryPtr.Method(i).Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
for j := 0; j < m.Type.NumOut(); j++ {
|
|
||||||
if m.Type.Out(j) == gocqlQueryPtr {
|
|
||||||
t.Errorf("Queryx method %s not wrapped", m.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,313 +0,0 @@
|
|||||||
// Copyright (C) 2017 ScyllaDB
|
|
||||||
// Use of this source code is governed by a ALv2-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package table
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
|
|
||||||
"github.com/scylladb/gocqlx/v3/qb"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTableGet(t *testing.T) {
|
|
||||||
table := []struct {
|
|
||||||
M Metadata
|
|
||||||
C []string
|
|
||||||
N []string
|
|
||||||
S string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
M: Metadata{
|
|
||||||
Name: "table",
|
|
||||||
Columns: []string{"a", "b", "c", "d"},
|
|
||||||
PartKey: []string{"a"},
|
|
||||||
SortKey: []string{"b"},
|
|
||||||
},
|
|
||||||
N: []string{"a", "b"},
|
|
||||||
S: "SELECT * FROM table WHERE a=? AND b=? ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
M: Metadata{
|
|
||||||
Name: "table",
|
|
||||||
Columns: []string{"a", "b", "c", "d"},
|
|
||||||
PartKey: []string{"a"},
|
|
||||||
},
|
|
||||||
N: []string{"a"},
|
|
||||||
S: "SELECT * FROM table WHERE a=? ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
M: Metadata{
|
|
||||||
Name: "table",
|
|
||||||
Columns: []string{"a", "b", "c", "d"},
|
|
||||||
PartKey: []string{"a"},
|
|
||||||
},
|
|
||||||
C: []string{"d"},
|
|
||||||
N: []string{"a"},
|
|
||||||
S: "SELECT d FROM table WHERE a=? ",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range table {
|
|
||||||
stmt, names := New(test.M).Get(test.C...)
|
|
||||||
if diff := cmp.Diff(test.S, stmt); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(test.N, names); diff != "" {
|
|
||||||
t.Error(diff, names)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// run GetBuilder on the same data set
|
|
||||||
for _, test := range table {
|
|
||||||
stmt, names := New(test.M).GetBuilder(test.C...).ToCql()
|
|
||||||
if diff := cmp.Diff(test.S, stmt); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(test.N, names); diff != "" {
|
|
||||||
t.Error(diff, names)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTableSelect(t *testing.T) {
|
|
||||||
table := []struct {
|
|
||||||
M Metadata
|
|
||||||
C []string
|
|
||||||
N []string
|
|
||||||
S string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
M: Metadata{
|
|
||||||
Name: "table",
|
|
||||||
Columns: []string{"a", "b", "c", "d"},
|
|
||||||
PartKey: []string{"a"},
|
|
||||||
SortKey: []string{"b"},
|
|
||||||
},
|
|
||||||
N: []string{"a"},
|
|
||||||
S: "SELECT * FROM table WHERE a=? ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
M: Metadata{
|
|
||||||
Name: "table",
|
|
||||||
Columns: []string{"a", "b", "c", "d"},
|
|
||||||
PartKey: []string{"a"},
|
|
||||||
SortKey: []string{"b"},
|
|
||||||
},
|
|
||||||
C: []string{"d"},
|
|
||||||
N: []string{"a"},
|
|
||||||
S: "SELECT d FROM table WHERE a=? ",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range table {
|
|
||||||
stmt, names := New(test.M).Select(test.C...)
|
|
||||||
if diff := cmp.Diff(test.S, stmt); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(test.N, names); diff != "" {
|
|
||||||
t.Error(diff, names)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// run SelectBuilder on the same data set
|
|
||||||
for _, test := range table {
|
|
||||||
stmt, names := New(test.M).SelectBuilder(test.C...).ToCql()
|
|
||||||
if diff := cmp.Diff(test.S, stmt); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(test.N, names); diff != "" {
|
|
||||||
t.Error(diff, names)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTableInsert(t *testing.T) {
|
|
||||||
table := []struct {
|
|
||||||
M Metadata
|
|
||||||
N []string
|
|
||||||
S string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
M: Metadata{
|
|
||||||
Name: "table",
|
|
||||||
Columns: []string{"a", "b", "c", "d"},
|
|
||||||
PartKey: []string{"a"},
|
|
||||||
SortKey: []string{"b"},
|
|
||||||
},
|
|
||||||
N: []string{"a", "b", "c", "d"},
|
|
||||||
S: "INSERT INTO table (a,b,c,d) VALUES (?,?,?,?) ",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range table {
|
|
||||||
stmt, names := New(test.M).Insert()
|
|
||||||
if diff := cmp.Diff(test.S, stmt); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(test.N, names); diff != "" {
|
|
||||||
t.Error(diff, names)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTableUpdate(t *testing.T) {
|
|
||||||
table := []struct {
|
|
||||||
M Metadata
|
|
||||||
C []string
|
|
||||||
N []string
|
|
||||||
S string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
M: Metadata{
|
|
||||||
Name: "table",
|
|
||||||
Columns: []string{"a", "b", "c", "d"},
|
|
||||||
PartKey: []string{"a"},
|
|
||||||
SortKey: []string{"b"},
|
|
||||||
},
|
|
||||||
C: []string{"d"},
|
|
||||||
N: []string{"d", "a", "b"},
|
|
||||||
S: "UPDATE table SET d=? WHERE a=? AND b=? ",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range table {
|
|
||||||
stmt, names := New(test.M).Update(test.C...)
|
|
||||||
if diff := cmp.Diff(test.S, stmt); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(test.N, names); diff != "" {
|
|
||||||
t.Error(diff, names)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// run UpdateBuilder on the same data set
|
|
||||||
for _, test := range table {
|
|
||||||
stmt, names := New(test.M).UpdateBuilder(test.C...).ToCql()
|
|
||||||
if diff := cmp.Diff(test.S, stmt); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(test.N, names); diff != "" {
|
|
||||||
t.Error(diff, names)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTableDelete(t *testing.T) {
|
|
||||||
table := []struct {
|
|
||||||
M Metadata
|
|
||||||
C []string
|
|
||||||
N []string
|
|
||||||
S string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
M: Metadata{
|
|
||||||
Name: "table",
|
|
||||||
Columns: []string{"a", "b", "c", "d"},
|
|
||||||
PartKey: []string{"a"},
|
|
||||||
SortKey: []string{"b"},
|
|
||||||
},
|
|
||||||
N: []string{"a", "b"},
|
|
||||||
S: "DELETE FROM table WHERE a=? AND b=? ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
M: Metadata{
|
|
||||||
Name: "table",
|
|
||||||
Columns: []string{"a", "b", "c", "d"},
|
|
||||||
PartKey: []string{"a"},
|
|
||||||
},
|
|
||||||
N: []string{"a"},
|
|
||||||
S: "DELETE FROM table WHERE a=? ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
M: Metadata{
|
|
||||||
Name: "table",
|
|
||||||
Columns: []string{"a", "b", "c", "d"},
|
|
||||||
PartKey: []string{"a"},
|
|
||||||
},
|
|
||||||
C: []string{"d"},
|
|
||||||
N: []string{"a"},
|
|
||||||
S: "DELETE d FROM table WHERE a=? ",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range table {
|
|
||||||
stmt, names := New(test.M).Delete(test.C...)
|
|
||||||
if diff := cmp.Diff(test.S, stmt); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(test.N, names); diff != "" {
|
|
||||||
t.Error(diff, names)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// run DeleteBuilder on the same data set
|
|
||||||
for _, test := range table {
|
|
||||||
stmt, names := New(test.M).DeleteBuilder(test.C...).ToCql()
|
|
||||||
if diff := cmp.Diff(test.S, stmt); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(test.N, names); diff != "" {
|
|
||||||
t.Error(diff, names)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTableConcurrentUsage(t *testing.T) {
|
|
||||||
table := []struct {
|
|
||||||
Name string
|
|
||||||
M Metadata
|
|
||||||
C []string
|
|
||||||
N []string
|
|
||||||
S string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Name: "Full select",
|
|
||||||
M: Metadata{
|
|
||||||
Name: "table",
|
|
||||||
Columns: []string{"a", "b", "c", "d"},
|
|
||||||
PartKey: []string{"a"},
|
|
||||||
SortKey: []string{"b"},
|
|
||||||
},
|
|
||||||
N: []string{"a", "b"},
|
|
||||||
S: "SELECT * FROM table WHERE a=? AND b=? ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Sub select",
|
|
||||||
M: Metadata{
|
|
||||||
Name: "table",
|
|
||||||
Columns: []string{"a", "b", "c", "d"},
|
|
||||||
PartKey: []string{"a"},
|
|
||||||
SortKey: []string{"b"},
|
|
||||||
},
|
|
||||||
C: []string{"d"},
|
|
||||||
N: []string{"a", "b"},
|
|
||||||
S: "SELECT d FROM table WHERE a=? AND b=? ",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
parallelCount := 3
|
|
||||||
// run SelectBuilder on the data set in parallel
|
|
||||||
for _, test := range table {
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
testTable := New(test.M)
|
|
||||||
wg.Add(parallelCount)
|
|
||||||
for i := 0; i < parallelCount; i++ {
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
stmt, names := testTable.SelectBuilder(test.C...).
|
|
||||||
Where(qb.Eq("b")).ToCql()
|
|
||||||
if diff := cmp.Diff(test.S, stmt); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(test.N, names); diff != "" {
|
|
||||||
t.Error(diff, names)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user