• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stephenafamo / bob / 26625652402

29 May 2026 07:55AM UTC coverage: 46.047% (+0.2%) from 45.83%
26625652402

Pull #701

github

Roman A. Grigorovich
Add Assign for SET clauses without extra parentheses.

EQ wraps comparisons in parentheses, which breaks UPDATE SET. Introduce
expr.Assign and Chain.Assign for col = val in um.Set / im.Set / mm.Set.
Pull Request #701: Add Assign for SET clauses; omit redundant AS in NameAsExpr

18 of 25 new or added lines in 5 files covered. (72.0%)

3 existing lines in 1 file now uncovered.

11305 of 24551 relevant lines covered (46.05%)

664.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

51.92
/dialect/sqlite/view.go
1
package sqlite
2

3
import (
4
        "context"
5
        "fmt"
6
        "reflect"
7

8
        "github.com/stephenafamo/bob"
9
        "github.com/stephenafamo/bob/dialect/sqlite/dialect"
10
        "github.com/stephenafamo/bob/dialect/sqlite/sm"
11
        "github.com/stephenafamo/bob/expr"
12
        "github.com/stephenafamo/bob/internal/mappings"
13
        "github.com/stephenafamo/bob/orm"
14
        "github.com/stephenafamo/scan"
15
)
16

17
// UseSchema modifies a context to add a schema that will be used when
18
// a tablle/view was generated with an empty schema
19
func UseSchema(ctx context.Context, schema string) context.Context {
×
20
        return context.WithValue(ctx, orm.CtxUseSchema, schema)
×
21
}
×
22

23
func NewView[T any, C bob.Expression](schema, tableName string, columns C) *View[T, []T, C] {
2✔
24
        return NewViewx[T, []T](schema, tableName, columns)
2✔
25
}
2✔
26

27
func NewViewx[T any, Tslice ~[]T, C bob.Expression](schema, tableName string, columns C) *View[T, Tslice, C] {
2✔
28
        v, _ := newView[T, Tslice](schema, tableName, columns)
2✔
29
        return v
2✔
30
}
2✔
31

32
func newView[T any, Tslice ~[]T, C bob.Expression](schema, tableName string, columns C) (*View[T, Tslice, C], mappings.Mapping) {
2✔
33
        mappings := mappings.GetMappings(reflect.TypeOf(*new(T)))
2✔
34
        alias := tableName
2✔
35
        if schema != "" {
2✔
36
                alias = fmt.Sprintf("%s.%s", schema, tableName)
×
37
        }
×
38

39
        allCols := expr.NewColumnsExpr(mappings.All...).WithParent(alias)
2✔
40

2✔
41
        return &View[T, Tslice, C]{
2✔
42
                schema:        schema,
2✔
43
                name:          tableName,
2✔
44
                alias:         alias,
2✔
45
                allCols:       allCols,
2✔
46
                returningCols: allCols.WithParent(tableName),
2✔
47
                scanner:       scan.StructMapper[T](),
2✔
48
                Columns:       columns,
2✔
49
        }, mappings
2✔
50
}
51

52
type View[T any, Tslice ~[]T, C bob.Expression] struct {
53
        schema string
54
        name   string
55
        alias  string
56

57
        allCols       expr.ColumnsExpr
58
        returningCols expr.ColumnsExpr
59
        scanner       scan.Mapper[T]
60

61
        Columns C
62

63
        AfterSelectHooks bob.Hooks[Tslice, bob.SkipModelHooksKey]
64
        SelectQueryHooks bob.Hooks[*dialect.SelectQuery, bob.SkipQueryHooksKey]
65
}
66

67
// NameExpr returns the table name as an expression
68
func (v *View[T, Tslice, C]) NameExpr() Expression {
4✔
69
        // schema is not empty, never override
4✔
70
        if v.schema != "" {
4✔
71
                return Quote(v.schema, v.name)
×
72
        }
×
73

74
        return Expression{}.New(orm.SchemaTable(v.name))
4✔
75
}
76

77
// NameAsExpr returns the table name as an expression with an alias when needed.
78
func (v *View[T, Tslice, C]) NameAsExpr() bob.Expression {
4✔
79
        expr := v.NameExpr()
4✔
80
        if v.schema != "" || v.alias != v.name {
4✔
NEW
81
                return expr.As(v.alias)
×
NEW
82
        }
×
83
        return expr
4✔
84
}
85

