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

stephenafamo / bob / 26239357742

21 May 2026 04:31PM UTC coverage: 45.452% (+0.009%) from 45.443%
26239357742

Pull #691

github

Roman A. Grigorovich
feat(dialect): add TableName and ColumnsExpr to View types

Add TableName() to mysql, psql, and sqlite View types.
Add ColumnsExpr() to psql.View for parity with other dialects.
Pull Request #691: feat(dialect): add TableName and ColumnsExpr to View types

4 of 21 new or added lines in 3 files covered. (19.05%)

21 existing lines in 2 files now uncovered.

11123 of 24472 relevant lines covered (45.45%)

663.92 hits per line

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

0.0
/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] {
×
24
        return NewViewx[T, []T](schema, tableName, columns)
×
25
}
×
26

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

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

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

×
41
        return &View[T, Tslice, C]{
×
42
                schema:        schema,
×
43
                name:          tableName,
×
44
                alias:         alias,
×
45
                allCols:       allCols,
×
46
                returningCols: allCols.WithParent(tableName),
×
47
                scanner:       scan.StructMapper[T](),
×
48
                Columns:       columns,
×
49
        }, mappings
×
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
func (v *View[T, Tslice, C]) Name() Expression {
×
68
        // schema is not empty, never override
×
69
        if v.schema != "" {
×
70
                return Quote(v.schema, v.name)
×
71
        }
×
72

73
        return Expression{}.New(orm.SchemaTable(v.name))
×
74
}
75

76
func (v *View[T, Tslice, C]) NameAs() bob.Expression {
×
77
        return v.Name().As(v.alias)
×
78
}
×
79

80
func (v *View[T, Tslice, C]) Alias() string {
×
81
        return v.alias
×
82
}
×
83

84
// TableName returns the table name
NEW
85
func (v *View[T, Tslice, C]) TableName() string {
×
NEW
86
        return v.name
×
NEW
87
}
×
88

89
// ColumnsExpr returns a column list
NEW
90
func (v *View[T, Tslice, C]) ColumnsExpr() expr.ColumnsExpr {
×
NEW
91
        // get the schema
×
NEW
92
        return v.allCols
×
NEW
93
}
×
94

95
// Starts a select query
96
func (v *View[T, Tslice, C]) Query(queryMods ...bob.Mod[*dialect.SelectQuery]) *ViewQuery[T, Tslice] {
×
97
        q := &ViewQuery[T, Tslice]{
×
98
                Query: orm.Query[*dialect.SelectQuery, T, Tslice, bob.SliceTransformer[T, Tslice]]{
×
UNCOV
99
                        ExecQuery: orm.ExecQuery[*dialect.SelectQuery]{
×
UNCOV
100
                                BaseQuery: Select(sm.From(v.NameAs())),
×
101
                                Hooks:     &v.SelectQueryHooks,
×
102
                        },
×
103
                        Scanner: v.scanner,
×
104
                },
×
105
        }
×
106

×
107
        q.BaseQuery.Expression.AppendContextualModFunc(
×
108
                func(ctx context.Context, q *dialect.SelectQuery) (context.Context, error) {
×
109
                        if len(q.SelectList.Columns) == 0 {
×
110
                                q.AppendSelect(v.ColumnsExpr())
×
111
                        }
×
112
                        return ctx, nil
×
113
                },
114
        )
115

116
        q.Apply(queryMods...)
×
117

×
UNCOV
118
        return q
×
119
}
120

121
type ViewQuery[T any, Ts ~[]T] struct {
122
        orm.Query[*dialect.SelectQuery, T, Ts, bob.SliceTransformer[T, Ts]]
123
}
124

125
// Count the number of matching rows
UNCOV
126
func (v *ViewQuery[T, Tslice]) Count(ctx context.Context, exec bob.Executor) (int64, error) {
×
UNCOV
127
        ctx, err := v.RunHooks(ctx, exec)
×
UNCOV
128
        if err != nil {
×
UNCOV
129
                return 0, err
×
UNCOV
130
        }
×
131
        return bob.One(ctx, exec, asCountQuery(v.BaseQuery), scan.SingleColumnMapper[int64])
×
132
}
133

134
// Exists checks if there is any matching row
135
func (v *ViewQuery[T, Tslice]) Exists(ctx context.Context, exec bob.Executor) (bool, error) {
×
136
        count, err := v.Count(ctx, exec)
×
UNCOV
137
        return count > 0, err
×
UNCOV
138
}
×
139

140
// asCountQuery clones and rewrites an existing query to a count query
141
func asCountQuery(query bob.BaseQuery[*dialect.SelectQuery]) bob.BaseQuery[*dialect.SelectQuery] {
×
142
        // clone the original query, so it's not being modified silently
×
143
        countQuery := query.Clone()
×
UNCOV
144
        // only select the count
×
UNCOV
145
        countQuery.Expression.SetSelect("count(1)")
×
146
        // don't select any preload columns
×
147
        countQuery.Expression.SetPreloadSelect()
×
148
        // disable mapper mods
×
149
        countQuery.Expression.SetMapperMods()
×
150
        // disable loaders
×
151
        countQuery.Expression.SetLoaders()
×
152
        // set the limit to 1
×
153
        countQuery.Expression.SetLimit(1)
×
154
        // remove ordering
×
155
        countQuery.Expression.ClearOrderBy()
×
156
        // remove group by
×
157
        countQuery.Expression.SetGroups()
×
158
        // remove offset
×
159
        countQuery.Expression.SetOffset(0)
×
160

×
161
        return countQuery
×
162
}
×
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