dialect/sql: add expression function support

This commit is contained in:
Ariel Mashraki
2021-07-27 10:58:13 +03:00
committed by Ariel Mashraki
parent bab709001c
commit 9de519d027
2 changed files with 57 additions and 4 deletions

View File

@@ -2543,6 +2543,29 @@ type expr struct {
func (e *expr) Query() (string, []interface{}) { return e.s, e.args }
// ExprFunc returns an expression function that implements the Querier interface.
//
// Update("users").
// Set("x", ExprFunc(func(b *Builder) {
// // The sql.Builder config (argc and dialect)
// // was set before the function was executed.
// b.Ident("x").WriteOp(OpAdd).Arg(1)
// }))
//
func ExprFunc(fn func(*Builder)) Querier {
return &exprFunc{fn: fn}
}
type exprFunc struct {
Builder
fn func(*Builder)
}
func (e *exprFunc) Query() (string, []interface{}) {
e.fn(&e.Builder)
return e.Builder.Query()
}
// Queries are list of queries join with space between them.
type Queries []Querier
@@ -2688,12 +2711,12 @@ func (b *Builder) Err() error {
return fmt.Errorf(br.String())
}
// An Op represents a predicate operator.
// An Op represents an operator.
type Op int
// Predicate operators
const (
OpEQ Op = iota // logical and.
// Predicate operators.
OpEQ Op = iota // =
OpNEQ // <>
OpGT // >
OpGTE // >=
@@ -2704,6 +2727,13 @@ const (
OpLike // LIKE
OpIsNull // IS NULL
OpNotNull // IS NOT NULL
// Arithmetic operators.
OpAdd // +
OpSub // -
OpMul // *
OpDiv // / (Quotient)
OpMod // % (Reminder)
)
var ops = [...]string{
@@ -2718,12 +2748,17 @@ var ops = [...]string{
OpLike: "LIKE",
OpIsNull: "IS NULL",
OpNotNull: "IS NOT NULL",
OpAdd: "+",
OpSub: "-",
OpMul: "*",
OpDiv: "/",
OpMod: "%",
}
// WriteOp writes an operator to the builder.
func (b *Builder) WriteOp(op Op) *Builder {
switch {
case op >= OpEQ && op <= OpLike:
case op >= OpEQ && op <= OpLike || op >= OpAdd && op <= OpMod:
b.Pad().WriteString(ops[op]).Pad()
case op == OpIsNull || op == OpNotNull:
b.Pad().WriteString(ops[op])

View File

@@ -1715,3 +1715,21 @@ func TestSelector_UnionOrderBy(t *testing.T) {
Query()
require.Equal(t, `SELECT * FROM "users" WHERE "active" = $1 UNION SELECT * FROM "old_users1" ORDER BY "users"."whatever"`, query)
}
func TestUpdateBuilder_SetExpr(t *testing.T) {
d := Dialect(dialect.Postgres)
excluded := d.Table("excluded")
query, args := d.Update("users").
Set("name", "Ariel").
Set("active", Expr("NOT(active)")).
Set("age", Expr(excluded.C("age"))).
Set("x", ExprFunc(func(b *Builder) {
b.WriteString(excluded.C("x")).WriteString(" || ' (formerly ' || ").Ident("x").WriteString(" || ')'")
})).
Set("y", ExprFunc(func(b *Builder) {
b.Arg("~").WriteOp(OpAdd).WriteString(excluded.C("y")).WriteOp(OpAdd).Arg("~")
})).
Query()
require.Equal(t, `UPDATE "users" SET "name" = $1, "active" = NOT(active), "age" = "excluded"."age", "x" = "excluded"."x" || ' (formerly ' || "x" || ')', "y" = $2 + "excluded"."y" + $3`, query)
require.Equal(t, []interface{}{"Ariel", "~", "~"}, args)
}