Skip to content
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

Define code generation interface via Protocol Buffers #1406

Merged
merged 11 commits into from Feb 6, 2022
3 changes: 3 additions & 0 deletions Makefile
Expand Up @@ -21,6 +21,9 @@ sqlc-pg-gen:
start:
docker-compose up -d

fmt:
go fmt ./...

psql:
PGPASSWORD=mysecretpassword psql --host=127.0.0.1 --port=5432 --username=postgres dinotest

Expand Down
1 change: 1 addition & 0 deletions examples/authors/mysql/db_test.go
@@ -1,3 +1,4 @@
//go:build examples
// +build examples

package authors
Expand Down
1 change: 1 addition & 0 deletions examples/authors/postgresql/db_test.go
@@ -1,3 +1,4 @@
//go:build examples
// +build examples

package authors
Expand Down
1 change: 1 addition & 0 deletions examples/booktest/postgresql/db_test.go
@@ -1,3 +1,4 @@
//go:build examples
// +build examples

package booktest
Expand Down
1 change: 1 addition & 0 deletions examples/ondeck/mysql/db_test.go
@@ -1,3 +1,4 @@
//go:build examples
// +build examples

package ondeck
Expand Down
1 change: 1 addition & 0 deletions examples/ondeck/postgresql/db_test.go
@@ -1,3 +1,4 @@
//go:build examples
// +build examples

