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

supabase / cli / 3572813994

04 Dec 2022 06:36PM UTC coverage: 54.923% (-2.8%) from 57.76%
3572813994

Pull #648

github

Kevin Saliou
chore: remove all tabs & trailing spaces from SQL files
Pull Request #648: chore: remove all tabs & trailing spaces from SQL files

3057 of 5566 relevant lines covered (54.92%)

498.14 hits per line

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

78.79
/internal/db/lint/lint.go
1
package lint
2

3
import (
4
        "context"
5
        _ "embed"
6
        "encoding/json"
7
        "fmt"
8
        "io"
9
        "os"
10
        "strings"
11

12
        "github.com/jackc/pgx/v4"
13
        "github.com/spf13/afero"
14
        "github.com/spf13/viper"
15
        "github.com/supabase/cli/internal/debug"
16
        "github.com/supabase/cli/internal/utils"
17
)
18

19
const ENABLE_PGSQL_CHECK = "CREATE EXTENSION IF NOT EXISTS plpgsql_check"
20

21
var (
22
        AllowedLevels = []string{
23
                "warning",
24
                "error",
25
        }
26
        //go:embed templates/check.sql
27
        checkSchemaScript string
28
)
29

30
type LintLevel int
31

32
func toEnum(level string) LintLevel {
10✔
33
        for i, curr := range AllowedLevels {
24✔
34
                if strings.HasPrefix(level, curr) {
24✔
35
                        return LintLevel(i)
10✔
36
                }
10✔
37
        }
38
        return -1
×
39
}
40

41
func Run(ctx context.Context, schema []string, level string, fsys afero.Fs, opts ...func(*pgx.ConnConfig)) error {
1✔
42
        // Sanity checks.
1✔
43
        if err := utils.LoadConfigFS(fsys); err != nil {
1✔
44
                return err
×
45
        }
×
46
        if err := utils.AssertSupabaseDbIsRunning(); err != nil {
1✔
47
                return err
×
48
        }
×
49
        // Run lint script
50
        conn, err := ConnectLocalPostgres(ctx, "localhost", utils.Config.Db.Port, "postgres", opts...)
1✔
51
        if err != nil {
1✔
52
                return err
×
53
        }
×
54
        defer conn.Close(context.Background())
1✔
55
        result, err := LintDatabase(ctx, conn, schema)
1✔
56
        if err != nil {
1✔
57
                return err
×
58
        }
×
59
        return printResultJSON(result, toEnum(level), os.Stdout)
1✔
60
}
61

62
func filterResult(result []Result, minLevel LintLevel) (filtered []Result) {
3✔
63
        for _, r := range result {
8✔
64
                out := Result{Function: r.Function}
5✔
65
                for _, issue := range r.Issues {
12✔
66
                        if toEnum(issue.Level) >= minLevel {
12✔
67
                                out.Issues = append(out.Issues, issue)
5✔
68
                        }
5✔
69
                }
70
                if len(out.Issues) > 0 {
9✔
71
                        filtered = append(filtered, out)
4✔
72
                }
4✔
73
        }
74
        return filtered
3✔
75
}
76

77
func printResultJSON(result []Result, minLevel LintLevel, stdout io.Writer) error {
3✔
78
        filtered := filterResult(result, minLevel)
3✔
79
        if len(filtered) == 0 {
3✔
80
                return nil
×
81
        }
×
82
        // Pretty print output
83
        enc := json.NewEncoder(stdout)
3✔
84
        enc.SetIndent("", "  ")
3✔
85
        return enc.Encode(filtered)
3✔
86
}
87

88
// Connnect to local Postgres with optimised settings. The caller is responsible for closing the connection returned.
89
func ConnectLocalPostgres(ctx context.Context, host string, port uint, database string, options ...func(*pgx.ConnConfig)) (*pgx.Conn, error) {
38✔
90
        url := fmt.Sprintf("postgresql://postgres:postgres@%s:%d/%s?connect_timeout=2", host, port, database)
38✔
91
        // Parse connection url
38✔
92
        config, err := pgx.ParseConfig(url)
38✔
93
        if err != nil {
44✔
94
                return nil, err
6✔
95
        }
6✔
96
        // Apply config overrides
97
        for _, op := range options {
62✔
98
                op(config)
30✔
99
        }
30✔
100
        if viper.GetBool("DEBUG") {
33✔
101
                debug.SetupPGX(config)
1✔
102
        }
1✔
103
        // Connect to database
104
        return pgx.ConnectConfig(ctx, config)
32✔
105
}
106

107
func LintDatabase(ctx context.Context, conn *pgx.Conn, schema []string) ([]Result, error) {
5✔
108
        tx, err := conn.Begin(ctx)
5✔
109
        if err != nil {
5✔
110
                return nil, err
×
111
        }
×
112
        // Always rollback since lint should not have side effects
113
        defer func() {
10✔
114
                if err := tx.Rollback(context.Background()); err != nil {
5✔
115
                        fmt.Fprintln(os.Stderr, err)
×
116
                }
×
117
        }()
118
        if _, err := conn.Exec(ctx, ENABLE_PGSQL_CHECK); err != nil {
6✔
119
                return nil, err
1✔
120
        }
1✔
121
        // Batch prepares statements
122
        batch := pgx.Batch{}
4✔
123
        for _, s := range schema {
9✔
124
                batch.Queue(checkSchemaScript, s)
5✔
125
        }
5✔
126
        br := conn.SendBatch(ctx, &batch)
4✔
127
        defer br.Close()
4✔
128
        var result []Result
4✔
129
        for _, s := range schema {
9✔
130
                fmt.Fprintln(os.Stderr, "Linting schema:", s)
5✔
131
                rows, err := br.Query()
5✔
132
                if err != nil {
5✔
133
                        return nil, err
×
134
                }
×
135
                // Parse result row
136
                for rows.Next() {
11✔
137
                        var name string
6✔
138
                        var data []byte
6✔
139
                        if err := rows.Scan(&name, &data); err != nil {
6✔
140
                                return nil, err
×
141
                        }
×
142
                        var r Result
6✔
143
                        if err := json.Unmarshal(data, &r); err != nil {
7✔
144
                                return nil, err
1✔
145
                        }
1✔
146
                        // Update function name
147
                        r.Function = s + "." + name
5✔
148
                        result = append(result, r)
5✔
149
                }
150
                err = rows.Err()
4✔
151
                if err != nil {
4✔
152
                        return nil, err
×
153
                }
×
154
        }
155
        return result, nil
3✔
156
}
157

158
type Query struct {
159
        Position string `json:"position"`
160
        Text     string `json:"text"`
161
}
162

163
type Statement struct {
164
        LineNumber string `json:"lineNumber"`
165
        Text       string `json:"text"`
166
}
167

168
type Issue struct {
169
        Level     string     `json:"level"`
170
        Message   string     `json:"message"`
171
        Statement *Statement `json:"statement,omitempty"`
172
        Query     *Query     `json:"query,omitempty"`
173
        Hint      string     `json:"hint,omitempty"`
174
        Detail    string     `json:"detail,omitempty"`
175
        Context   string     `json:"context,omitempty"`
176
        SQLState  string     `json:"sqlState,omitempty"`
177
}
178

179
type Result struct {
180
        Function string  `json:"function"`
181
        Issues   []Issue `json:"issues"`
182
}
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

© 2025 Coveralls, Inc