Skip to content

Commit e2440d6

Browse files
committed
It works!
1 parent ce71699 commit e2440d6

File tree

13 files changed

+1040
-369
lines changed

13 files changed

+1040
-369
lines changed

cmd/sqlc-gen-json/main.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package main
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"io"
7+
"os"
8+
9+
"github.com/kyleconroy/sqlc/internal/codegen/json"
10+
"github.com/kyleconroy/sqlc/internal/plugin"
11+
)
12+
13+
func main() {
14+
if err := run(); err != nil {
15+
fmt.Fprintf(os.Stderr, "error generating JSON: %s", err)
16+
os.Exit(2)
17+
}
18+
}
19+
20+
func run() error {
21+
var req plugin.CodeGenRequest
22+
reqBlob, err := io.ReadAll(os.Stdin)
23+
if err != nil {
24+
return err
25+
}
26+
if err := req.UnmarshalVT(reqBlob); err != nil {
27+
return err
28+
}
29+
resp, err := json.Generate(&req)
30+
if err != nil {
31+
return err
32+
}
33+
respBlob, err := resp.MarshalVT()
34+
if err != nil {
35+
return err
36+
}
37+
w := bufio.NewWriter(os.Stdout)
38+
if _, err := w.Write(respBlob); err != nil {
39+
return err
40+
}
41+
if err := w.Flush(); err != nil {
42+
return err
43+
}
44+
return nil
45+
}

internal/cmd/generate.go

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/kyleconroy/sqlc/internal/codegen/golang"
1515
"github.com/kyleconroy/sqlc/internal/codegen/json"
1616
"github.com/kyleconroy/sqlc/internal/codegen/kotlin"
17+
"github.com/kyleconroy/sqlc/internal/codegen/process"
1718
"github.com/kyleconroy/sqlc/internal/codegen/python"
1819
"github.com/kyleconroy/sqlc/internal/compiler"
1920
"github.com/kyleconroy/sqlc/internal/config"
@@ -44,7 +45,9 @@ func printFileErr(stderr io.Writer, dir string, fileErr *multierr.FileError) {
4445
}
4546

4647
type outPair struct {
47-
Gen config.SQLGen
48+
Gen config.SQLGen
49+
Plugin *config.Codegen
50+
4851
config.SQL
4952
}
5053

@@ -145,10 +148,19 @@ func Generate(ctx context.Context, e Env, dir, filename string, stderr io.Writer
145148
Gen: config.SQLGen{JSON: sql.Gen.JSON},
146149
})
147150
}
151+
for i, _ := range sql.Codegen {
152+
pairs = append(pairs, outPair{
153+
SQL: sql,
154+
Plugin: &sql.Codegen[i],
155+
})
156+
}
148157
}
149158