package ondeck
Expand Down
11 changes: 10 additions & 1 deletion internal/cmd/generate.go
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/kyleconroy/sqlc/internal/debug"
"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 Down Expand Up @@ -187,6 +188,7 @@ func Generate(ctx context.Context, e Env, dir, filename string, stderr io.Writer
region = trace.StartRegion(ctx, "codegen")
}
var files map[string]string
var resp *plugin.CodeGenResponse
var out string
switch {
case sql.Gen.Go != nil:
Expand All @@ -197,14 +199,21 @@ func Generate(ctx context.Context, e Env, dir, filename string, stderr io.Writer
files, err = kotlin.Generate(result, combo)
case sql.Gen.Python != nil:
out = combo.Python.Out
files, err = python.Generate(result, combo)
resp, err = python.Generate(codeGenRequest(result, combo))
default:
panic("missing language backend")
}
if region != nil {
region.End()
}

if resp != nil {
files = map[string]string{}
for _, file := range resp.Files {
files[file.Name] = string(file.Contents)
}
}

if err != nil {
fmt.Fprintf(stderr, "# package %s\n", name)
fmt.Fprintf(stderr, "error generating code: %s\n", err)
Expand Down
217 changes: 217 additions & 0 deletions internal/cmd/shim.go
@@ -0,0 +1,217 @@
package cmd

import (
"strings"

"github.com/kyleconroy/sqlc/internal/compiler"
"github.com/kyleconroy/sqlc/internal/config"
"github.com/kyleconroy/sqlc/internal/plugin"
"github.com/kyleconroy/sqlc/internal/sql/catalog"
)

func pluginOverride(o config.Override) *plugin.Override {
var column string
var table plugin.Identifier

if o.Column != "" {
colParts := strings.Split(o.Column, ".")
switch len(colParts) {
case 2:
table.Schema = "public"
table.Name = colParts[0]
column = colParts[1]
case 3:
table.Schema = colParts[0]
table.Name = colParts[1]
column = colParts[2]
case 4:
table.Catalog = colParts[0]
table.Schema = colParts[1]
table.Name = colParts[2]
column = colParts[3]
}
}
return &plugin.Override{
CodeType: "", // FIXME
DbType: o.DBType,
Nullable: o.Nullable,
Column: o.Column,
ColumnName: column,
Table: &table,
PythonType: pluginPythonType(o.PythonType),
}
}

func pluginSettings(cs config.CombinedSettings) *plugin.Settings {
var over []*plugin.Override
for _, o := range cs.Overrides {
over = append(over, pluginOverride(o))
}
return &plugin.Settings{
Version: cs.Global.Version,
Engine: string(cs.Package.Engine),
Schema: []string(cs.Package.Schema),
Queries: []string(cs.Package.Queries),
Overrides: over,
Rename: cs.Rename,
Python: pluginPythonCode(cs.Python),
}
}

func pluginPythonCode(s config.SQLPython) *plugin.PythonCode {
return &plugin.PythonCode{
Out: s.Out,
Package: s.Package,
EmitExactTableNames: s.EmitExactTableNames,
EmitSyncQuerier: s.EmitSyncQuerier,
EmitAsyncQuerier: s.EmitAsyncQuerier,
}
}

func pluginPythonType(pt config.PythonType) *plugin.PythonType {
return &plugin.PythonType{
Module: pt.Module,
Name: pt.Name,
}
}

func pluginCatalog(c *catalog.Catalog) *plugin.Catalog {
var schemas []*plugin.Schema
for _, s := range c.Schemas {
var enums []*plugin.Enum
for _, typ := range s.Types {
enum, ok := typ.(*catalog.Enum)
if !ok {
continue
}
enums = append(enums, &plugin.Enum{
Name: enum.Name,
Comment: enum.Comment,
Vals: enum.Vals,
})
}
var tables []*plugin.Table
for _, t := range s.Tables {
var columns []*plugin.Column
for _, c := range t.Columns {
l := -1
if c.Length != nil {
l = *c.Length
}
columns = append(columns, &plugin.Column{
Name: c.Name,
Type: &plugin.Identifier{
Catalog: c.Type.Catalog,
Schema: c.Type.Schema,
Name: c.Type.Name,
},
Comment: c.Comment,
NotNull: c.IsNotNull,
IsArray: c.IsArray,
Length: int32(l),
Table: &plugin.Identifier{
Catalog: t.Rel.Catalog,
Schema: t.Rel.Schema,
Name: t.Rel.Name,
},
})
}
tables = append(tables, &plugin.Table{
Rel: &plugin.Identifier{
Catalog: t.Rel.Catalog,
Schema: t.Rel.Schema,
Name: t.Rel.Name,
},
Columns: columns,
Comment: t.Comment,
})
}
schemas = append(schemas, &plugin.Schema{
Comment: s.Comment,
Name: s.Name,
Tables: tables,
Enums: enums,
})
}
return &plugin.Catalog{
Name: c.Name,
DefaultSchema: c.DefaultSchema,
Comment: c.Comment,
Schemas: schemas,
}
}

func pluginQueries(r *compiler.Result) []*plugin.Query {
var out []*plugin.Query
for _, q := range r.Queries {
var params []*plugin.Parameter
var columns []*plugin.Column
for _, c := range q.Columns {
columns = append(columns, pluginQueryColumn(c))
}
for _, p := range q.Params {
params = append(params, pluginQueryParam(p))
}
out = append(out, &plugin.Query{
Name: q.Name,
Cmd: q.Cmd,
Text: q.SQL,
Comments: q.Comments,
Columns: columns,
Params: params,
Filename: q.Filename,
})
}
return out
}

func pluginQueryColumn(c *compiler.Column) *plugin.Column {
l := -1
if c.Length != nil {
l = *c.Length
}
out := &plugin.Column{
Name: c.Name,
Comment: c.Comment,
NotNull: c.NotNull,
IsArray: c.IsArray,
Length: int32(l),
}

if c.Type != nil {
out.Type = &plugin.Identifier{
Catalog: c.Type.Catalog,
Schema: c.Type.Schema,
Name: c.Type.Name,
}
} else {
out.Type = &plugin.Identifier{
Name: c.DataType,
}
}

if c.Table != nil {
out.Table = &plugin.Identifier{
Catalog: c.Table.Catalog,
Schema: c.Table.Schema,
Name: c.Table.Name,
}
}

return out
}

func pluginQueryParam(p compiler.Parameter) *plugin.Parameter {
return &plugin.Parameter{
Number: int32(p.Number),
Column: pluginQueryColumn(p.Column),
}
}

func codeGenRequest(r *compiler.Result, settings config.CombinedSettings) *plugin.CodeGenRequest {
return &plugin.CodeGenRequest{
Settings: pluginSettings(settings),
Catalog: pluginCatalog(r.Catalog),
Queries: pluginQueries(r),
}
}