diff --git a/builder/args.go b/builder/args.go index 59ef976..6870c8c 100644 --- a/builder/args.go +++ b/builder/args.go @@ -22,12 +22,28 @@ func (a argExp) WriteSQL(sb *SQLBuilder) { sb.WriteString(p) } +type Expressions []Exp + +func (e Expressions) IsExp() {} + +func (e Expressions) WriteSQL(sb *SQLBuilder) { + sb.WriteRune('(') + for i, exp := range e { + if i > 0 { + sb.WriteRune(',') + } + exp.WriteSQL(sb) + } + sb.WriteRune(')') +} + +func (e Expressions) isSelectOrExpressions() {} + // Args creates argument expressions for the given arguments. -func Args(argument any, rest ...any) []Exp { - exps := make([]Exp, 1+len(rest)) - exps[0] = Arg(argument) - for i, arg := range rest { - exps[i+1] = Arg(arg) +func Args[T any](arguments ...T) Expressions { + exps := make([]Exp, len(arguments)) + for i, arg := range arguments { + exps[i] = Arg(arg) } return exps } diff --git a/builder/helper.go b/builder/helper.go index 641bb5c..0f36c6b 100644 --- a/builder/helper.go +++ b/builder/helper.go @@ -41,3 +41,13 @@ func cloneSlice[T any](dst *[]T, src []T, additionalCapacity int) { *dst = make([]T, len(src), len(src)+additionalCapacity) copy(*dst, src) } + +func nonNil(exps []Exp) []Exp { + result := make([]Exp, 0, len(exps)) + for _, exp := range exps { + if exp != nil { + result = append(result, exp) + } + } + return result +} diff --git a/builder/op.go b/builder/op.go index ea539c6..75a12c1 100644 --- a/builder/op.go +++ b/builder/op.go @@ -133,16 +133,18 @@ func (u unaryExp) WriteSQL(sb *SQLBuilder) { // --- Junction expressions +// And builds an AND expression of non-nil expressions. func And(exps ...Exp) Exp { return junctionExp{ - exps: exps, + exps: nonNil(exps), op: "AND", } } +// Or builds an OR expression of non-nil expressions. func Or(exps ...Exp) Exp { return junctionExp{ - exps: exps, + exps: nonNil(exps), op: "OR", } } diff --git a/insert_builder_test.go b/insert_builder_test.go index ea045ce..909c176 100644 --- a/insert_builder_test.go +++ b/insert_builder_test.go @@ -321,7 +321,14 @@ func TestInsertBuilder(t *testing.T) { q := qrb. InsertInto(qrb.N("films")). ColumnNames("code", "date_prod", "did", "kind", "length", "title"). - Values(qrb.Args("UA502", "1971-07-13", 105, "Comedy", "82 minutes", "Bananas")...) + Values( + qrb.Arg("UA502"), + qrb.Arg("1971-07-13"), + qrb.Arg(105), + qrb.Arg("Comedy"), + qrb.Arg("82 minutes"), + qrb.Arg("Bananas"), + ) testhelper.AssertSQLWriterEquals( t, diff --git a/root.go b/root.go index 74e77be..6fa6829 100644 --- a/root.go +++ b/root.go @@ -91,9 +91,9 @@ func Arg(argument any) builder.ExpBase { return builder.Arg(argument) } -// Args creates argument expressions for the given arguments. -func Args(argument any, rest ...any) []builder.Exp { - return builder.Args(argument, rest...) +// Args creates argument expressions for the given arguments (of the same type). +func Args[T any](arguments ...T) builder.Expressions { + return builder.Args(arguments...) } // Bind creates an expression that represents an argument that will be bound to a placeholder with the given value. @@ -144,7 +144,7 @@ func Interval(s string) builder.Exp { // Exps returns a slice of expressions, just for syntactic sugar. // TODO We could use this as a way to express a scalar list of expressions e.g. for IN by using a custom slice type -func Exps(exps ...builder.Exp) []builder.Exp { +func Exps(exps ...builder.Exp) builder.Expressions { return exps } diff --git a/select_builder_test.go b/select_builder_test.go index b76fd54..10cfcdb 100644 --- a/select_builder_test.go +++ b/select_builder_test.go @@ -822,6 +822,42 @@ func TestSelectBuilder_Where(t *testing.T) { q, ) }) + + t.Run("where in args", func(t *testing.T) { + ids := []int{1, 2, 3} + + q := qrb.Select(qrb.N("username")). + From(qrb.N("accounts")). + Where(qrb.N("id").In(qrb.Args(ids...))) + + testhelper.AssertSQLWriterEquals( + t, + ` + SELECT username + FROM accounts + WHERE id IN ($1, $2, $3) + `, + []any{1, 2, 3}, + q, + ) + }) + + t.Run("where in exps", func(t *testing.T) { + q := qrb.Select(qrb.N("username")). + From(qrb.N("accounts")). + Where(qrb.N("id").In(qrb.Exps(qrb.Int(42), qrb.String("abc")))) + + testhelper.AssertSQLWriterEquals( + t, + ` + SELECT username + FROM accounts + WHERE id IN (42, 'abc') + `, + nil, + q, + ) + }) } func TestSelectBuilder_GroupBy(t *testing.T) {