blob: aa6e19750b985eae38695264e9221f1242b0b40f [file] [log] [blame]
// This executable generates a go file that contains the SQL schema for Gold defined as a string.
// By doing this, we have the source of truth as a documented go struct, which can be used in a
// more flexible way than having the SQL as the source of truth.
package main
import (
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"strings"
"go.skia.org/infra/go/sklog"
"go.skia.org/infra/golden/go/sql/schema"
)
func main() {
outputFile := flag.String("output_file", "", "The name of the file to write to.")
outputPkg := flag.String("output_pkg", "", "The name of the package to output to.")
flag.Parse()
cwd, err := os.Getwd()
if err != nil {
sklog.Fatalf("Could not get working dir")
}
generatedText := generateSQL(schema.Tables{}, *outputPkg)
out := filepath.Join(cwd, *outputFile)
err = ioutil.WriteFile(out, []byte(generatedText), 0666)
if err != nil {
sklog.Fatalf("Could not write SQL to %s: %s", out, err)
}
sklog.Infof("Tables schema written to %s.\n", out)
}
// generateSQL takes in a "table type", that is a table whose fields are slices. Each field
// will be interpreted as a table. The sql struct tags will be used to generate the SQL schema.
// A package name is taken in to be included in the returned string. If a malformed type is passed
// in, this function will panic.
func generateSQL(inputType interface{}, pkg string) string {
header := fmt.Sprintf("package %s\n\n// Generated by //golden/go/sql/exporter/tosql\n// DO NOT EDIT\n\nconst Schema = `", pkg)
body := strings.Builder{}
t := reflect.TypeOf(inputType)
for i := 0; i < t.NumField(); i++ {
table := t.Field(i) // Fields of the outer type are expected to be tables.
if table.Type.Kind() != reflect.Slice {
panic(`Expected table should be a slice: ` + table.Name)
}
body.WriteString("CREATE TABLE IF NOT EXISTS ")
body.WriteString(table.Name)
body.WriteString(" (")
row := table.Type.Elem()
wasFirst := true
for j := 0; j < row.NumField(); j++ {
col := row.Field(j)
sqlText, ok := col.Tag.Lookup("sql")
if !ok {
panic(`Field missing "sql" tag:` + table.Name + "." + row.Name())
}
if !wasFirst {
body.WriteString(",")
}
wasFirst = false
body.WriteString("\n ")
body.WriteString(strings.TrimSpace(sqlText))
}
body.WriteString("\n);\n")
}
const footer = "`\n"
return header + body.String() + footer
}