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

stephenafamo / bob / 26506256479

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

Pull #697

github

Roman A. Grigorovich
refactor: improve SQL formatter error handling and clean up code
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