86
// Alias returns the alias
87
func (v *View[T, Tslice, C]) Alias() string {
×
88
        return v.alias
×
89
}
×
90

91
// Schema returns the schema name for the view
92
func (v *View[T, Tslice, C]) Schema() string {
×
93
        return v.schema
×
94
}
×
95

96
// Name returns the view (table/view) name
97
func (v *View[T, Tslice, C]) Name() string {
×
98
        return v.name
×
99
}
×
100

101
// ColumnsExpr returns a column list expression
102
func (v *View[T, Tslice, C]) ColumnsExpr() expr.ColumnsExpr {
2✔
103
        // get the schema
2✔
104
        return v.allCols
2✔
105
}
2✔
106

107
// Query starts a select query on the view
108
func (v *View[T, Tslice, C]) Query(queryMods ...bob.Mod[*dialect.SelectQuery]) *ViewQuery[T, Tslice] {
2✔
109
        q := &ViewQuery[T, Tslice]{
2✔
110
                Query: orm.Query[*dialect.SelectQuery, T, Tslice, bob.SliceTransformer[T, Tslice]]{
2✔
111
                        ExecQuery: orm.ExecQuery[*dialect.SelectQuery]{
2✔
112
                                BaseQuery: Select(sm.From(v.NameAsExpr())),
2✔
113
                                Hooks:     &v.SelectQueryHooks,
2✔
114
                        },
2✔
115
                        Scanner: v.scanner,
2✔
116
                },
2✔
117
        }
2✔
118

2✔
119
        q.BaseQuery.Expression.AppendContextualModFunc(
2✔
120
                func(ctx context.Context, q *dialect.SelectQuery) (context.Context, error) {
4✔
121
                        if len(q.SelectList.Columns) == 0 {
4✔
122
                                q.AppendSelect(v.ColumnsExpr())
2✔
123
                        }
2✔
124
                        return ctx, nil
2✔
125
                },
126
        )
127

128
        q.Apply(queryMods...)
2✔
129

2✔
130
        return q
2✔
131
}
132

133
type ViewQuery[T any, Ts ~[]T] struct {
134
        orm.Query[*dialect.SelectQuery, T, Ts, bob.SliceTransformer[T, Ts]]
135
}
136

137
// Count the number of matching rows
138
func (v *ViewQuery[T, Tslice]) Count(ctx context.Context, exec bob.Executor) (int64, error) {
×
139
        ctx, err := v.RunHooks(ctx, exec)
×
140
        if err != nil {
×
141
                return 0, err
×
142
        }
×
143
        return bob.One(ctx, exec, asCountQuery(v.BaseQuery), scan.SingleColumnMapper[int64])
×
144
}
145

146
// Exists checks if there is any matching row
147
func (v *ViewQuery[T, Tslice]) Exists(ctx context.Context, exec bob.Executor) (bool, error) {
×
148
        count, err := v.Count(ctx, exec)
×
149
        return count > 0, err
×
150
}
×
151

152
// asCountQuery clones and rewrites an existing query to a count query
153
func asCountQuery(query bob.BaseQuery[*dialect.SelectQuery]) bob.BaseQuery[*dialect.SelectQuery] {
×
154
        // clone the original query, so it's not being modified silently
×
155
        countQuery := query.Clone()
×
156
        // only select the count
×
157
        countQuery.Expression.SetSelect("count(1)")
×
158
        // don't select any preload columns
×
159
        countQuery.Expression.SetPreloadSelect()
×
160
        // disable mapper mods
×
161
        countQuery.Expression.SetMapperMods()
×
162
        // disable loaders
×
163
        countQuery.Expression.SetLoaders()
×
164
        // set the limit to 1
×
165
        countQuery.Expression.SetLimit(1)
×
166
        // remove ordering
×
167
        countQuery.Expression.ClearOrderBy()
×
168
        // remove group by
×
169
        countQuery.Expression.SetGroups()
×
170
        // remove offset
×
171
        countQuery.Expression.SetOffset(0)
×
172

×
173
        return countQuery
×
174
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc