Skip to content

feat: Process-based codegen plugins #1578

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ jobs:
with:
go-version: '1.18'

- name: install ./...
run: go install ./...

- name: test ./...
run: go test --tags=examples ./...
env:
Expand Down
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
build:
go build ./...

install:
go install ./...

test:
go test ./...

Expand Down
45 changes: 45 additions & 0 deletions cmd/sqlc-gen-json/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package main

import (
"bufio"
"fmt"
"io"
"os"

"github.com/kyleconroy/sqlc/internal/codegen/json"
"github.com/kyleconroy/sqlc/internal/plugin"
)

func main() {
if err := run(); err != nil {
fmt.Fprintf(os.Stderr, "error generating JSON: %s", err)
os.Exit(2)
}
}

func run() error {
var req plugin.CodeGenRequest
reqBlob, err := io.ReadAll(os.Stdin)
if err != nil {
return err
}
if err := req.UnmarshalVT(reqBlob); err != nil {
return err
}
resp, err := json.Generate(&req)
if err != nil {
return err
}
respBlob, err := resp.MarshalVT()
if err != nil {
return err
}
w := bufio.NewWriter(os.Stdout)
if _, err := w.Write(respBlob); err != nil {
return err
}
if err := w.Flush(); err != nil {
return err
}
return nil
}
55 changes: 43 additions & 12 deletions internal/cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ import (
"github.com/kyleconroy/sqlc/internal/compiler"
"github.com/kyleconroy/sqlc/internal/config"
"github.com/kyleconroy/sqlc/internal/debug"
"github.com/kyleconroy/sqlc/internal/ext"
"github.com/kyleconroy/sqlc/internal/ext/process"
"github.com/kyleconroy/sqlc/internal/multierr"
"github.com/kyleconroy/sqlc/internal/opts"
"github.com/kyleconroy/sqlc/internal/plugin"
)

