Skip to content

Commit dbfd9e8

Browse files
committed
support with/from/returning and table-like targets in update
1 parent 91b7f95 commit dbfd9e8

File tree

2 files changed

+106
-8
lines changed

2 files changed

+106
-8
lines changed

sqlbuilder_test.go

+27
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,33 @@ func TestUpdate(t *testing.T) {
5757
a.Equal([]interface{}{"jim", 5}, qv)
5858
}
5959

60+
func TestUpdateWithFromReturning(t *testing.T) {
61+
a := assert.New(t)
62+
63+
tbl := NewTable("jobs", "id", "data", "reserved_to", "completed_at")
64+
65+
nextJob := CommonTableExpression("next_job").As(
66+
Select().From(tbl).Columns(tbl.C("id")).Where(BooleanOperator("and",
67+
BooleanOperator("or", IsNull(tbl.C("reserved_to")), Lte(tbl.C("reserved_to"), Func("now"))),
68+
IsNull(tbl.C("completed_at")),
69+
)).OrderBy(OrderAsc(tbl.C("id"))).OffsetLimit(OffsetLimit(nil, Literal("1"))),
70+
)
71+
72+
updatedJob := AliasTable(tbl, "updated_job")
73+
74+
expiry := time.Date(2023, time.July, 25, 18, 34, 15, 0, time.UTC)
75+
76+
q := Update().With(nextJob).Target(updatedJob).Set(UpdateColumns{
77+
updatedJob.C("reserved_to"): Bind(expiry),
78+
}).Where(Eq(updatedJob.C("id"), nextJob.C("id"))).Returning(updatedJob.C("id"))
79+
80+
qs, qv, err := NewSerializer(DialectPostgres{}).F(q.AsStatement).ToSQL()
81+
82+
a.NoError(err)
83+
a.Equal(`WITH "next_job" AS (SELECT "jobs"."id" FROM "jobs" WHERE (("jobs"."reserved_to" IS NULL or ("jobs"."reserved_to" <= now())) and "jobs"."completed_at" IS NULL) ORDER BY "jobs"."id" ASC LIMIT 1) UPDATE "jobs" "updated_job" SET "reserved_to" = $1 WHERE ("updated_job"."id" = "next_job"."id") RETURNING "updated_job"."id"`, qs)
84+
a.Equal([]interface{}{expiry}, qv)
85+
}
86+
6087
func TestSelect(t *testing.T) {
6188
a := assert.New(t)
6289

update.go

+79-8
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,55 @@ package sqlbuilder
33
type UpdateColumns map[*BasicColumn]AsExpr
44

55
type UpdateStatement struct {
6-
table *Table
7-
where AsExpr
8-
set UpdateColumns
6+
with []AsCommonTableExpression
7+
target AsTableOrSubquery
8+
set UpdateColumns
9+
from AsTableOrSubquery
10+
where AsExpr
11+
returning []AsExpr
912
}
1013

1114
func (s *UpdateStatement) clone() *UpdateStatement {
1215
return &UpdateStatement{
13-
table: s.table,
14-
where: s.where,
15-
set: s.set,
16+
with: s.with,
17+
target: s.target,
18+
set: s.set,
19+
from: s.from,
20+
where: s.where,
21+
returning: s.returning,
1622
}
1723
}
1824

1925
func Update() *UpdateStatement {
2026
return &UpdateStatement{set: make(UpdateColumns)}
2127
}
2228

29+
func (s *UpdateStatement) With(with ...AsCommonTableExpression) *UpdateStatement {
30+
c := s.clone()
31+
c.with = with
32+
return c
33+
}
34+
35+
func (s *UpdateStatement) AndWith(with ...AsCommonTableExpression) *UpdateStatement {
36+
c := s.clone()
37+
c.with = append(c.with, with...)
38+
return c
39+
}
40+
2341
func (s *UpdateStatement) Table(table *Table) *UpdateStatement {
42+
return s.Target(table)
43+
}
44+
45+
func (s *UpdateStatement) Target(target AsTableOrSubquery) *UpdateStatement {
2446
c := s.clone()
25-
c.table = table
47+
c.target = target
2648
return c
2749
}
2850

51+
func (s *UpdateStatement) GetTarget() AsTableOrSubquery {
52+
return s.target
53+
}
54+
2955
func (s *UpdateStatement) Set(set UpdateColumns) *UpdateStatement {
3056
c := s.clone()
3157
c.set = set
@@ -47,22 +73,67 @@ func (s *UpdateStatement) AndSet(column *BasicColumn, expr AsExpr) *UpdateStatem
4773
return c
4874
}
4975

76+
func (s *UpdateStatement) From(from AsTableOrSubquery) *UpdateStatement {
77+
c := s.clone()
78+
c.from = from
79+
return c
80+
}
81+
5082
func (s *UpdateStatement) Where(where AsExpr) *UpdateStatement {
5183
c := s.clone()
5284
c.where = where
5385
return c
5486
}
5587

88+
func (s *UpdateStatement) Returning(returning ...AsExpr) *UpdateStatement {
89+
c := s.clone()
90+
c.returning = returning
91+
return c
92+
}
93+
5694
func (q *UpdateStatement) AsStatement(s *Serializer) {
57-
s.D("UPDATE ").F(q.table.AsNamed).D(" SET ")
95+
if len(q.with) > 0 {
96+
s.D("WITH ")
97+
98+
for _, w := range q.with {
99+
if w.IsRecursive() {
100+
s.D("RECURSIVE ")
101+
break
102+
}
103+
}
104+
105+
for i, w := range q.with {
106+
s.F(w.AsCommonTableExpression).DC(",", i != len(q.with)-1).D(" ")
107+
}
108+
}
109+
110+
s.D("UPDATE ").F(q.target.AsTableOrSubquery).D(" SET ")
58111

59112
i := 0
60113
for k, e := range q.set {
61114
s.F(k.AsNamedShort).D(" = ").F(e.AsExpr).DC(", ", i < len(q.set)-1)
62115
i++
63116
}
64117

118+
if q.from != nil {
119+
s.D(" FROM ").F(q.from.AsTableOrSubquery)
120+
}
121+
65122
if q.where != nil {
66123
s.D(" WHERE ").F(q.where.AsExpr)
67124
}
125+
126+
if len(q.returning) > 0 {
127+
s.D(" RETURNING ")
128+
129+
for i, c := range q.returning {
130+
if a, ok := c.(AsResultColumn); ok {
131+
s.F(a.AsResultColumn)
132+
} else {
133+
s.F(c.AsExpr)
134+
}
135+
136+
s.DC(", ", i < len(q.returning)-1)
137+
}
138+
}
68139
}

0 commit comments

Comments
 (0)