mirror of
https://github.com/hanzoai/sqlbuilder.git
synced 2026-10-01 00:37:42 +00:00
refs #108: support tuples
This commit is contained in:
@@ -222,6 +222,10 @@ func (args *Args) compileArg(buf *strings.Builder, flavor Flavor, values []inter
|
||||
case rawArgs:
|
||||
buf.WriteString(a.expr)
|
||||
case listArgs:
|
||||
if a.isTuple {
|
||||
buf.WriteRune('(')
|
||||
}
|
||||
|
||||
if len(a.args) > 0 {
|
||||
values = args.compileArg(buf, flavor, values, a.args[0])
|
||||
}
|
||||
@@ -230,6 +234,11 @@ func (args *Args) compileArg(buf *strings.Builder, flavor Flavor, values []inter
|
||||
buf.WriteString(", ")
|
||||
values = args.compileArg(buf, flavor, values, a.args[i])
|
||||
}
|
||||
|
||||
if a.isTuple {
|
||||
buf.WriteRune(')')
|
||||
}
|
||||
|
||||
default:
|
||||
switch flavor {
|
||||
case MySQL, SQLite, CQL, ClickHouse, Presto:
|
||||
|
||||
+30
-3
@@ -47,7 +47,12 @@ func flatten(v reflect.Value) (elem interface{}, flattened []interface{}) {
|
||||
}
|
||||
|
||||
if k != reflect.Slice && k != reflect.Array {
|
||||
return v.Interface(), nil
|
||||
if !v.IsValid() || !v.CanInterface() {
|
||||
return
|
||||
}
|
||||
|
||||
elem = v.Interface()
|
||||
return elem, nil
|
||||
}
|
||||
|
||||
for i, l := 0, v.Len(); i < l; i++ {
|
||||
@@ -73,13 +78,35 @@ func Raw(expr string) interface{} {
|
||||
}
|
||||
|
||||
type listArgs struct {
|
||||
args []interface{}
|
||||
args []interface{}
|
||||
isTuple bool
|
||||
}
|
||||
|
||||
// List marks arg as a list of data.
|
||||
// If arg is `[]int{1, 2, 3}`, it will be compiled to `?, ?, ?` with args `[1 2 3]`.
|
||||
func List(arg interface{}) interface{} {
|
||||
return listArgs{Flatten(arg)}
|
||||
return listArgs{
|
||||
args: Flatten(arg),
|
||||
}
|
||||
}
|
||||
|
||||
// Tuple wraps values into a tuple and can be used as a single value.
|
||||
func Tuple(values ...interface{}) interface{} {
|
||||
return listArgs{
|
||||
args: values,
|
||||
isTuple: true,
|
||||
}
|
||||
}
|
||||
|
||||
// TupleNames joins names with tuple format.
|
||||
// The names is not escaped. Use `EscapeAll` to escape them if necessary.
|
||||
func TupleNames(names ...string) string {
|
||||
buf := &strings.Builder{}
|
||||
buf.WriteRune('(')
|
||||
buf.WriteString(strings.Join(names, ", "))
|
||||
buf.WriteRune(')')
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
type namedArgs struct {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package sqlbuilder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/huandu/go-assert"
|
||||
@@ -54,3 +55,47 @@ func TestFlatten(t *testing.T) {
|
||||
a.Equal(actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTuple(t *testing.T) {
|
||||
a := assert.New(t)
|
||||
cases := []struct {
|
||||
values []interface{}
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
nil,
|
||||
"()",
|
||||
},
|
||||
{
|
||||
[]interface{}{1, "bar", nil, Tuple("foo", Tuple(2, "baz"))},
|
||||
"(1, 'bar', NULL, ('foo', (2, 'baz')))",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
sql, args := Build("$?", Tuple(c.values...)).Build()
|
||||
actual, err := DefaultFlavor.Interpolate(sql, args)
|
||||
a.NilError(err)
|
||||
a.Equal(actual, c.expected)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleTuple() {
|
||||
sb := Select("id", "name").From("user")
|
||||
sb.Where(
|
||||
sb.In(
|
||||
TupleNames("type", "status"),
|
||||
Tuple("web", 1),
|
||||
Tuple("app", 1),
|
||||
Tuple("app", 2),
|
||||
),
|
||||
)
|
||||
sql, args := sb.Build()
|
||||
|
||||
fmt.Println(sql)
|
||||
fmt.Println(args)
|
||||
|
||||
// Output:
|
||||
// SELECT id, name FROM user WHERE (type, status) IN ((?, ?), (?, ?), (?, ?))
|
||||
// [web 1 app 1 app 2]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user