Skip to content

Commit 434568a

Browse files
committed
fix(psql): address immutable query review feedback
1 parent 1129a37 commit 434568a

8 files changed

Lines changed: 84 additions & 10 deletions

File tree

dialect/psql/dialect/delete.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ type DeleteQuery struct {
2424
}
2525

2626
func (d *DeleteQuery) SetTargetOnly(only bool) {
27-
d.Table.SetOnly(only)
27+
d.Only = only
28+
d.Table.SetOnly(false)
2829
}
2930

3031
func (d *DeleteQuery) SetTargetTable(table any) {

dialect/psql/dialect/derive.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ func (base *DeleteQuery) Derive(queryMods ...bob.Mod[*DeleteQuery]) (*DeleteQuer
9797
appendDerived[bob.Expression](&next.With.CTEs, base.With.CTEs, &cloneWith, m())
9898
case mods.TargetOnly[*DeleteQuery]:
9999
next.Only = bool(m)
100+
next.Table.Only = false
100101
case mods.TargetTable[*DeleteQuery]:
101102
next.Table = cloneTableRef(clause.TableRef(m))
102103
case mods.Where[*DeleteQuery]:

dialect/psql/dialect/update.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ type UpdateQuery struct {
2525
}
2626

2727
func (u *UpdateQuery) SetTargetOnly(only bool) {
28-
u.Table.SetOnly(only)
28+
u.Only = only
29+
u.Table.SetOnly(false)
2930
}
3031

3132
func (u *UpdateQuery) SetTargetTable(table any) {

dialect/psql/dialect/writer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func (w *queryWriter) writeAny(value any) error {
7777
case uint64:
7878
_, _ = w.w.WriteString(strconv.FormatUint(v, 10))
7979
case sql.NamedArg:
80-
return fmt.Errorf("named args are not supported by psql dialect")
80+
return bob.ErrNoNamedArgs
8181
case bob.Expression:
8282
return w.writeExpression(v)
8383
default:

dialect/psql/immutable_select_test.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ package psql
22

33
import (
44
"context"
5+
"database/sql"
6+
"errors"
57
"testing"
68

79
"github.com/stephenafamo/bob"
10+
"github.com/stephenafamo/bob/dialect/psql/dialect"
811
"github.com/stephenafamo/bob/dialect/psql/sm"
912
"github.com/stephenafamo/bob/expr"
1013
)
@@ -74,6 +77,14 @@ func TestImmutableViewQueryWithDoesNotMutateOriginal(t *testing.T) {
7477
}
7578
}
7679

80+
func TestViewQueryWithNilReceiver(t *testing.T) {
81+
var q *ViewQuery[*someStruct, []*someStruct]
82+
83+
if got := q.With(sm.Where(Quote("id").EQ(Arg(1)))); got != nil {
84+
t.Fatalf("expected nil view query, got %#v", got)
85+
}
86+
}
87+
7788
func TestImmutableSelectQueryApplyDoesNotMutateOriginal(t *testing.T) {
7889
base := Select(
7990
sm.Columns("id"),
@@ -243,8 +254,12 @@ func TestViewSelectQueryHooksUseImmutableSelectQuery(t *testing.T) {
243254
view := NewView[*someStruct, bob.Expression]("public", "some_struct", expr.ColsForStruct[someStruct]("some_struct"))
244255

245256
var hookSQL string
246-
view.SelectQueryHooks.AppendHooks(func(ctx context.Context, exec bob.Executor, q *SelectQuery) (context.Context, error) {
247-
sql, _, err := q.Build(ctx)
257+
view.SelectQueryHooks.AppendHooks(func(ctx context.Context, exec bob.Executor, q *dialect.SelectQuery) (context.Context, error) {
258+
sql, _, err := bob.BaseQuery[*dialect.SelectQuery]{
259+
Expression: q,
260+
Dialect: dialect.Dialect,
261+
QueryType: bob.QueryTypeSelect,
262+
}.Build(ctx)
248263
if err != nil {
249264
return ctx, err
250265
}
@@ -272,6 +287,19 @@ func TestViewSelectQueryHooksUseImmutableSelectQuery(t *testing.T) {
272287
}
273288
}
274289

290+
func TestImmutableSelectQueryBuildReturnsErrNoNamedArgs(t *testing.T) {
291+
_, _, err := Select(
292+
sm.Columns(sql.Named("id", 1)),
293+
sm.From("users"),
294+
).Build(t.Context())
295+
if err == nil {
296+
t.Fatal("expected named arg error")
297+
}
298+
if !errors.Is(err, bob.ErrNoNamedArgs) {
299+
t.Fatalf("expected ErrNoNamedArgs, got %v", err)
300+
}
301+
}
302+
275303
func TestImmutableSelectQueryApplySupportsCommonDerivedMods(t *testing.T) {
276304
base := Select(
277305
sm.Columns("users.id", "users.name"),

dialect/psql/immutable_write_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,41 @@ func TestDeleteApplyDoesNotMutateOriginal(t *testing.T) {
147147
}
148148
}
149149

150+
func TestUpdateApplyDoesNotDuplicateOnly(t *testing.T) {
151+
base := Update(
152+
um.Table("films"),
153+
um.SetCol("kind").ToArg("Drama"),
154+
um.Only(),
155+
)
156+
157+
derived := base.Apply(um.Only())
158+
159+
sql, _, err := derived.Build(t.Context())
160+
if err != nil {
161+
t.Fatal(err)
162+
}
163+
if sql != "UPDATE ONLY films SET\n\"kind\" = $1" {
164+
t.Fatalf("unexpected update SQL: %#v", sql)
165+
}
166+
}
167+
168+
func TestDeleteApplyDoesNotDuplicateOnly(t *testing.T) {
169+
base := Delete(
170+
dm.From("films"),
171+
dm.Only(),
172+
)
173+
174+
derived := base.Apply(dm.Only())
175+
176+
sql, _, err := derived.Build(t.Context())
177+
if err != nil {
178+
t.Fatal(err)
179+
}
180+
if sql != "DELETE FROM ONLY films" {
181+
t.Fatalf("unexpected delete SQL: %#v", sql)
182+
}
183+
}
184+
150185
func TestInsertApplyDoesNotMutateOriginal(t *testing.T) {
151186
base := Insert(
152187
im.Into("films"),

dialect/psql/view.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ type View[T any, Tslice ~[]T, C bob.Expression] struct {
5757
Columns C
5858

5959
AfterSelectHooks bob.Hooks[Tslice, bob.SkipModelHooksKey]
60-
SelectQueryHooks bob.Hooks[*SelectQuery, bob.SkipQueryHooksKey]
60+
SelectQueryHooks bob.Hooks[*dialect.SelectQuery, bob.SkipQueryHooksKey]
6161
}
6262

6363
func (v *View[T, Tslice, C]) Name() Expression {
@@ -100,10 +100,14 @@ func (v *View[T, Tslice, C]) Query(queryMods ...bob.Mod[*dialect.SelectQuery]) *
100100
type ViewQuery[T any, Ts ~[]T] struct {
101101
SelectQuery
102102
Scanner scan.Mapper[T]
103-
Hooks *bob.Hooks[*SelectQuery, bob.SkipQueryHooksKey]
103+
Hooks *bob.Hooks[*dialect.SelectQuery, bob.SkipQueryHooksKey]
104104
}
105105

106106
func (q *ViewQuery[T, Ts]) With(queryMods ...bob.Mod[*dialect.SelectQuery]) *ViewQuery[T, Ts] {
107+
if q == nil {
108+
return nil
109+
}
110+
107111
next := *q
108112
next.SelectQuery = next.SelectQuery.Apply(queryMods...)
109113
return &next
@@ -158,5 +162,5 @@ func (q *ViewQuery[T, Ts]) RunHooks(ctx context.Context, exec bob.Executor) (con
158162
return ctx, nil
159163
}
160164

161-
return q.Hooks.RunHooks(ctx, exec, &q.SelectQuery)
165+
return q.Hooks.RunHooks(ctx, exec, q.SelectQuery.Expression)
162166
}

orm/query.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ func (q *ExecQuery[Q]) With(queryMods ...bob.Mod[Q]) *ExecQuery[Q] {
2828
}
2929

3030
next := q.Clone()
31-
next.BaseQuery = next.BaseQuery.Apply(queryMods...)
31+
for _, mod := range queryMods {
32+
mod.Apply(next.BaseQuery.Expression)
33+
}
3234
return &next
3335
}
3436

@@ -79,7 +81,9 @@ func (q *Query[Q, T, Ts, Tr]) With(queryMods ...bob.Mod[Q]) *Query[Q, T, Ts, Tr]
7981
}
8082

8183
next := q.Clone()
82-
next.BaseQuery = next.BaseQuery.Apply(queryMods...)
84+
for _, mod := range queryMods {
85+
mod.Apply(next.BaseQuery.Expression)
86+
}
8387
return &next
8488
}
8589

0 commit comments

Comments
 (0)