Update golangci-lint and turn it on in CI

This commit is contained in:
Dmitry Kropachev
2024-06-14 13:07:21 -04:00
committed by Sylwia Szunejko
parent a9ab270196
commit ab80d70106
37 changed files with 225 additions and 151 deletions

View File

@@ -17,7 +17,8 @@ jobs:
steps:
- name: Git Checkout
uses: actions/checkout@v3
with:
fetch-depth: '0'
- name: Install Go 1.17
uses: actions/setup-go@v3
with:
@@ -33,18 +34,14 @@ jobs:
${{ runner.os }}-go-
- name: Make Directory for GOBIN
run: mkdir -p ${GOBIN}
run: mkdir -p "${GOBIN}"
- name: Download Dependencies
run: make get-deps
# - name: Lint
# uses: golangci/golangci-lint-action@v3
# with:
# version: v1.45.2
- run: go vet ./...
run: git --version && make get-deps && make get-tools
- name: Lint
run: make check
- name: Run Scylla Container
run: make run-scylla

View File

@@ -1,8 +1,23 @@
run:
deadline: 5m
tests: false
tests: true
allow-parallel-runners: true
modules-download-mode: readonly
build-tags: [ all, integration ]
linters-settings:
revive:
rules:
- name: package-comments
disabled: true
goimports:
local-prefixes: github.com/scylladb/gocqlx
gofumpt:
extra-rules: true
govet:
enable-all: true
disable:
- shadow
errcheck:
check-blank: true
gocognit:
@@ -23,22 +38,43 @@ linters-settings:
line-length: 180
linters:
enable-all: true
disable:
- funlen
- gas
- gochecknoglobals
- gochecknoinits
- gomnd
- interfacer
- maligned
- nakedret
- prealloc
- wsl
disable-all: true
enable:
- errcheck
- gocritic
- gofumpt
- goheader
- goimports
- gosimple
- govet
- ineffassign
- lll
- misspell
- predeclared
- revive
- staticcheck
- thelper
- tparallel
- typecheck
- unused
- forbidigo
issues:
new: true
new-from-rev: origin/master
exclude-use-default: false
exclude:
- composite literal uses unkeyed fields
- Error return value of `.+\.Close` is not checked
- method Json should be JSON
exclude-rules:
- path: (.*_test.go|migrate/example|gocqlxtest/)
linters:
- fieldalignment
- govet
- errcheck
- path: doc_test.go
linters:
- unused
- revive

View File

