Skip to content

Commit

Permalink
transformer: add transformer for unsetting empty values
Browse files Browse the repository at this point in the history
  • Loading branch information
N1cOs authored and mmatczuk committed Nov 26, 2021
1 parent 504f652 commit e502c7c
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 0 deletions.
72 changes: 72 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/scylladb/gocqlx/v2/qb"
"github.com/scylladb/gocqlx/v2/table"
"golang.org/x/sync/errgroup"
"gopkg.in/inf.v0"
)

// Running examples locally:
Expand Down Expand Up @@ -47,6 +48,7 @@ func TestExample(t *testing.T) {
pagingEfficientFullTableScan(t, session)

lwtLock(t, session)
unsetEmptyValues(t, session)
}

// This example shows how to use query builders and table models to build
Expand Down Expand Up @@ -705,6 +707,76 @@ 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) {
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 {
Expand Down
17 changes: 17 additions & 0 deletions transformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,27 @@

package gocqlx

import (
"reflect"

"github.com/gocql/gocql"
)

// Transformer transforms the value of the named parameter to another value.
type Transformer func(name string, val interface{}) interface{}

// DefaultBindTransformer just do nothing.
//
// A custom transformer can always be set per Query.
var DefaultBindTransformer Transformer

// UnsetEmptyTransformer unsets all empty parameters.
// It helps to avoid tombstones when using the same insert/update
// statement for filled and partially filled named parameters.
var UnsetEmptyTransformer = func(name string, val interface{}) interface{} {
v := reflect.ValueOf(val)
if v.IsZero() {
return gocql.UnsetValue
}
return val
}

0 comments on commit e502c7c

Please sign in to comment.