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

stephenafamo / bob / 15646986598

14 Jun 2025 01:31AM UTC coverage: 36.722% (-0.3%) from 37.021%
15646986598

push

github

web-flow
Merge pull request #460 from stephenafamo/nested-query

Nest columns with `.` in query codegen

12 of 234 new or added lines in 13 files covered. (5.13%)

5 existing lines in 2 files now uncovered.

8171 of 22251 relevant lines covered (36.72%)

268.43 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/internal/mappings"
12
        "github.com/stephenafamo/bob/orm"
13
        "github.com/stephenafamo/scan"
14
)
15

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

22
func NewView[T any](schema, tableName string) *View[T, []T] {
×
23
        return NewViewx[T, []T](schema, tableName)
×
24
}
×
25

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

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

38
        allCols := orm.NewColumns(mappings.All...).WithParent(alias)
×
39

×
40
        return &View[T, Tslice]{
×
41
                schema:        schema,
×
42
                name:          tableName,
×
43
                alias:         alias,
×
44
                allCols:       allCols,
×
45
                returningCols: allCols.WithParent(tableName),
×
46
                scanner:       scan.StructMapper[T](),
×
47
        }, mappings
×
48
}
49

50
type View[T any, Tslice ~[]T] struct {
51
        schema string
52
        name   string
53
        alias  string
54

55
        allCols       orm.Columns
56
        returningCols orm.Columns
57
        scanner       scan.Mapper[T]
58

59
        AfterSelectHooks bob.Hooks[Tslice, bob.SkipModelHooksKey]
60
        SelectQueryHooks bob.Hooks[*dialect.SelectQuery, bob.SkipQueryHooksKey]
61
}
62

63
func (v *View[T, Tslice]) Name() Expression {
×
64
        // schema is not empty, never override
×
65
        if v.schema != "" {
×
66
                return Quote(v.schema, v.name)
×
67
        }
×
68

69
        return Expression{}.New(orm.SchemaTable(v.name))
×
70
}
71

72
func (v *View[T, Tslice]) NameAs() bob.Expression {
×
73
        return v.Name().As(v.alias)
×
74
}
×
75

76
func (v *View[T, Tslice]) Alias() string {
×
77
        return v.alias
×
78
}
×
79

80
// Returns a column list
81
func (v *View[T, Tslice]) Columns() orm.Columns {
×
82
        // get the schema
×
83
        return v.allCols
×
84
}
×
85

86
// Starts a select query
87
func (v *View[T, Tslice]) Query(queryMods ...bob.Mod[*dialect.SelectQuery]) *ViewQuery[T, Tslice] {
×
88
        q := &ViewQuery[T, Tslice]{
×
NEW
89
                Query: orm.Query[*dialect.SelectQuery, T, Tslice, bob.SliceTransformer[T, Tslice]]{
×
90
                        ExecQuery: orm.ExecQuery[*dialect.SelectQuery]{
×
91
                                BaseQuery: Select(sm.From(v.NameAs())),
×
92
                                Hooks:     &v.SelectQueryHooks,
×
93
                        },
×
94
                        Scanner: v.scanner,
×
95
                },
×
96
        }
×
97

×
98
        q.BaseQuery.Expression.AppendContextualModFunc(
×
99
                func(ctx context.Context, q *dialect.SelectQuery) (context.Context, error) {
×
100
                        if len(q.SelectList.Columns) == 0 {
×
101
                                q.AppendSelect(v.Columns())
×
102
                        }
×
103
                        return ctx, nil
×
104
                },
105
        )
106

107
        q.Apply(queryMods...)
×
108

×
109
        return q
×
110
}
111

112
type ViewQuery[T any, Ts ~[]T] struct {
113
        orm.Query[*dialect.SelectQuery, T, Ts, bob.SliceTransformer[T, Ts]]
114
}
115

116
// Count the number of matching rows
117
func (v *ViewQuery[T, Tslice]) Count(ctx context.Context, exec bob.Executor) (int64, error) {
×
118
        ctx, err := v.RunHooks(ctx, exec)
×
119
        if err != nil {
×
120
                return 0, err
×
121
        }
×
122
        return bob.One(ctx, exec, asCountQuery(v.BaseQuery), scan.SingleColumnMapper[int64])
×
123
}
124

125
// Exists checks if there is any matching row
126
func (v *ViewQuery[T, Tslice]) Exists(ctx context.Context, exec bob.Executor) (bool, error) {
×
127
        count, err := v.Count(ctx, exec)
×
128
        return count > 0, err
×
129
}
×
130

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

×
152
        return countQuery
×
153
}
×
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