@@ -28,6 +28,12 @@ fmt:
check:
@$(GOBIN)/golangci-lint run ./...
.PHONY: fix
fix:
@$(GOBIN)/golangci-lint run --fix ./...
@fieldalignment -V=full >/dev/null 2>&1 || go install golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment@v0.11.0
@$(GOBIN)/fieldalignment -test=false -fix ./...
GOTEST := go test -cpu $(GOTEST_CPU) -count=1 -cover -race -tags all
.PHONY: test
@@ -51,12 +57,12 @@ run-examples:
run-scylla:
@echo "==> Running test instance of Scylla $(SCYLLA_VERSION)"
@docker pull scylladb/scylla:$(SCYLLA_VERSION)
@docker run --name scylla -p 9042:9042 --cpuset-cpus=$(SCYLLA_CPU) --memory 1G --rm -d scylladb/scylla:$(SCYLLA_VERSION)
@until docker exec scylla cqlsh -e "DESCRIBE SCHEMA"; do sleep 2; done
@docker run --name gocqlx-scylla -p 9042:9042 --cpuset-cpus=$(SCYLLA_CPU) --memory 1G --rm -d scylladb/scylla:$(SCYLLA_VERSION)
@until docker exec gocqlx-scylla cqlsh -e "DESCRIBE SCHEMA"; do sleep 2; done
.PHONY: stop-scylla
stop-scylla:
@docker stop scylla
@docker stop gocqlx-scylla
.PHONY: get-deps
get-deps:
@@ -69,4 +75,4 @@ endef
.PHONY: get-tools
get-tools:
@echo "==> Installing tools at $(GOBIN)..."
@$(call dl_tgz,golangci-lint,https://github.com/golangci/golangci-lint/releases/download/v1.45.2/golangci-lint-v1.45.2-linux-amd64.tar.gz)
@$(call dl_tgz,golangci-lint,https://github.com/golangci/golangci-lint/releases/download/v1.59.1/golangci-lint-1.59.1-linux-amd64.tar.gz)

View File

@@ -4,6 +4,7 @@ import (
"github.com/gocql/gocql"
)
// Batch is a wrapper around gocql.Batch
type Batch struct {
*gocql.Batch
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/gocql/gocql"
"github.com/google/go-cmp/cmp"
"github.com/scylladb/gocqlx/v2"
"github.com/scylladb/gocqlx/v2/gocqlxtest"
"github.com/scylladb/gocqlx/v2/qb"

View File

@@ -13,7 +13,7 @@ import (
"testing"
"github.com/scylladb/gocqlx/v2"
. "github.com/scylladb/gocqlx/v2/gocqlxtest"
"github.com/scylladb/gocqlx/v2/gocqlxtest"
"github.com/scylladb/gocqlx/v2/qb"
)
@@ -46,7 +46,7 @@ var benchPersonCols = []string{"id", "first_name", "last_name", "email", "gender
// BenchmarkBaseGocqlInsert performs standard insert.
func BenchmarkBaseGocqlInsert(b *testing.B) {
people := loadFixtures()
session := CreateSession(b)
session := gocqlxtest.CreateSession(b)
defer session.Close()
if err := session.ExecStmt(benchPersonSchema); err != nil {
@@ -69,7 +69,7 @@ func BenchmarkBaseGocqlInsert(b *testing.B) {
// BenchmarkGocqlInsert performs insert with struct binding.
func BenchmarkGocqlxInsert(b *testing.B) {
people := loadFixtures()
session := CreateSession(b)
session := gocqlxtest.CreateSession(b)
defer session.Close()
if err := session.ExecStmt(benchPersonSchema); err != nil {
@@ -96,7 +96,7 @@ func BenchmarkGocqlxInsert(b *testing.B) {
// BenchmarkBaseGocqlGet performs standard scan.
func BenchmarkBaseGocqlGet(b *testing.B) {
people := loadFixtures()
session := CreateSession(b)
session := gocqlxtest.CreateSession(b)
defer session.Close()
initTable(b, session, people)
@@ -119,7 +119,7 @@ func BenchmarkBaseGocqlGet(b *testing.B) {
// BenchmarkGocqlxGet performs get.
func BenchmarkGocqlxGet(b *testing.B) {
people := loadFixtures()
session := CreateSession(b)
session := gocqlxtest.CreateSession(b)
defer session.Close()
initTable(b, session, people)
@@ -147,7 +147,7 @@ func BenchmarkGocqlxGet(b *testing.B) {
// pointers.
func BenchmarkBaseGocqlSelect(b *testing.B) {
people := loadFixtures()
session := CreateSession(b)
session := gocqlxtest.CreateSession(b)
defer session.Close()
initTable(b, session, people)
@@ -162,7 +162,7 @@ func BenchmarkBaseGocqlSelect(b *testing.B) {
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)
v = append(v, p) // nolint:staticcheck
p = new(benchPerson)
}
if err := iter.Close(); err != nil {
@@ -174,7 +174,7 @@ func BenchmarkBaseGocqlSelect(b *testing.B) {
// BenchmarkGocqlSelect performs select to a slice pointers.
func BenchmarkGocqlxSelect(b *testing.B) {
people := loadFixtures()
session := CreateSession(b)
session := gocqlxtest.CreateSession(b)
defer session.Close()
initTable(b, session, people)
@@ -212,6 +212,8 @@ func loadFixtures() []*benchPerson {
}
func initTable(b *testing.B, session gocqlx.Session, people []*benchPerson) {
b.Helper()
if err := session.ExecStmt(benchPersonSchema); err != nil {
b.Fatal(err)
}

View File

@@ -74,9 +74,9 @@ func mapScyllaToGoType(s string) string {
typeStr := "struct {\n"
for i, t := range types {
typeStr = typeStr + "\t\tField" + strconv.Itoa(i+1) + " " + mapScyllaToGoType(t) + "\n"
typeStr += "\t\tField" + strconv.Itoa(i+1) + " " + mapScyllaToGoType(t) + "\n"
}
typeStr = typeStr + "\t}"
typeStr += "\t}"
return typeStr
}
@@ -96,8 +96,8 @@ func typeToString(t interface{}) string {
return t.(gocql.NativeType).String()
case "gocql.CollectionType":
collectionType := t.(gocql.CollectionType).String()
collectionType = strings.Replace(collectionType, "(", "<", -1)
collectionType = strings.Replace(collectionType, ")", ">", -1)
collectionType = strings.ReplaceAll(collectionType, "(", "<")
collectionType = strings.ReplaceAll(collectionType, ")", ">")
return collectionType
default:
panic(fmt.Sprintf("Did not expect %v type in user defined type", tType))

View File

@@ -15,6 +15,7 @@ import (
"strings"
"github.com/gocql/gocql"
"github.com/scylladb/gocqlx/v2"
_ "github.com/scylladb/gocqlx/v2/table"
)
@@ -31,10 +32,8 @@ var (
flagIgnoreIndexes = cmd.Bool("ignore-indexes", false, "don't generate types for indexes")
)
var (
//go:embed keyspace.tmpl
keyspaceTmpl string
)
//go:embed keyspace.tmpl
var keyspaceTmpl string
func main() {
err := cmd.Parse(os.Args[1:])
@@ -80,7 +79,6 @@ func renderTemplate(md *gocql.KeyspaceMetadata) ([]byte, error) {
Funcs(template.FuncMap{"mapScyllaToGoType": mapScyllaToGoType}).
Funcs(template.FuncMap{"typeToString": typeToString}).
Parse(keyspaceTmpl)
if err != nil {
log.Fatalln("unable to parse models template:", err)
}
@@ -169,7 +167,7 @@ func existsInSlice(s []string, v string) bool {
// The second element contains the name of the type.
//
// [["<my_type,", "my_type"] ["my_other_type>", "my_other_type"]]
var userTypes = regexp.MustCompile(`(?:<|\s)(\w+)(?:>|,)`) // match all types contained in set<X>, list<X>, tuple<A, B> etc.
var userTypes = regexp.MustCompile(`(?:<|\s)(\w+)[>,]`) // match all types contained in set<X>, list<X>, tuple<A, B> etc.
// usedInTables reports whether the typeName is used in any of columns of the provided tables.
func usedInTables(typeName string, tables map[string]*gocql.TableMetadata) bool {

View File

@@ -10,6 +10,7 @@ import (
"github.com/gocql/gocql"
"github.com/google/go-cmp/cmp"
"github.com/scylladb/gocqlx/v2/gocqlxtest"
)

View File

@@ -12,13 +12,13 @@ import (
"time"
"github.com/scylladb/gocqlx/v2/dbutil"
. "github.com/scylladb/gocqlx/v2/gocqlxtest"
"github.com/scylladb/gocqlx/v2/gocqlxtest"
"github.com/scylladb/gocqlx/v2/qb"
"github.com/scylladb/gocqlx/v2/table"
)
func TestRewriteTableTTL(t *testing.T) {
session := CreateSession(t)
session := gocqlxtest.CreateSession(t)
defer session.Close()
if err := session.ExecStmt(`CREATE TABLE gocqlx_test.rewrite_table (testtext text PRIMARY KEY)`); err != nil {
@@ -61,7 +61,7 @@ func TestRewriteTableTTL(t *testing.T) {
}
func TestRewriteTableClone(t *testing.T) {
session := CreateSession(t)
session := gocqlxtest.CreateSession(t)
defer session.Close()
if err := session.ExecStmt(`CREATE TABLE gocqlx_test.rewrite_table_clone_src (testtext text PRIMARY KEY, testint int)`); err != nil {

View File

@@ -6,6 +6,7 @@ package gocqlx_test
import (
"github.com/gocql/gocql"
"github.com/scylladb/gocqlx/v2"
"github.com/scylladb/gocqlx/v2/qb"
)

View File

@@ -14,12 +14,13 @@ import (
"time"
"github.com/gocql/gocql"
"golang.org/x/sync/errgroup"
"gopkg.in/inf.v0"
"github.com/scylladb/gocqlx/v2"
"github.com/scylladb/gocqlx/v2/gocqlxtest"
"github.com/scylladb/gocqlx/v2/qb"
"github.com/scylladb/gocqlx/v2/table"
"golang.org/x/sync/errgroup"
"gopkg.in/inf.v0"
)
// Running examples locally:
@@ -43,7 +44,7 @@ func TestExample(t *testing.T) {
datatypesBlob(t, session)
datatypesUserDefinedType(t, session)
datatypesUserDefinedTypeWrapper(t, session)
datatypesJson(t, session)
datatypesJSON(t, session)
pagingForwardPaging(t, session)
pagingEfficientFullTableScan(t, session)
@@ -73,6 +74,8 @@ type PlaylistItem struct {
// 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,
@@ -162,6 +165,8 @@ func basicCreateAndPopulateKeyspace(t *testing.T, session gocqlx.Session, keyspa
// 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)
@@ -178,7 +183,7 @@ func createAndPopulateKeyspaceAllTypes(t *testing.T, session gocqlx.Session) {
DoublE float64
DuratioN gocql.Duration
FloaT float32
Id [16]byte
ID [16]byte
InT int32
IneT string
ListInt []int32
@@ -256,14 +261,16 @@ func createAndPopulateKeyspaceAllTypes(t *testing.T, session gocqlx.Session) {
// 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)
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
var byteID [16]byte
id := []byte("756716f7-2e54-4715-9f00-91dcbea6cf50")
copy(byteId[:], id)
copy(byteID[:], id)
date := time.Date(2021, time.December, 11, 10, 23, 0, 0, time.UTC)
var double float64 = 1.2
var double float64 = 1.2 // nolint:revive
var float float32 = 1.3
var integer int32 = 123
listInt := []int32{1, 2, 3}
@@ -286,7 +293,7 @@ func createAndPopulateKeyspaceAllTypes(t *testing.T, session gocqlx.Session) {
DoublE: double,
DuratioN: gocql.Duration{Months: 1, Days: 1, Nanoseconds: 86400},
FloaT: float,
Id: byteId,
ID: byteID,
InT: integer,
IneT: "127.0.0.1",
ListInt: listInt,
@@ -309,7 +316,7 @@ func createAndPopulateKeyspaceAllTypes(t *testing.T, session gocqlx.Session) {
queryCheckTypes := checkTypesTable.SelectQuery(session)
queryCheckTypes.BindStruct(&CheckTypesStruct{
Id: byteId,
ID: byteID,
})
var items []*CheckTypesStruct
@@ -325,6 +332,8 @@ func createAndPopulateKeyspaceAllTypes(t *testing.T, session gocqlx.Session) {
// 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)
@@ -340,6 +349,8 @@ func basicReadScyllaVersion(t *testing.T, session gocqlx.Session) {
// to handle 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)
@@ -392,6 +403,8 @@ type Coordinates struct {
// 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)
@@ -446,6 +459,8 @@ type coordinates struct {
// 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)
@@ -501,7 +516,9 @@ func datatypesUserDefinedTypeWrapper(t *testing.T, session gocqlx.Session) {
}
// This example shows how to use query builder to work with
func datatypesJson(t *testing.T, session gocqlx.Session) {
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)
@@ -524,17 +541,17 @@ func datatypesJson(t *testing.T, session gocqlx.Session) {
}
// fromJson lets you provide individual columns as JSON:
insertFromJson := qb.Insert("examples.querybuilder_json").
insertFromJSON := qb.Insert("examples.querybuilder_json").
Columns("id", "name").
FuncColumn("specs", qb.Fn("fromJson", "json")).
Query(session)
insertFromJson.BindMap(qb.M{
insertFromJSON.BindMap(qb.M{
"id": 3,
"name": "Screen",
"json": `{ "size": "24-inch" }`,
})
if err := insertFromJson.Exec(); err != nil {
if err := insertFromJSON.Exec(); err != nil {
t.Fatal("Exec() failed:", err)
}
@@ -559,12 +576,12 @@ func datatypesJson(t *testing.T, session gocqlx.Session) {
row := &struct {
ID int
JsonSpecs string
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)
t.Logf("Entry #%d's specs as JSON: %s", row.ID, row.JSONSpecs)
}
type Video struct {
@@ -600,6 +617,8 @@ func pagingFillTable(t *testing.T, insert *gocqlx.Queryx) {
// 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)
@@ -669,6 +688,8 @@ func pagingForwardPaging(t *testing.T, session gocqlx.Session) {
//
// [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)
@@ -779,6 +800,8 @@ func pagingEfficientFullTableScan(t *testing.T, session gocqlx.Session) {
// 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)
@@ -878,6 +901,8 @@ func lwtLock(t *testing.T, session gocqlx.Session) {
// 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)

5
go.sum
View File

@@ -3,13 +3,8 @@ github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCS
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gocql/gocql v0.0.0-20200131111108-92af2e088537 h1:NaMut1fdw76YYX/TPinSAbai4DShF5tPort3bHpET6g=
github.com/gocql/gocql v0.0.0-20200131111108-92af2e088537/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY=
github.com/gocql/gocql v0.0.0-20211015133455-b225f9b53fa1 h1:px9qUCy/RNJNsfCam4m2IxWGxNuimkrioEF0vrrbPsg=
github.com/gocql/gocql v0.0.0-20211015133455-b225f9b53fa1/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=

View File

@@ -13,6 +13,7 @@ import (
"time"
"github.com/gocql/gocql"
"github.com/scylladb/gocqlx/v2"
)
@@ -31,8 +32,10 @@ var initOnce sync.Once
// CreateSession creates a new gocqlx session from flags.
func CreateSession(tb testing.TB) gocqlx.Session {
tb.Helper()
cluster := CreateCluster()
return createSessionFromCluster(cluster, tb)
return createSessionFromCluster(tb, cluster)
}
// CreateCluster creates gocql ClusterConfig from flags.
@@ -92,7 +95,8 @@ func CreateKeyspace(cluster *gocql.ClusterConfig, keyspace string) error {
return nil
}
func createSessionFromCluster(cluster *gocql.ClusterConfig, tb testing.TB) gocqlx.Session {
func createSessionFromCluster(tb testing.TB, cluster *gocql.ClusterConfig) gocqlx.Session {
tb.Helper()
if !flag.Parsed() {
flag.Parse()
}

View File

@@ -19,17 +19,16 @@ var DefaultUnsafe bool
// Iterx is a wrapper around gocql.Iter which adds struct scanning capabilities.
type Iterx struct {
err error
*gocql.Iter
Mapper *reflectx.Mapper
// Cache memory for a rows during iteration in structScan.
fields [][]int
values []interface{}
unsafe bool
structOnly bool
applied bool
err error
// Cache memory for a rows during iteration in structScan.
fields [][]int
values []interface{}
}
// Unsafe forces the iterator to ignore missing fields. By default when scanning
@@ -206,9 +205,9 @@ func (iter *Iterx) scanAll(dest interface{}) bool {
// isScannable takes the reflect.Type and the actual dest value and returns
// whether or not it's Scannable. t is scannable if:
// * ptr to t implements gocql.Unmarshaler, gocql.UDTUnmarshaler or UDT
// * it is not a struct
// * it has no exported fields
// - ptr to t implements gocql.Unmarshaler, gocql.UDTUnmarshaler or UDT
// - it is not a struct
// - it has no exported fields
func (iter *Iterx) isScannable(t reflect.Type) bool {
ptr := reflect.PtrTo(t)
switch {

View File

@@ -16,10 +16,11 @@ import (
"github.com/gocql/gocql"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/scylladb/gocqlx/v2"
. "github.com/scylladb/gocqlx/v2/gocqlxtest"
"github.com/scylladb/gocqlx/v2/qb"
"gopkg.in/inf.v0"
"github.com/scylladb/gocqlx/v2"
"github.com/scylladb/gocqlx/v2/gocqlxtest"
"github.com/scylladb/gocqlx/v2/qb"
)
type FullName struct {
@@ -48,7 +49,7 @@ type FullNamePtrUDT struct {
}
func TestIterxStruct(t *testing.T) {
session := CreateSession(t)
session := gocqlxtest.CreateSession(t)
defer session.Close()
if err := session.ExecStmt(`CREATE TYPE gocqlx_test.FullName (first_Name text, last_name text)`); err != nil {
@@ -125,7 +126,10 @@ func TestIterxStruct(t *testing.T) {
Testptrudt: FullNamePtrUDT{FullName: &FullName{FirstName: "John", LastName: "Doe"}},
}
const insertStmt = `INSERT INTO struct_table (testuuid, testtimestamp, testvarchar, testbigint, testblob, testbool, testfloat,testdouble, testint, testdecimal, testlist, testset, testmap, testvarint, testinet, testcustom, testudt, testptrudt) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
const insertStmt = `INSERT INTO struct_table (
testuuid, testtimestamp, testvarchar, testbigint, testblob, testbool, testfloat, testdouble,
testint, testdecimal, testlist, testset, testmap, testvarint, testinet, testcustom, testudt, testptrudt
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
if err := session.Query(insertStmt, nil).Bind(
m.Testuuid,
@@ -212,7 +216,7 @@ func TestIterxStruct(t *testing.T) {
}
func TestIterxScannable(t *testing.T) {
session := CreateSession(t)
session := gocqlxtest.CreateSession(t)
defer session.Close()
if err := session.ExecStmt(`CREATE TABLE gocqlx_test.scannable_table (testfullname text PRIMARY KEY)`); err != nil {
@@ -265,7 +269,7 @@ func TestIterxScannable(t *testing.T) {
}
func TestIterxStructOnly(t *testing.T) {
session := CreateSession(t)
session := gocqlxtest.CreateSession(t)
defer session.Close()
if err := session.ExecStmt(`CREATE TABLE gocqlx_test.struct_only_table (first_name text, last_name text, PRIMARY KEY (first_name, last_name))`); err != nil {
@@ -336,7 +340,7 @@ func TestIterxStructOnly(t *testing.T) {
}
func TestIterxStructOnlyUDT(t *testing.T) {
session := CreateSession(t)
session := gocqlxtest.CreateSession(t)
defer session.Close()
if err := session.ExecStmt(`CREATE TABLE gocqlx_test.struct_only_udt_table (first_name text, last_name text, PRIMARY KEY (first_name, last_name))`); err != nil {
@@ -412,7 +416,7 @@ func TestIterxStructOnlyUDT(t *testing.T) {
}
func TestIterxUnsafe(t *testing.T) {
session := CreateSession(t)
session := gocqlxtest.CreateSession(t)
defer session.Close()
if err := session.ExecStmt(`CREATE TABLE gocqlx_test.unsafe_table (testtext text PRIMARY KEY, testtextunbound text)`); err != nil {
@@ -499,7 +503,7 @@ func TestIterxUnsafe(t *testing.T) {
}
func TestIterxNotFound(t *testing.T) {
session := CreateSession(t)
session := gocqlxtest.CreateSession(t)
defer session.Close()
if err := session.ExecStmt(`CREATE TABLE gocqlx_test.not_found_table (testtext text PRIMARY KEY)`); err != nil {
@@ -547,7 +551,7 @@ func TestIterxNotFound(t *testing.T) {
}
func TestIterxErrorOnNil(t *testing.T) {
session := CreateSession(t)
session := gocqlxtest.CreateSession(t)
defer session.Close()
if err := session.ExecStmt(`CREATE TABLE gocqlx_test.nil_table (testtext text PRIMARY KEY)`); err != nil {
@@ -582,7 +586,7 @@ func TestIterxErrorOnNil(t *testing.T) {
}
func TestIterxPaging(t *testing.T) {
session := CreateSession(t)
session := gocqlxtest.CreateSession(t)
defer session.Close()
if err := session.ExecStmt(`CREATE TABLE gocqlx_test.paging_table (id int PRIMARY KEY, val int)`); err != nil {
@@ -625,7 +629,7 @@ func TestIterxPaging(t *testing.T) {
}
func TestIterxCAS(t *testing.T) {
session := CreateSession(t)
session := gocqlxtest.CreateSession(t)
defer session.Close()
const (

View File

@@ -2,6 +2,7 @@
// 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 cql
@@ -9,5 +10,6 @@ package cql
import "embed"
// Files contains *.cql schema migration files.
//
//go:embed *.cql
var Files embed.FS

View File

@@ -2,6 +2,7 @@
// 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 example

View File

@@ -7,7 +7,6 @@ package migrate
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"io/fs"
@@ -20,6 +19,7 @@ import (
"time"
"github.com/gocql/gocql"
"github.com/scylladb/gocqlx/v2"
"github.com/scylladb/gocqlx/v2/qb"
)
@@ -58,11 +58,11 @@ const (
// Info contains information on migration applied on a database.
type Info struct {
StartTime time.Time
EndTime time.Time
Name string
Checksum string
Done int
StartTime time.Time
EndTime time.Time
}
// List provides a listing of applied migrations.
@@ -128,8 +128,7 @@ func FromFS(ctx context.Context, session gocqlx.Session, f fs.FS) error {
for i := 0; i < len(dbm); i++ {
if dbm[i].Name != fm[i] {
fmt.Println(dbm[i].Name, fm[i], i)
return errors.New("inconsistent migrations")
return fmt.Errorf("inconsistent migrations found, expected %q got %q at %d", fm[i], dbm[i].Name, i)
}
c, err := fileChecksum(f, fm[i])
if err != nil {

View File

@@ -15,8 +15,9 @@ import (
"testing"
"github.com/psanford/memfs"
"github.com/scylladb/gocqlx/v2"
. "github.com/scylladb/gocqlx/v2/gocqlxtest"
"github.com/scylladb/gocqlx/v2/gocqlxtest"
"github.com/scylladb/gocqlx/v2/migrate"
)
@@ -45,7 +46,7 @@ func recreateTables(tb testing.TB, session gocqlx.Session) {
}
func TestMigration(t *testing.T) {
session := CreateSession(t)
session := gocqlxtest.CreateSession(t)
defer session.Close()
recreateTables(t, session)
@@ -91,7 +92,7 @@ func TestMigration(t *testing.T) {
}
func TestMigrationNoSemicolon(t *testing.T) {
session := CreateSession(t)
session := gocqlxtest.CreateSession(t)
defer session.Close()
recreateTables(t, session)
@@ -171,11 +172,11 @@ func TestMigrationCallback(t *testing.T) {
migrate.Callback = func(ctx context.Context, session gocqlx.Session, ev migrate.CallbackEvent, name string) error {
switch ev {
case migrate.BeforeMigration:
beforeCalled += 1
beforeCalled++
case migrate.AfterMigration:
afterCalled += 1
afterCalled++
case migrate.CallComment:
inCalled += 1
inCalled++
}
return nil
}
@@ -191,6 +192,8 @@ func TestMigrationCallback(t *testing.T) {
}
assertCallbacks := func(t *testing.T, before, afer, in int) {
t.Helper()
if beforeCalled != before {
t.Fatalf("expected %d before calls got %d", before, beforeCalled)
}
@@ -202,7 +205,7 @@ func TestMigrationCallback(t *testing.T) {
}
}
session := CreateSession(t)
session := gocqlxtest.CreateSession(t)
defer session.Close()
recreateTables(t, session)

View File

@@ -18,21 +18,21 @@ import (
// BatchBuilder builds CQL BATCH statements.
type BatchBuilder struct {
unlogged bool
counter bool
using using
stmts []string
names []string
using using
unlogged bool
counter bool
}
// Batch returns a new BatchBuilder.
// BatchBuilder encapsulates batch cqls as one ordinary gocql.Query for convenience.
// Below are the limitations of encapsulating batch cqls based on gocql.Query instead of gocql.Batch:
// * gocql.Batch has some more batch specific check, such as BatchSize(65535).
// * gocql.Batch use BatchObserver instead of QueryObserver.
// * gocql.Batch has cancelBatch call back.
// * gocql.Batch prepares the included statements separately, which is more efficient.
// In contrast, gocqlx.qb.BatchBuilder, which is based on gocql.Query, prepares the whole batch statements as one ordinary query.
// - gocql.Batch has some more batch specific check, such as BatchSize(65535).
// - gocql.Batch use BatchObserver instead of QueryObserver.
// - gocql.Batch has cancelBatch call back.
// - gocql.Batch prepares the included statements separately, which is more efficient.
// In contrast, gocqlx.qb.BatchBuilder, which is based on gocql.Query, prepares the whole batch statements as one ordinary query.
//
// Deprecated: Please use gocql.Session.NewBatch() instead.
func Batch() *BatchBuilder {

View File

@@ -39,7 +39,8 @@ func TestBatchBuilder(t *testing.T) {
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 ",
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
@@ -64,7 +65,7 @@ func TestBatchBuilder(t *testing.T) {
},
// Add TIMESTAMP
{
B: Batch().Timestamp(time.Date(2005, 05, 05, 0, 0, 0, 0, time.UTC)),
B: Batch().Timestamp(time.Date(2005, 5, 5, 0, 0, 0, 0, time.UTC)),
S: "BEGIN BATCH USING TIMESTAMP 1115251200000000 APPLY BATCH ",
},
{

View File

@@ -26,9 +26,9 @@ const (
// Cmp if a filtering comparator that is used in WHERE and IF clauses.
type Cmp struct {
op op
column string
value value
column string
op op
}
func (c Cmp) writeCql(cql *bytes.Buffer) (names []string) {

View File

@@ -165,8 +165,6 @@ func TestCmp(t *testing.T) {
S: "like LIKE (?,?)",
N: []string{"name[0]", "name[1]"},
},
// Custom bind names on tuples
{
C: EqTupleNamed("eq", 2, "name"),

View File

@@ -19,9 +19,9 @@ import (
type DeleteBuilder struct {
table string
columns columns
using using
where where
_if _if
using using
exists bool
}

View File

@@ -69,7 +69,7 @@ func TestDeleteBuilder(t *testing.T) {
},
// Add TIMESTAMP
{
B: Delete("cycling.cyclist_name").Where(w).Timestamp(time.Date(2005, 05, 05, 0, 0, 0, 0, time.UTC)),
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"},
},

View File

@@ -17,16 +17,16 @@ import (
// initializer specifies an value for a column in an insert operation.
type initializer struct {
column string
value value
column string
}
// InsertBuilder builds CQL INSERT statements.
type InsertBuilder struct {
table string
columns []initializer
unique bool
using using
unique bool
json bool
}

View File

@@ -17,7 +17,6 @@ func TestInsertBuilder(t *testing.T) {
N []string
S string
}{
// Basic test for insert
{
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname"),
@@ -67,7 +66,7 @@ func TestInsertBuilder(t *testing.T) {
},
// Add TIMESTAMP
{
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").Timestamp(time.Date(2005, 05, 05, 0, 0, 0, 0, time.UTC)),
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"},
},
@@ -78,7 +77,7 @@ func TestInsertBuilder(t *testing.T) {
},
// Add TIMESTAMP
{
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").Timestamp(time.Date(2005, 05, 05, 0, 0, 0, 0, time.UTC)),
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"},
},
@@ -89,7 +88,7 @@ func TestInsertBuilder(t *testing.T) {
},
// Add TIMESTAMP
{
B: Insert("cycling.cyclist_name").Columns("id", "user_uuid", "firstname").Timestamp(time.Date(2005, 05, 05, 0, 0, 0, 0, time.UTC)),
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"},
},

View File

@@ -34,15 +34,15 @@ func (o Order) String() string {
// SelectBuilder builds CQL SELECT statements.
type SelectBuilder struct {
limit limit
limitPerPartition limit
table string
columns columns
distinct columns
using using
where where
groupBy columns
orderBy columns
limit limit
limitPerPartition limit
columns columns
distinct columns
using using
allowFiltering bool
bypassCache bool
json bool
@@ -132,7 +132,7 @@ func (b *SelectBuilder) From(table string) *SelectBuilder {
}
// Json sets the clause of the query.
func (b *SelectBuilder) Json() *SelectBuilder {
func (b *SelectBuilder) Json() *SelectBuilder { // nolint: revive
b.json = true
return b
}

View File

@@ -32,10 +32,10 @@ func (a assignment) writeCql(cql *bytes.Buffer) (names []string) {
// UpdateBuilder builds CQL UPDATE statements.
type UpdateBuilder struct {
table string
using using
assignments []assignment
where where
_if _if
using using
exists bool
}

View File

@@ -117,7 +117,7 @@ func TestUpdateBuilder(t *testing.T) {
},
// Add TIMESTAMP
{
B: Update("cycling.cyclist_name").Set("id", "user_uuid", "firstname").Where(w).Timestamp(time.Date(2005, 05, 05, 0, 0, 0, 0, time.UTC)),
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"},
},

View File

@@ -21,12 +21,12 @@ func Timestamp(t time.Time) int64 {
}
type using struct {
ttl int64
ttlName string
timestamp int64
timestampName string
timeout time.Duration
timeoutName string
ttl int64
timestamp int64
timeout time.Duration
}
func (u *using) TTL(d time.Duration) *using {

View File

@@ -43,7 +43,7 @@ func TestUsing(t *testing.T) {
},
// Timestamp
{
B: new(using).Timestamp(time.Date(2005, 05, 05, 0, 0, 0, 0, time.UTC)),
B: new(using).Timestamp(time.Date(2005, 5, 5, 0, 0, 0, 0, time.UTC)),
S: "USING TIMESTAMP 1115251200000000 ",
},
// TimestampNamed
@@ -65,7 +65,7 @@ func TestUsing(t *testing.T) {
},
// TTL Timestamp
{
B: new(using).TTL(time.Second).Timestamp(time.Date(2005, 05, 05, 0, 0, 0, 0, time.UTC)),
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
@@ -82,7 +82,7 @@ func TestUsing(t *testing.T) {
},
// TTLNamed Timestamp
{
B: new(using).TTLNamed("ttl").Timestamp(time.Date(2005, 05, 05, 0, 0, 0, 0, time.UTC)),
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"},
},
@@ -99,7 +99,7 @@ func TestUsing(t *testing.T) {
},
// TTL Timestamp Timeout
{
B: new(using).TTL(time.Second).Timestamp(time.Date(2005, 05, 05, 0, 0, 0, 0, time.UTC)).Timeout(time.Second),
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
@@ -129,13 +129,13 @@ func TestUsing(t *testing.T) {
},
// Timestamp TimestampNamed
{
B: new(using).Timestamp(time.Date(2005, 05, 05, 0, 0, 0, 0, time.UTC)).TimestampNamed("ts"),
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, 05, 05, 0, 0, 0, 0, time.UTC)),
B: new(using).TimestampNamed("ts").Timestamp(time.Date(2005, 5, 5, 0, 0, 0, 0, time.UTC)),
S: "USING TIMESTAMP 1115251200000000 ",
},
}

View File

@@ -90,12 +90,11 @@ func allowedBindRune(b byte) bool {
// Queryx is a wrapper around gocql.Query which adds struct binding capabilities.
type Queryx struct {
*gocql.Query
Names []string
err error
tr Transformer
Mapper *reflectx.Mapper
tr Transformer
err error
*gocql.Query
Names []string
}
// Query creates a new Queryx from gocql.Query using a default mapper.
@@ -151,7 +150,7 @@ func (q *Queryx) bindStructArgs(arg0 interface{}, arg1 map[string]interface{}) (
// grab the indirected value of arg
v := reflect.ValueOf(arg0)
for v = reflect.ValueOf(arg0); v.Kind() == reflect.Ptr; {
for v.Kind() == reflect.Ptr {
v = v.Elem()
}

View File

@@ -34,7 +34,8 @@ func NewSession(session *gocql.Session) Session {
// the created session to gocqlx.Session.
//
// Example:
// session, err := gocqlx.WrapSession(cluster.CreateSession())
//
// session, err := gocqlx.WrapSession(cluster.CreateSession())
func WrapSession(session *gocql.Session, err error) (Session, error) {
return Session{
Session: session,

View File

@@ -9,6 +9,7 @@ import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/scylladb/gocqlx/v2/qb"
)

2
udt.go
View File

@@ -24,8 +24,8 @@ var (
)
type udt struct {
value reflect.Value
field map[string]reflect.Value
value reflect.Value
unsafe bool
}