const errMessageNoVersion = `The configuration file must have a version number.
Expand All @@ -44,7 +45,9 @@ func printFileErr(stderr io.Writer, dir string, fileErr *multierr.FileError) {
}

type outPair struct {
Gen config.SQLGen
Gen config.SQLGen
Plugin *config.Codegen

config.SQL
}

Expand Down Expand Up @@ -145,10 +148,19 @@ func Generate(ctx context.Context, e Env, dir, filename string, stderr io.Writer
Gen: config.SQLGen{JSON: sql.Gen.JSON},
})
}
for i, _ := range sql.Codegen {
pairs = append(pairs, outPair{
SQL: sql,
Plugin: &sql.Codegen[i],
})
}
}

for _, sql := range pairs {
combo := config.Combine(*conf, sql.SQL)
if sql.Plugin != nil {
combo.Codegen = *sql.Plugin
}

// TODO: This feels like a hack that will bite us later
joined := make([]string, 0, len(sql.Schema))
Expand All @@ -167,24 +179,32 @@ func Generate(ctx context.Context, e Env, dir, filename string, stderr io.Writer
parseOpts := opts.Parser{
Debug: debug.Debug,
}
if sql.Gen.Go != nil {

switch {
case sql.Gen.Go != nil:
name = combo.Go.Package
lang = "golang"
} else if sql.Gen.Kotlin != nil {

case sql.Gen.Kotlin != nil:
if sql.Engine == config.EnginePostgreSQL {
parseOpts.UsePositionalParameters = true
}
lang = "kotlin"
name = combo.Kotlin.Package
} else if sql.Gen.Python != nil {

case sql.Gen.Python != nil:
lang = "python"
name = combo.Python.Package

case sql.Plugin != nil:
lang = fmt.Sprintf("process:%s", sql.Plugin.Plugin)
name = sql.Plugin.Plugin
}

var packageRegion *trace.Region
if debug.Traced {
packageRegion = trace.StartRegion(ctx, "package")
trace.Logf(ctx, "", "name=%s dir=%s language=%s", name, dir, lang)
trace.Logf(ctx, "", "name=%s dir=%s plugin=%s", name, dir, lang)
}

result, failed := parse(ctx, e, name, dir, sql.SQL, combo, parseOpts, stderr)
Expand All @@ -200,25 +220,36 @@ func Generate(ctx context.Context, e Env, dir, filename string, stderr io.Writer
if debug.Traced {
region = trace.StartRegion(ctx, "codegen")
}
var genfunc func(req *plugin.CodeGenRequest) (*plugin.CodeGenResponse, error)
var handler ext.Handler
var out string
switch {
case sql.Gen.Go != nil:
out = combo.Go.Out
genfunc = golang.Generate
handler = ext.HandleFunc(golang.Generate)

case sql.Gen.Kotlin != nil:
out = combo.Kotlin.Out
genfunc = kotlin.Generate
handler = ext.HandleFunc(kotlin.Generate)

case sql.Gen.Python != nil:
out = combo.Python.Out
genfunc = python.Generate
handler = ext.HandleFunc(python.Generate)

case sql.Gen.JSON != nil:
out = combo.JSON.Out
genfunc = json.Generate
handler = ext.HandleFunc(json.Generate)

case sql.Plugin != nil:
out = sql.Plugin.Out
handler = &process.Runner{
Config: combo.Global,
Plugin: sql.Plugin.Plugin,
}

default:
panic("missing language backend")
}
resp, err := genfunc(codeGenRequest(result, combo))
resp, err := handler.Generate(codeGenRequest(result, combo))
if region != nil {
region.End()
}
Expand Down
19 changes: 17 additions & 2 deletions internal/cmd/shim.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/kyleconroy/sqlc/internal/compiler"
"github.com/kyleconroy/sqlc/internal/config"
"github.com/kyleconroy/sqlc/internal/config/convert"
"github.com/kyleconroy/sqlc/internal/info"
"github.com/kyleconroy/sqlc/internal/plugin"
"github.com/kyleconroy/sqlc/internal/sql/catalog"
Expand Down Expand Up @@ -56,13 +57,26 @@ func pluginSettings(cs config.CombinedSettings) *plugin.Settings {
Queries: []string(cs.Package.Queries),
Overrides: over,
Rename: cs.Rename,
Codegen: pluginCodegen(cs.Codegen),
Python: pluginPythonCode(cs.Python),
Kotlin: pluginKotlinCode(cs.Kotlin),
Go: pluginGoCode(cs.Go),
Json: pluginJSONCode(cs.JSON),
}
}

func pluginCodegen(s config.Codegen) *plugin.Codegen {
opts, err := convert.YAMLtoJSON(s.Options)
if err != nil {
panic(err)
}
return &plugin.Codegen{
Out: s.Out,
Plugin: s.Plugin,
Options: opts,
}
}

func pluginPythonCode(s config.SQLPython) *plugin.PythonCode {
return &plugin.PythonCode{
Out: s.Out,
Expand Down Expand Up @@ -130,8 +144,9 @@ func pluginKotlinCode(s config.SQLKotlin) *plugin.KotlinCode {

func pluginJSONCode(s config.SQLJSON) *plugin.JSONCode {
return &plugin.JSONCode{
Out: s.Out,
Indent: s.Indent,
Out: s.Out,
Indent: s.Indent,
Filename: s.Filename,
}
}

Expand Down
42 changes: 38 additions & 4 deletions internal/codegen/json/gen.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,52 @@
package json

import (
"bytes"
ejson "encoding/json"
"fmt"

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

"github.com/kyleconroy/sqlc/internal/plugin"
)

func parseOptions(req *plugin.CodeGenRequest) (plugin.JSONCode, error) {
if req.Settings == nil {
return plugin.JSONCode{}, nil
}
if req.Settings.Codegen != nil {
if len(req.Settings.Codegen.Options) != 0 {
var options plugin.JSONCode
dec := ejson.NewDecoder(bytes.NewReader(req.Settings.Codegen.Options))
dec.DisallowUnknownFields()
if err := dec.Decode(&options); err != nil {
return options, fmt.Errorf("unmarshalling options: %s", err)
}
return options, nil
}
}
if req.Settings.Json != nil {
return *req.Settings.Json, nil
}
return plugin.JSONCode{}, nil
}

func Generate(req *plugin.CodeGenRequest) (*plugin.CodeGenResponse, error) {
indent := ""
if req.Settings != nil && req.Settings.Json != nil {
indent = req.Settings.Json.Indent
options, err := parseOptions(req)
if err != nil {
return nil, err
}

indent := " "
if options.Indent != "" {
indent = options.Indent
}

filename := "codegen_request.json"
if options.Filename != "" {
filename = options.Filename
}

// The output of protojson has randomized whitespace
// https://github.com/golang/protobuf/issues/1082
m := &protojson.MarshalOptions{
Expand All @@ -32,7 +66,7 @@ func Generate(req *plugin.CodeGenRequest) (*plugin.CodeGenResponse, error) {
return &plugin.CodeGenResponse{
Files: []*plugin.File{
{
Name: "codegen_request.json",
Name: filename,
Contents: append(blob, '\n'),
},
},
Expand Down
Loading