Skip to content

Commit dc85f43

Browse files
authored
feat: Codegen plugins, powered by WASM (#1684)
Generate code via WASM-based plugins
1 parent 9cc7a6d commit dc85f43

File tree

18 files changed

+401
-74
lines changed

18 files changed

+401
-74
lines changed

cmd/sqlc-gen-json/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"bufio"
5+
"context"
56
"fmt"
67
"io"
78
"os"
@@ -26,7 +27,7 @@ func run() error {
2627
if err := req.UnmarshalVT(reqBlob); err != nil {
2728
return err
2829
}
29-
resp, err := json.Generate(&req)
30+
resp, err := json.Generate(context.Background(), &req)
3031
if err != nil {
3132
return err
3233
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ require (
2121
)
2222

2323
require (
24+
github.com/bytecodealliance/wasmtime-go v0.37.0 // indirect
2425
github.com/golang/protobuf v1.5.2 // indirect
2526
github.com/inconshreveable/mousetrap v1.0.0 // indirect
2627
github.com/jackc/chunkreader/v2 v2.0.1 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220209173558-ad29539cd2e9 h1:z
55
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220209173558-ad29539cd2e9/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
66
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
77
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
8+
github.com/bytecodealliance/wasmtime-go v0.37.0 h1:eNP2Snp5UFMuGuunRPxwVETJ/WpC8LhWonZAklXJfjk=
9+
github.com/bytecodealliance/wasmtime-go v0.37.0/go.mod h1:q320gUxqyI8yB+ZqRuaJOEnGkAnHh6WtJjMaT2CW4wI=
810
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
911
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
1012
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=

internal/cmd/generate.go

Lines changed: 68 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ import (
2020
"github.com/kyleconroy/sqlc/internal/debug"
2121
"github.com/kyleconroy/sqlc/internal/ext"
2222
"github.com/kyleconroy/sqlc/internal/ext/process"
23+
"github.com/kyleconroy/sqlc/internal/ext/wasm"
2324
"github.com/kyleconroy/sqlc/internal/multierr"
2425
"github.com/kyleconroy/sqlc/internal/opts"
26+
"github.com/kyleconroy/sqlc/internal/plugin"
2527
)
2628

2729
const errMessageNoVersion = `The configuration file must have a version number.
@@ -51,6 +53,15 @@ type outPair struct {
5153
config.SQL
5254
}
5355

56+
func findPlugin(conf config.Config, name string) (*config.Plugin, error) {
57+
for _, plug := range conf.Plugins {
58+
if plug.Name == name {
59+
return &plug, nil
60+
}
61+
}
62+
return nil, fmt.Errorf("plugin not found")
63+
}
64+
5465
func readConfig(stderr io.Writer, dir, filename string) (string, *config.Config, error) {
5566
configPath := ""
5667
if filename != "" {
@@ -216,43 +227,7 @@ func Generate(ctx context.Context, e Env, dir, filename string, stderr io.Writer
216227
break
217228
}
218229

219-
var region *trace.Region
220-
if debug.Traced {
221-
region = trace.StartRegion(ctx, "codegen")
222-
}
223-
var handler ext.Handler
224-
var out string
225-
switch {
226-
case sql.Gen.Go != nil:
227-
out = combo.Go.Out
228-
handler = ext.HandleFunc(golang.Generate)
229-
230-
case sql.Gen.Kotlin != nil:
231-
out = combo.Kotlin.Out
232-
handler = ext.HandleFunc(kotlin.Generate)
233-
234-
case sql.Gen.Python != nil:
235-
out = combo.Python.Out
236-
handler = ext.HandleFunc(python.Generate)
237-
238-
case sql.Gen.JSON != nil:
239-
out = combo.JSON.Out
240-
handler = ext.HandleFunc(json.Generate)
241-
242-
case sql.Plugin != nil:
243-
out = sql.Plugin.Out
244-
handler = &process.Runner{
245-
Config: combo.Global,
246-
Plugin: sql.Plugin.Plugin,
247-
}
248-
249-
default:
250-
panic("missing language backend")
251-
}
252-
resp, err := handler.Generate(codeGenRequest(result, combo))
253-
if region != nil {
254-
region.End()
255-
}
230+
out, resp, err := codegen(ctx, combo, sql, result)
256231
if err != nil {
257232
fmt.Fprintf(stderr, "# package %s\n", name)
258233
fmt.Fprintf(stderr, "error generating code: %s\n", err)
@@ -262,6 +237,7 @@ func Generate(ctx context.Context, e Env, dir, filename string, stderr io.Writer
262237
}
263238
continue
264239
}
240+
265241
files := map[string]string{}
266242
for _, file := range resp.Files {
267243
files[file.Name] = string(file.Contents)
@@ -313,3 +289,58 @@ func parse(ctx context.Context, e Env, name, dir string, sql config.SQL, combo c
313289
}
314290
return c.Result(), false
315291
}
292+
293+
func codegen(ctx context.Context, combo config.CombinedSettings, sql outPair, result *compiler.Result) (string, *plugin.CodeGenResponse, error) {
294+
var region *trace.Region
295+
if debug.Traced {
296+
region = trace.StartRegion(ctx, "codegen")
297+
}
298+
var handler ext.Handler
299+
var out string
300+
switch {
301+
case sql.Gen.Go != nil:
302+
out = combo.Go.Out
303+
handler = ext.HandleFunc(golang.Generate)
304+
305+
case sql.Gen.Kotlin != nil:
306+
out = combo.Kotlin.Out
307+
handler = ext.HandleFunc(kotlin.Generate)
308+
309+
case sql.Gen.Python != nil:
310+
out = combo.Python.Out
311+
handler = ext.HandleFunc(python.Generate)
312+
313+
case sql.Gen.JSON != nil:
314+
out = combo.JSON.Out
315+
handler = ext.HandleFunc(json.Generate)
316+
317+
case sql.Plugin != nil:
318+
out = sql.Plugin.Out
319+
plug, err := findPlugin(combo.Global, sql.Plugin.Plugin)
320+
if err != nil {
321+
return "", nil, fmt.Errorf("plugin not found: %s", err)
322+
}
323+
324+
switch {
325+
case plug.Process != nil:
326+
handler = &process.Runner{
327+
Cmd: plug.Process.Cmd,
328+
}
329+
case plug.WASM != nil:
330+
handler = &wasm.Runner{
331+
URL: plug.WASM.URL,
332+
Checksum: plug.WASM.Checksum,
333+
}
334+
default:
335+
return "", nil, fmt.Errorf("unsupported plugin type")
336+
}
337+
338+
default:
339+
return "", nil, fmt.Errorf("missing language backend")
340+
}
341+
resp, err := handler.Generate(ctx, codeGenRequest(result, combo))
342+
if region != nil {
343+
region.End()
344+
}
345+
return out, resp, err
346+
}

internal/codegen/golang/gen.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package golang
33
import (
44
"bufio"
55
"bytes"
6+
"context"
67
"errors"
78
"fmt"
89
"go/format"
@@ -42,7 +43,7 @@ func (t *tmplCtx) OutputQuery(sourceName string) bool {
4243
return t.SourceName == sourceName
4344
}
4445

45-
func Generate(req *plugin.CodeGenRequest) (*plugin.CodeGenResponse, error) {
46+
func Generate(ctx context.Context, req *plugin.CodeGenRequest) (*plugin.CodeGenResponse, error) {
4647
enums := buildEnums(req)
4748
structs := buildStructs(req)
4849
queries, err := buildQueries(req, structs)

internal/codegen/json/gen.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package json
22

33
import (
44
"bytes"
5+
"context"
56
ejson "encoding/json"
67
"fmt"
78

@@ -31,7 +32,7 @@ func parseOptions(req *plugin.CodeGenRequest) (plugin.JSONCode, error) {
3132
return plugin.JSONCode{}, nil
3233
}
3334

34-
func Generate(req *plugin.CodeGenRequest) (*plugin.CodeGenResponse, error) {
35+
func Generate(ctx context.Context, req *plugin.CodeGenRequest) (*plugin.CodeGenResponse, error) {
3536
options, err := parseOptions(req)
3637
if err != nil {
3738
return nil, err

internal/codegen/kotlin/gen.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package kotlin
33
import (
44
"bufio"
55
"bytes"
6+
"context"
67
"errors"
78
"fmt"
89
"regexp"
@@ -759,7 +760,7 @@ func ktFormat(s string) string {
759760
return o
760761
}
761762

762-
func Generate(req *plugin.CodeGenRequest) (*plugin.CodeGenResponse, error) {
763+
func Generate(ctx context.Context, req *plugin.CodeGenRequest) (*plugin.CodeGenResponse, error) {
763764
enums := buildEnums(req)
764765
structs := buildDataClasses(req)
765766
queries, err := buildQueries(req, structs)

internal/codegen/python/gen.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package python
22

33
import (
4+
"context"
45
"errors"
56
"fmt"
67
"log"
@@ -1080,7 +1081,7 @@ func HashComment(s string) string {
10801081
return "# " + strings.ReplaceAll(s, "\n", "\n# ")
10811082
}
10821083

1083-
func Generate(req *plugin.CodeGenRequest) (*plugin.CodeGenResponse, error) {
1084+
func Generate(_ context.Context, req *plugin.CodeGenRequest) (*plugin.CodeGenResponse, error) {
10841085
enums := buildEnums(req)
10851086
models := buildModels(req)
10861087
queries, err := buildQueries(req, models)

internal/config/config.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ type Plugin struct {
8888
Cmd string `json:"cmd" yaml:"cmd"`
8989
} `json:"process" yaml:"process"`
9090
WASM *struct {
91-
URL string `json:"url" yaml:"url"`
91+
URL string `json:"url" yaml:"url"`
92+
Checksum string `json:"checksum" yaml:"checksum"`
9293
} `json:"wasm" yaml:"wasm"`
9394
}
9495

internal/endtoend/endtoend_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,11 @@ func cmpDirectory(t *testing.T, dir string, actual map[string]string) {
116116
if file.IsDir() {
117117
return nil
118118
}
119-
if !strings.HasSuffix(path, ".go") && !strings.HasSuffix(path, ".kt") && !strings.HasSuffix(path, ".py") && !strings.HasSuffix(path, ".json") {
119+
if !strings.HasSuffix(path, ".go") && !strings.HasSuffix(path, ".kt") && !strings.HasSuffix(path, ".py") && !strings.HasSuffix(path, ".json") && !strings.HasSuffix(path, ".txt") {
120+
return nil
121+
}
122+
// TODO: Figure out a better way to ignore certain files
123+
if strings.HasSuffix(path, ".txt") && filepath.Base(path) != "hello.txt" {
120124
return nil
121125
}
122126
if filepath.Base(path) == "sqlc.json" {

0 commit comments

Comments
 (0)