Skip to content

Commit f5c1a5e

Browse files
authored
feat: Add the diff command (#1963)
1 parent 3623ec8 commit f5c1a5e

File tree

20 files changed

+571
-4
lines changed

20 files changed

+571
-4
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.19
55
require (
66
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220626175859-9abda183db8e
77
github.com/bytecodealliance/wasmtime-go v1.0.0
8+
github.com/cubicdaiya/gonp v1.0.4
89
github.com/davecgh/go-spew v1.1.1
910
github.com/go-sql-driver/mysql v1.6.0
1011
github.com/google/go-cmp v0.5.9

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
1313
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
1414
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
1515
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
16+
github.com/cubicdaiya/gonp v1.0.4 h1:ky2uIAJh81WiLcGKBVD5R7KsM/36W6IqqTy6Bo6rGws=
17+
github.com/cubicdaiya/gonp v1.0.4/go.mod h1:iWGuP/7+JVTn02OWhRemVbMmG1DOUnmrGTYYACpOI0I=
1618
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso=
1719
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
1820
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=

internal/cmd/cmd.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package cmd
22

33
import (
4+
"bufio"
5+
"bytes"
46
"context"
57
"fmt"
68
"io"
@@ -9,6 +11,7 @@ import (
911
"path/filepath"
1012
"runtime/trace"
1113

14+
"github.com/cubicdaiya/gonp"
1215
"github.com/spf13/cobra"
1316
"github.com/spf13/pflag"
1417
"gopkg.in/yaml.v3"
@@ -31,6 +34,7 @@ func Do(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) int
3134
rootCmd.PersistentFlags().BoolP("experimental", "x", false, "enable experimental features (default: false)")
3235

3336
rootCmd.AddCommand(checkCmd)
37+
rootCmd.AddCommand(diffCmd)
3438
rootCmd.AddCommand(genCmd)
3539
rootCmd.AddCommand(initCmd)
3640
rootCmd.AddCommand(versionCmd)
@@ -201,3 +205,48 @@ var checkCmd = &cobra.Command{
201205
return nil
202206
},
203207
}
208+
209+
func getLines(f []byte) []string {
210+
fp := bytes.NewReader(f)
211+
scanner := bufio.NewScanner(fp)
212+
lines := make([]string, 0)
213+
for scanner.Scan() {
214+
lines = append(lines, scanner.Text())
215+
}
216+
return lines
217+
}
218+
219+
func filterHunks[T gonp.Elem](uniHunks []gonp.UniHunk[T]) []gonp.UniHunk[T] {
220+
var out []gonp.UniHunk[T]
221+
for i, uniHunk := range uniHunks {
222+
var changed bool
223+
for _, e := range uniHunk.GetChanges() {
224+
switch e.GetType() {
225+
case gonp.SesDelete:
226+
changed = true
227+
case gonp.SesAdd:
228+
changed = true
229+
}
230+
}
231+
if changed {
232+
out = append(out, uniHunks[i])
233+
}
234+
}
235+
return out
236+
}
237+
238+
var diffCmd = &cobra.Command{
239+
Use: "diff",
240+
Short: "Compare the generated files to the existing files",
241+
RunE: func(cmd *cobra.Command, args []string) error {
242+
if debug.Traced {
243+
defer trace.StartRegion(cmd.Context(), "diff").End()
244+
}
245+
stderr := cmd.ErrOrStderr()
246+
dir, name := getConfigPath(stderr, cmd.Flag("file"))
247+
if err := Diff(cmd.Context(), ParseEnv(cmd), dir, name, stderr); err != nil {
248+
os.Exit(1)
249+
}
250+
return nil
251+
},
252+
}

internal/cmd/diff.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"io"
8+
"os"
9+
"runtime/trace"
10+
"sort"
11+
"strings"
12+
13+
"github.com/cubicdaiya/gonp"
14+
"github.com/kyleconroy/sqlc/internal/debug"
15+
)
16+
17+
func Diff(ctx context.Context, e Env, dir, name string, stderr io.Writer) error {
18+
output, err := Generate(ctx, e, dir, name, stderr)
19+
if err != nil {
20+
return err
21+
}
22+
if debug.Traced {
23+
defer trace.StartRegion(ctx, "checkfiles").End()
24+
}
25+
var errored bool
26+
27+
keys := make([]string, 0, len(output))
28+
for k, _ := range output {
29+
kk := k
30+
keys = append(keys, kk)
31+
}
32+
sort.Strings(keys)
33+
34+
for _, filename := range keys {
35+
source := output[filename]
36+
if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) {
37+
errored = true
38+
// stdout message
39+
continue
40+
}
41+
existing, err := os.ReadFile(filename)
42+
if err != nil {
43+
errored = true
44+
fmt.Fprintf(stderr, "%s: %s\n", filename, err)
45+
continue
46+
}
47+
diff := gonp.New(getLines(existing), getLines([]byte(source)))
48+
diff.Compose()
49+
uniHunks := filterHunks(diff.UnifiedHunks())
50+
51+
if len(uniHunks) > 0 {
52+
errored = true
53+
fmt.Fprintf(stderr, "--- a%s\n", strings.TrimPrefix(filename, dir))
54+
fmt.Fprintf(stderr, "+++ b%s\n", strings.TrimPrefix(filename, dir))
55+
diff.FprintUniHunks(stderr, uniHunks)
56+
}
57+
}
58+
if errored {
59+
return errors.New("diff found")
60+
}
61+
return nil
62+
}

internal/endtoend/endtoend_test.go

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"bytes"
55
"context"
6+
"encoding/json"
67
"os"
78
"path/filepath"
89
"strings"
@@ -92,14 +93,31 @@ func TestReplay(t *testing.T) {
9293
tc := replay
9394
t.Run(tc, func(t *testing.T) {
9495
t.Parallel()
95-
path, _ := filepath.Abs(tc)
96+
9697
var stderr bytes.Buffer
98+
var output map[string]string
99+
var err error
100+
101+
path, _ := filepath.Abs(tc)
102+
args := parseExec(t, path)
97103
expected := expectedStderr(t, path)
98-
output, err := cmd.Generate(ctx, cmd.Env{ExperimentalFeatures: true}, path, "", &stderr)
104+
105+
switch args.Command {
106+
case "diff":
107+
err = cmd.Diff(ctx, cmd.Env{ExperimentalFeatures: true}, path, "", &stderr)
108+
case "generate":
109+
output, err = cmd.Generate(ctx, cmd.Env{ExperimentalFeatures: true}, path, "", &stderr)
110+
if err == nil {
111+
cmpDirectory(t, path, output)
112+
}
113+
default:
114+
t.Fatalf("unknown command")
115+
}
116+
99117
if len(expected) == 0 && err != nil {
100-
t.Fatalf("sqlc generate failed: %s", stderr.String())
118+
t.Fatalf("sqlc %s failed: %s", args.Command, stderr.String())
101119
}
102-
cmpDirectory(t, path, output)
120+
103121
if diff := cmp.Diff(expected, stderr.String()); diff != "" {
104122
t.Errorf("stderr differed (-want +got):\n%s", diff)
105123
}
@@ -179,6 +197,29 @@ func expectedStderr(t *testing.T, dir string) string {
179197
return ""
180198
}
181199

200+
type exec struct {
201+
Command string `json:"command"`
202+
}
203+
204+
func parseExec(t *testing.T, dir string) exec {
205+
t.Helper()
206+
var e exec
207+
path := filepath.Join(dir, "exec.json")
208+
if _, err := os.Stat(path); !os.IsNotExist(err) {
209+
blob, err := os.ReadFile(path)
210+
if err != nil {
211+
t.Fatal(err)
212+
}
213+
if err := json.Unmarshal(blob, &e); err != nil {
214+
t.Fatal(err)
215+
}
216+
}
217+
if e.Command == "" {
218+
e.Command = "generate"
219+
}
220+
return e
221+
}
222+
182223
func BenchmarkReplay(b *testing.B) {
183224
ctx := context.Background()
184225
var dirs []string
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"command": "diff"
3+
}

internal/endtoend/testdata/diff_no_output/go/db.go

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/endtoend/testdata/diff_no_output/go/models.go

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/endtoend/testdata/diff_no_output/go/query.sql.go

Lines changed: 83 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-- name: GetAuthor :one
2+
SELECT * FROM authors
3+
WHERE id = $1 LIMIT 1;
4+
5+
-- name: ListAuthors :many
6+
SELECT * FROM authors
7+
ORDER BY bio;
8+
9+
-- name: SelectOne :one
10+
SELECT 1;
11+
12+
-- name: CreateAuthor :one
13+
INSERT INTO authors (
14+
name, bio
15+
) VALUES (
16+
$1, $2
17+
)
18+
RETURNING *;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CREATE TABLE authors (
2+
id BIGSERIAL PRIMARY KEY,
3+
name text NOT NULL,
4+
bio text
5+
);
6+
7+
CREATE TABLE books (
8+
id BIGSERIAL PRIMARY KEY,
9+
title text NOT NULL
10+
);
11+

0 commit comments

Comments
 (0)