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

stephenafamo / bob / 26505391422

27 May 2026 10:21AM UTC coverage: 45.826% (+0.5%) from 45.366%
26505391422

Pull #697

github

Roman A. Grigorovich
refactor: clean up SQL formatting in tests and improve error handling

Removed commented-out code and improved SQL formatting in test cases for MySQL and PostgreSQL dialects. Enhanced error handling in the formatter function to ensure cleaner output. Updated tests to utilize the formatter consistently across various SQL operations.
Pull Request #697: fix: quote SQL identifiers across dialects

125 of 177 new or added lines in 30 files covered. (70.62%)

5 existing lines in 5 files now uncovered.

11241 of 24530 relevant lines covered (45.83%)

664.14 hits per line

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

72.73
/dialect/psql/dialect/function.go
1
package dialect
2

3
import (
4
        "context"
5
        "io"
6

7
        "github.com/stephenafamo/bob"
8
        "github.com/stephenafamo/bob/clause"
9
        "github.com/stephenafamo/bob/expr"
10
)
11

12
func NewFunction(name any, args ...any) *Function {
44✔
13
        f := &Function{name: name, args: args}
44✔
14
        f.Chain = expr.Chain[Expression, Expression]{Base: f}
44✔
15

44✔
16
        return f
44✔
17
}
44✔
18

19
type Function struct {
20
        name any
21
        args []any
22

23
        Distinct    bool
24
        WithinGroup bool
25
        clause.OrderBy
26
        Filter []any
27
        w      *clause.Window
28

29
        Alias   string // used when there should be an alias before the columns
30
        Columns []columnDef
31

32
        expr.Chain[Expression, Expression]
33
}
34

35
func (f *Function) SetWindow(w clause.Window) {
6✔
36
        f.w = &w
6✔
37
}
6✔
38

39
func (f *Function) AppendColumn(name, datatype string) {
14✔
40
        f.Columns = append(f.Columns, columnDef{
14✔
41
                name:     name,
14✔
42
                dataType: datatype,
14✔
43
        })
14✔
44
}
14✔
45

46
func (f *Function) WriteSQL(ctx context.Context, w io.StringWriter, d bob.Dialect, start int) ([]any, error) {
44✔
47
        nameArgs, err := bob.Express(ctx, w, d, start, f.name)
44✔
48
        if err != nil {
44✔
NEW
49
                return nil, err
×
UNCOV
50
        }
×
51

52
        w.WriteString("(")
44✔
53
        start += len(nameArgs)
44✔
54

44✔
55
        if f.Distinct {
44✔
56
                w.WriteString("DISTINCT ")
×
57
        }
×
58

59
        args, err := bob.ExpressSlice(ctx, w, d, start, f.args, "", ", ", "")
44✔
60
        if err != nil {
44✔
61
                return nil, err
×
62
        }
×
63

64
        if !f.WithinGroup {
88✔
65
                orderArgs, err := bob.ExpressIf(ctx, w, d, start+len(args), f.OrderBy,
44✔
66
                        len(f.OrderBy.Expressions) > 0, " ", "")
44✔
67
                if err != nil {
44✔
68
                        return nil, err
×
69
                }
×
70
                args = append(args, orderArgs...)
44✔
71
        }
72
        w.WriteString(")")
44✔
73

44✔
74
        if f.WithinGroup {
44✔
75
                orderArgs, err := bob.ExpressIf(ctx, w, d, start+len(args), f.OrderBy,
×
76
                        len(f.OrderBy.Expressions) > 0, " WITHIN GROUP (", ")")
×
77
                if err != nil {
×
78
                        return nil, err
×
79
                }
×
80
                args = append(args, orderArgs...)
×
81
        }
82

83
        filterArgs, err := bob.ExpressSlice(ctx, w, d, start, f.Filter, " FILTER (WHERE ", " AND ", ")")
44✔
84
        if err != nil {
44✔
85
                return nil, err
×
86
        }
×
87
        args = append(args, filterArgs...)
44✔
88

44✔
89
        if len(f.Columns) > 0 || len(f.Alias) > 0 {
56✔
90
                w.WriteString(" AS ")
12✔
91
        }
12✔
92

93
        if len(f.Alias) > 0 {
44✔
NEW
94
                d.WriteQuoted(w, f.Alias)
×
95
                w.WriteString(" ")
×
96
        }
×
97

98
        colArgs, err := bob.ExpressSlice(ctx, w, d, start+len(args), f.Columns, "(", ", ", ")")
44✔
99
        if err != nil {
44✔
100
                return nil, err
×
101
        }
×
102
        args = append(args, colArgs...)
44✔
103

44✔
104
        winargs, err := bob.ExpressIf(ctx, w, d, start+len(args), f.w, f.w != nil, "OVER (", ")")
44✔
105
        if err != nil {
44✔
106
                return nil, err
×
107
        }
×
108
        args = append(args, winargs...)
44✔
109

44✔
110
        return append(nameArgs, args...), nil
44✔
111
}
112

113
type columnDef struct {
114
        name     string
115
        dataType string
116
}
117

118
func (c columnDef) WriteSQL(ctx context.Context, w io.StringWriter, d bob.Dialect, start int) ([]any, error) {
14✔
119
        d.WriteQuoted(w, c.name)
14✔
120
        w.WriteString(" ")
14✔
121
        w.WriteString(c.dataType)
14✔
122

14✔
123
        return nil, nil
14✔
124
}
14✔
125

126
// Functions renders ROWS FROM (f1, f2, ...) for multiple table functions in one
127
// from_item.
128
type Functions []*Function
129

130
// TableFunctions returns a FROM/USING expression for table functions: a single
131
// function_name(...) or ROWS FROM (...) when multiple are given.
132
func TableFunctions(funcs ...*Function) bob.Expression {
18✔
133
        switch len(funcs) {
18✔
134
        case 0:
×
135
                return nil
×
136
        case 1:
6✔
137
                return funcs[0]
6✔
138
        default:
12✔
139
                return Functions(funcs)
12✔
140
        }
141
}
142

143
func (f Functions) WriteSQL(ctx context.Context, w io.StringWriter, d bob.Dialect, start int) ([]any, error) {
12✔
144
        if len(f) > 1 {
24✔
145
                w.WriteString("ROWS FROM (")
12✔
146
        }
12✔
147

148
        args, err := bob.ExpressSlice(ctx, w, d, start, f, "", ", ", "")
12✔
149
        if err != nil {
12✔
150
                return nil, err
×
151
        }
×
152

153
        if len(f) > 1 {
24✔
154
                w.WriteString(")")
12✔
155
        }
12✔
156

157
        return args, nil
12✔
158
}
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