150159
for _, sql := range pairs {
151160
combo := config.Combine(*conf, sql.SQL)
161+
if sql.Plugin != nil {
162+
combo.Codegen = *sql.Plugin
163+
}
152164

153165
// TODO: This feels like a hack that will bite us later
154166
joined := make([]string, 0, len(sql.Schema))
@@ -167,24 +179,32 @@ func Generate(ctx context.Context, e Env, dir, filename string, stderr io.Writer
167179
parseOpts := opts.Parser{
168180
Debug: debug.Debug,
169181
}
170-
if sql.Gen.Go != nil {
182+
183+
switch {
184+
case sql.Gen.Go != nil:
171185
name = combo.Go.Package
172186
lang = "golang"
173-
} else if sql.Gen.Kotlin != nil {
187+
188+
case sql.Gen.Kotlin != nil:
174189
if sql.Engine == config.EnginePostgreSQL {
175190
parseOpts.UsePositionalParameters = true
176191
}
177192
lang = "kotlin"
178193
name = combo.Kotlin.Package
179-
} else if sql.Gen.Python != nil {
194+
195+
case sql.Gen.Python != nil:
180196
lang = "python"
181197
name = combo.Python.Package
198+
199+
case sql.Plugin != nil:
200+
lang = fmt.Sprintf("process:%s", sql.Plugin.Plugin)
201+
name = sql.Plugin.Plugin
182202
}
183203

184204
var packageRegion *trace.Region
185205
if debug.Traced {
186206
packageRegion = trace.StartRegion(ctx, "package")
187-
trace.Logf(ctx, "", "name=%s dir=%s language=%s", name, dir, lang)
207+
trace.Logf(ctx, "", "name=%s dir=%s plugin=%s", name, dir, lang)
188208
}
189209

190210
result, failed := parse(ctx, e, name, dir, sql.SQL, combo, parseOpts, stderr)
@@ -206,15 +226,27 @@ func Generate(ctx context.Context, e Env, dir, filename string, stderr io.Writer
206226
case sql.Gen.Go != nil:
207227
out = combo.Go.Out
208228
genfunc = golang.Generate
229+
209230
case sql.Gen.Kotlin != nil:
210231
out = combo.Kotlin.Out
211232
genfunc = kotlin.Generate
233+
212234
case sql.Gen.Python != nil:
213235
out = combo.Python.Out
214236
genfunc = python.Generate
237+
215238
case sql.Gen.JSON != nil:
216239
out = combo.JSON.Out
217240
genfunc = json.Generate
241+
242+
case sql.Plugin != nil:
243+
out = sql.Plugin.Out
244+
runner := process.Runner{
245+
Config: combo.Global,
246+
Plugin: sql.Plugin.Plugin,
247+
}
248+
genfunc = runner.Generate
249+
218250
default:
219251
panic("missing language backend")
220252
}

internal/cmd/shim.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/kyleconroy/sqlc/internal/compiler"
77
"github.com/kyleconroy/sqlc/internal/config"
8+
"github.com/kyleconroy/sqlc/internal/config/convert"
89
"github.com/kyleconroy/sqlc/internal/info"
910
"github.com/kyleconroy/sqlc/internal/plugin"
1011
"github.com/kyleconroy/sqlc/internal/sql/catalog"
@@ -56,13 +57,26 @@ func pluginSettings(cs config.CombinedSettings) *plugin.Settings {
5657
Queries: []string(cs.Package.Queries),
5758
Overrides: over,
5859
Rename: cs.Rename,
60+
Codegen: pluginCodegen(cs.Codegen),
5961
Python: pluginPythonCode(cs.Python),
6062
Kotlin: pluginKotlinCode(cs.Kotlin),
6163
Go: pluginGoCode(cs.Go),
6264
Json: pluginJSONCode(cs.JSON),
6365
}
6466
}
6567

68+
func pluginCodegen(s config.Codegen) *plugin.Codegen {
69+
opts, err := convert.YAMLtoJSON(s.Options)
70+
if err != nil {
71+
panic(err)
72+
}
73+
return &plugin.Codegen{
74+
Out: s.Out,
75+
Plugin: s.Plugin,
76+
Options: opts,
77+
}
78+
}
79+
6680
func pluginPythonCode(s config.SQLPython) *plugin.PythonCode {
6781
return &plugin.PythonCode{
6882
Out: s.Out,
@@ -127,8 +141,9 @@ func pluginKotlinCode(s config.SQLKotlin) *plugin.KotlinCode {
127141

128142
func pluginJSONCode(s config.SQLJSON) *plugin.JSONCode {
129143
return &plugin.JSONCode{
130-
Out: s.Out,
131-
Indent: s.Indent,
144+
Out: s.Out,
145+
Indent: s.Indent,
146+
Filename: s.Filename,
132147
}
133148
}
134149

internal/codegen/json/gen.go

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,52 @@
11
package json
22

33
import (
4+
"bytes"
45
ejson "encoding/json"
6+
"fmt"
57

68
"google.golang.org/protobuf/encoding/protojson"
79

810
"github.com/kyleconroy/sqlc/internal/plugin"
911
)
1012

13+
func parseOptions(req *plugin.CodeGenRequest) (plugin.JSONCode, error) {
14+
if req.Settings == nil {
15+
return plugin.JSONCode{}, nil
16+
}
17+
if req.Settings.Codegen != nil {
18+
if len(req.Settings.Codegen.Options) != 0 {
19+
var options plugin.JSONCode
20+
dec := ejson.NewDecoder(bytes.NewReader(req.Settings.Codegen.Options))
21+
dec.DisallowUnknownFields()
22+
if err := dec.Decode(&options); err != nil {
23+
return options, fmt.Errorf("unmarshalling options: %s", err)
24+
}
25+
return options, nil
26+
}
27+
}
28+
if req.Settings.Json != nil {
29+
return *req.Settings.Json, nil
30+
}
31+
return plugin.JSONCode{}, nil
32+
}
33+
1134
func Generate(req *plugin.CodeGenRequest) (*plugin.CodeGenResponse, error) {
12-
indent := ""
13-
if req.Settings != nil && req.Settings.Json != nil {
14-
indent = req.Settings.Json.Indent
35+
options, err := parseOptions(req)
36+
if err != nil {
37+
return nil, err
38+
}
39+
40+
indent := " "
41+
if options.Indent != "" {
42+
indent = options.Indent
1543
}
44+
45+
filename := "codegen_request.json"
46+
if options.Filename != "" {
47+
filename = options.Filename
48+
}
49+
1650
// The output of protojson has randomized whitespace
1751
// https://github.com/golang/protobuf/issues/1082
1852
m := &protojson.MarshalOptions{
@@ -32,7 +66,7 @@ func Generate(req *plugin.CodeGenRequest) (*plugin.CodeGenResponse, error) {
3266
return &plugin.CodeGenResponse{
3367
Files: []*plugin.File{
3468
{
35-
Name: "codegen_request.json",
69+
Name: filename,
3670
Contents: append(blob, '\n'),
3771
},
3872
},

internal/codegen/process/gen.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package process
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"errors"
7+
"fmt"
8+
"os/exec"
9+
10+
"google.golang.org/protobuf/proto"
11+
12+
"github.com/kyleconroy/sqlc/internal/config"
13+
"github.com/kyleconroy/sqlc/internal/plugin"
14+
)
15+
16+
type Runner struct {
17+
Config config.Config
18+
Plugin string
19+
}
20+
21+
func (r Runner) pluginCmd() (string, error) {
22+
for _, plug := range r.Config.Plugins {
23+
if plug.Name != r.Plugin {
24+
continue
25+
}
26+
if plug.Process == nil {
27+
continue
28+
}
29+
return plug.Process.Cmd, nil
30+
}
31+
return "", fmt.Errorf("plugin not found")
32+
}
33+
34+
// TODO: Update the gen func signature to take a ctx
35+
func (r Runner) Generate(req *plugin.CodeGenRequest) (*plugin.CodeGenResponse, error) {
36+
stdin, err := proto.Marshal(req)
37+
if err != nil {
38+
return nil, fmt.Errorf("failed to encode codegen request: %s", err)
39+
}
40+
41+
name, err := r.pluginCmd()
42+
if err != nil {
43+
return nil, fmt.Errorf("process: unknown plugin %s", r.Plugin)
44+
}
45+
46+
// Check if the output plugin exists
47+
path, err := exec.LookPath(name)
48+
if err != nil {
49+
return nil, fmt.Errorf("process: %s not found", name)
50+
}
51+
52+
ctx := context.Background()
53+
cmd := exec.CommandContext(ctx, path)
54+
cmd.Stdin = bytes.NewReader(stdin)
55+
cmd.Env = []string{
56+
"SQLC_VERSION=foo",
57+
}
58+
59+
out, err := cmd.Output()
60+
if err != nil {
61+
stderr := err.Error()
62+
var exit *exec.ExitError
63+
if errors.As(err, &exit) {
64+
stderr = string(exit.Stderr)
65+
}
66+
return nil, fmt.Errorf("process: error running command %s", stderr)
67+
}
68+
69+
var resp plugin.CodeGenResponse
70+
if err := proto.Unmarshal(out, &resp); err != nil {
71+
return nil, fmt.Errorf("process: failed to read codegen resp: %s", err)
72+
}
73+
74+
return &resp, nil
75+
}

internal/config/config.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import (
88
"io"
99

1010
yaml "gopkg.in/yaml.v3"
11-
12-
"github.com/kyleconroy/sqlc/internal/config/convert"
1311
)
1412

1513
const errMessageNoVersion = `The configuration file must have a version number.
@@ -119,6 +117,7 @@ type SQL struct {
119117
Codegen []Codegen `json:"codegen" yaml:"codegen"`
120118
}
121119

120+
// TODO: Figure out a better name for this
122121
type Codegen struct {
123122
Out string `json:"out" yaml:"out"`
124123
Plugin string `json:"plugin" yaml:"plugin"`
@@ -172,8 +171,9 @@ type SQLPython struct {
172171
}
173172

174173
type SQLJSON struct {
175-
Out string `json:"out" yaml:"out"`
176-
Indent string `json:"indent,omitempty" yaml:"indent"`
174+
Out string `json:"out" yaml:"out"`
175+
Indent string `json:"indent,omitempty" yaml:"indent"`
176+
Filename string `json:"filename,omitempty" yaml:"filename"`
177177
}
178178

179179
var ErrMissingEngine = errors.New("unknown engine")
@@ -189,6 +189,7 @@ var ErrUnknownVersion = errors.New("invalid version number")
189189
var ErrPluginBuiltin = errors.New("a built-in plugin with that name already exists")
190190
var ErrPluginNoName = errors.New("missing plugin name")
191191
var ErrPluginExists = errors.New("a plugin with that name already exists")
192+
var ErrPluginNotFound = errors.New("no plugin found")
192193
var ErrPluginNoType = errors.New("plugin: field `process` or `wasm` required")
193194
var ErrPluginBothTypes = errors.New("plugin: both `process` and `wasm` cannot both be defined")
194195
var ErrPluginProcessNoCmd = errors.New("plugin: missing process command")
@@ -238,18 +239,16 @@ type CombinedSettings struct {
238239
JSON SQLJSON
239240
Rename map[string]string
240241
Overrides []Override
242+
243+
// TODO: Combine these into a more usable type
244+
Codegen Codegen
241245
}
242246

243247
func Combine(conf Config, pkg SQL) CombinedSettings {
244248
cs := CombinedSettings{
245249
Global: conf,
246250
Package: pkg,
247251
}
248-
249-
for i, cg := range pkg.Codegen {
250-
fmt.Printf("%d: %s\n", i, convert.YAMLtoJSON(cg.Options))
251-
}
252-
253252
if conf.Gen.Go != nil {
254253
cs.Rename = conf.Gen.Go.Rename
255254
cs.Overrides = append(cs.Overrides, conf.Gen.Go.Overrides...)

0 commit comments

Comments
 (0)