From 2e903a54e0c0a56c635134846768bb0a7493107d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Matczuk?= Date: Wed, 30 Aug 2017 10:04:33 +0200 Subject: [PATCH] cmp: functions support --- qb/cmp.go | 98 ++++++++++++++++++++++++++++++++++---------- qb/cmp_test.go | 107 +++++++++++++++++++++++++++++++------------------ 2 files changed, 144 insertions(+), 61 deletions(-) diff --git a/qb/cmp.go b/qb/cmp.go index 3c7f21e..35edcb5 100644 --- a/qb/cmp.go +++ b/qb/cmp.go @@ -1,6 +1,11 @@ package qb -import "bytes" +// Functions reference: +// http://cassandra.apache.org/doc/latest/cql/functions.html + +import ( + "bytes" +) // op specifies Cmd operation type. type op byte @@ -19,12 +24,54 @@ const ( type Cmp struct { op op column string - name string + fn string + names []string } -func (cmp Cmp) writeCql(cql *bytes.Buffer) string { - cql.WriteString(cmp.column) - switch cmp.op { +// Func wraps comparator value with a custom function, fn is a function name, +// names are function arguments' bind names. For instance function: +// +// CREATE FUNCTION somefunction(somearg int, anotherarg text) +// +// can be used like this: +// +// stmt, names := qb.Select("table"). +// Where(qb.Eq("t").Func("somefunction", "somearg", "anotherarg")). +// ToCql() +// +// q := gocqlx.Query(session.Query(stmt), names).BindMap(qb.M{ +// "somearg": 1, +// "anotherarg": "text", +// }) +func (c Cmp) Func(fn string, names ...string) Cmp { + c.fn = fn + c.names = names + return c +} + +// MinTimeuuid sets minTimeuuid(?) compare value. +func (c Cmp) MinTimeuuid(name string) Cmp { + return c.Func("minTimeuuid", name) +} + +// MaxTimeuuid sets maxTimeuuid(?) compare value. +func (c Cmp) MaxTimeuuid(name string) Cmp { + return c.Func("maxTimeuuid", name) +} + +// Now sets now() compare value. +func (c Cmp) Now() Cmp { + return c.Func("now") +} + +// Token sets Token(?,?...) compare value. +func (c Cmp) Token(names ...string) Cmp { + return c.Func("token", names...) +} + +func (c Cmp) writeCql(cql *bytes.Buffer) (names []string) { + cql.WriteString(c.column) + switch c.op { case eq: cql.WriteByte('=') case lt: @@ -42,9 +89,23 @@ func (cmp Cmp) writeCql(cql *bytes.Buffer) string { case cnt: cql.WriteString(" CONTAINS ") } - cql.WriteByte('?') - return cmp.name + if c.fn == "" { + cql.WriteByte('?') + if c.names == nil { + names = append(names, c.column) + } else { + names = append(names, c.names...) + } + } else { + cql.WriteString(c.fn) + cql.WriteByte('(') + placeholders(cql, len(c.names)) + cql.WriteByte(')') + names = append(names, c.names...) + } + + return } // Eq produces column=?. @@ -52,7 +113,6 @@ func Eq(column string) Cmp { return Cmp{ op: eq, column: column, - name: column, } } @@ -61,7 +121,7 @@ func EqNamed(column, name string) Cmp { return Cmp{ op: eq, column: column, - name: name, + names: []string{name}, } } @@ -70,7 +130,6 @@ func Lt(column string) Cmp { return Cmp{ op: lt, column: column, - name: column, } } @@ -79,7 +138,7 @@ func LtNamed(column, name string) Cmp { return Cmp{ op: lt, column: column, - name: name, + names: []string{name}, } } @@ -88,7 +147,6 @@ func LtOrEq(column string) Cmp { return Cmp{ op: leq, column: column, - name: column, } } @@ -97,7 +155,7 @@ func LtOrEqNamed(column, name string) Cmp { return Cmp{ op: leq, column: column, - name: name, + names: []string{name}, } } @@ -106,7 +164,6 @@ func Gt(column string) Cmp { return Cmp{ op: gt, column: column, - name: column, } } @@ -115,7 +172,7 @@ func GtNamed(column, name string) Cmp { return Cmp{ op: gt, column: column, - name: name, + names: []string{name}, } } @@ -124,7 +181,6 @@ func GtOrEq(column string) Cmp { return Cmp{ op: geq, column: column, - name: column, } } @@ -133,7 +189,7 @@ func GtOrEqNamed(column, name string) Cmp { return Cmp{ op: geq, column: column, - name: name, + names: []string{name}, } } @@ -142,7 +198,6 @@ func In(column string) Cmp { return Cmp{ op: in, column: column, - name: column, } } @@ -151,7 +206,7 @@ func InNamed(column, name string) Cmp { return Cmp{ op: in, column: column, - name: name, + names: []string{name}, } } @@ -160,7 +215,6 @@ func Contains(column string) Cmp { return Cmp{ op: cnt, column: column, - name: column, } } @@ -169,7 +223,7 @@ func ContainsNamed(column, name string) Cmp { return Cmp{ op: cnt, column: column, - name: name, + names: []string{name}, } } @@ -177,7 +231,7 @@ type cmps []Cmp func (cs cmps) writeCql(cql *bytes.Buffer) (names []string) { for i, c := range cs { - names = append(names, c.writeCql(cql)) + names = append(names, c.writeCql(cql)...) if i < len(cs)-1 { cql.WriteString(" AND ") } diff --git a/qb/cmp_test.go b/qb/cmp_test.go index d0870eb..7daa619 100644 --- a/qb/cmp_test.go +++ b/qb/cmp_test.go @@ -11,77 +11,106 @@ func TestCmp(t *testing.T) { table := []struct { C Cmp S string - N string + N []string }{ + // Basic comparators { C: Eq("eq"), S: "eq=?", - N: "eq", - }, - { - C: EqNamed("eq", "name"), - S: "eq=?", - N: "name", + N: []string{"eq"}, }, { C: Lt("lt"), S: "lt?", - N: "gt", - }, - { - C: GtNamed("gt", "name"), - S: "gt>?", - N: "name", + N: []string{"gt"}, }, { C: GtOrEq("gt"), S: "gt>=?", - N: "gt", - }, - { - C: GtOrEqNamed("gt", "name"), - S: "gt>=?", - N: "name", + N: []string{"gt"}, }, { C: In("in"), S: "in IN ?", - N: "in", - }, - { - C: InNamed("in", "name"), - S: "in IN ?", - N: "name", + N: []string{"in"}, }, { C: Contains("cnt"), S: "cnt CONTAINS ?", - N: "cnt", + N: []string{"cnt"}, + }, + + // Custom bind names + { + C: EqNamed("eq", "name"), + S: "eq=?", + N: []string{"name"}, + }, + { + C: LtNamed("lt", "name"), + S: "lt?", + N: []string{"name"}, + }, + { + C: GtOrEqNamed("gt", "name"), + S: "gt>=?", + N: []string{"name"}, + }, + { + C: InNamed("in", "name"), + S: "in IN ?", + N: []string{"name"}, }, { C: ContainsNamed("cnt", "name"), S: "cnt CONTAINS ?", - N: "name", + N: []string{"name"}, + }, + + // Functions + { + C: Eq("eq").Func("fn", "arg0", "arg1"), + S: "eq=fn(?,?)", + N: []string{"arg0", "arg1"}, + }, + { + C: Eq("eq").MaxTimeuuid("arg0"), + S: "eq=maxTimeuuid(?)", + N: []string{"arg0"}, + }, + { + C: Eq("eq").MinTimeuuid("arg0"), + S: "eq=minTimeuuid(?)", + N: []string{"arg0"}, + }, + { + C: Eq("eq").Now(), + S: "eq=now()", + }, + { + C: Eq("eq").Token("arg0", "arg1"), + S: "eq=token(?,?)", + N: []string{"arg0", "arg1"}, }, }