Sql builder generator for postgres database.

This commit is contained in:
sub0Zero 2019-03-03 17:54:43 +01:00 committed by zer0sub
parent 3190d6f933
commit 92edc96c9a
10 changed files with 403 additions and 9 deletions

163
generator/generator.go Normal file
View file

@ -0,0 +1,163 @@
package generator
import (
"database/sql"
_ "github.com/lib/pq"
"github.com/serenize/snaker"
"os"
)
type DbConnectInfo struct {
host string
port int
user string
password string
dbname string
}
func Generate(folderPath string, connectString string, databaseName, schemaName string) error {
if _, err := os.Stat(folderPath); os.IsNotExist(err) {
err := os.Mkdir(folderPath, os.ModePerm)
if err != nil {
return err
}
}
db, err := sql.Open("postgres", connectString)
if err != nil {
return err
}
defer db.Close()
err = db.Ping()
if err != nil {
return err
}
tables, err := getTablesInfo(db, schemaName)
if err != nil {
return err
}
for _, table := range tables {
err = generateSqlBuilderModel(databaseName, schemaName, table, folderPath)
if err != nil {
return err
}
}
return nil
}
type TableInfo struct {
Name string
Columns []ColumnInfo
}
func getTablesInfo(db *sql.DB, schemaName string) ([]TableInfo, error) {
tableNames, err := getListOfTables(db, schemaName)
if err != nil {
return nil, err
}
tables := []TableInfo{}
for _, tableName := range tableNames {
columns, err := getColumnInfos(db, tableName)
if err != nil {
return nil, err
}
tables = append(tables, TableInfo{tableName, columns})
}
return tables, nil
}
func getListOfTables(db *sql.DB, schemaName string) ([]string, error) {
rows, err := db.Query(`
SELECT table_name FROM information_schema.tables
where table_schema = $1 and table_type = 'BASE TABLE';`, schemaName)
if err != nil {
return nil, err
}
defer rows.Close()
tables := []string{}
for rows.Next() {
var table string
err = rows.Scan(&table)
if err != nil {
return nil, err
}
tables = append(tables, table)
}
err = rows.Err()
if err != nil {
return nil, err
}
return tables, nil
}
type ColumnInfo struct {
Name string
IsNullable bool
DataType string
}
func (c *ColumnInfo) CamelCaseName() string {
return snaker.SnakeToCamel(c.Name)
}
func getColumnInfos(db *sql.DB, tableName string) ([]ColumnInfo, error) {
query := `
SELECT column_name, is_nullable, data_type
FROM information_schema.columns
where table_name = $1
order by ordinal_position;`
//fmt.Println(query)
rows, err := db.Query(query, &tableName)
if err != nil {
return nil, err
}
defer rows.Close()
ret := []ColumnInfo{}
for rows.Next() {
columnInfo := ColumnInfo{}
var isNullable string
err := rows.Scan(&columnInfo.Name, &isNullable, &columnInfo.DataType)
columnInfo.IsNullable = isNullable == "YES"
if err != nil {
return nil, err
}
ret = append(ret, columnInfo)
}
err = rows.Err()
if err != nil {
return nil, err
}
return ret, nil
}

View file

@ -0,0 +1,91 @@
package generator
import (
"bytes"
"github.com/serenize/snaker"
"go/format"
"os"
"path/filepath"
"strings"
"text/template"
)
func generateSqlBuilderModel(databaseName, schemaName string, tableInfo TableInfo, dirPath string) error {
schemaDirPath := filepath.Join(dirPath, databaseName, schemaName, "table")
if _, err := os.Stat(schemaDirPath); os.IsNotExist(err) {
err := os.MkdirAll(schemaDirPath, os.ModePerm)
if err != nil {
return err
}
}
t, err := template.New("TableTemplate").Funcs(template.FuncMap{
"camelize": func(txt string) string {
return snaker.SnakeToCamel(txt)
},
"columnName": columnName,
}).Parse(TableTemplate)
if err != nil {
return err
}
newGoFilePath := filepath.Join(schemaDirPath, tableInfo.Name) + ".go"
file, err := os.Create(newGoFilePath)
if err != nil {
return err
}
defer file.Close()
tableTemplate := TableTemplateData{
databaseName,
tableInfo,
}
//err = t.Execute(file, &tableTemplate)
//
//if err != nil {
// return err
//}
var buf bytes.Buffer
if err := t.Execute(&buf, &tableTemplate); err != nil {
return err
}
p, err := format.Source(buf.Bytes())
if err != nil {
return err
}
_, err = file.Write(p)
if err != nil {
return err
}
return nil
}
type TableTemplateData struct {
PackageName string
TableInfo TableInfo
}
func columnName(table, column string) string {
return snaker.SnakeToCamelLower(table) + snaker.SnakeToCamel(column) + "Column"
}
func (t *TableTemplateData) ColumnNameList(sep string) string {
columnNames := []string{}
for _, columnInfo := range t.TableInfo.Columns {
columnInfoName := columnInfo.Name
columnNames = append(columnNames, columnName(t.TableInfo.Name, columnInfoName))
}
return strings.Join(columnNames, sep)
}

View file

@ -0,0 +1,21 @@
package generator
//import (
// "gotest.tools/assert"
// "testing"
//)
//
//func TestGenerateSqlBuilderModel(t *testing.T) {
// table := TableInfo{
// "actor",
// []ColumnInfo{
// {"actor_id", false, "integer"},
// {"first_name", true, "character varying"},
// {"last_name", false, "timestamp without time zone"},
// },
// }
//
// err := generateSqlBuilderModel("dvd_rental", table, "../../sqlbuildertest")
//
// assert.NilError(t, err)
//}

30
generator/templates.go Normal file
View file

@ -0,0 +1,30 @@
package generator
var TableTemplate = `package table
import "github.com/sub0Zero/go-sqlbuilder/sqlbuilder"
type {{camelize .TableInfo.Name}}Table struct {
sqlbuilder.Table
//Columns
{{- range .TableInfo.Columns}}
{{camelize .Name}} sqlbuilder.NonAliasColumn
{{- end}}
}
var {{camelize .TableInfo.Name}} = &{{camelize .TableInfo.Name}}Table{
Table: *sqlbuilder.NewTable("{{.TableInfo.Name}}", {{.ColumnNameList ", "}}),
//Columns
{{- range .TableInfo.Columns}}
{{camelize .Name}}: {{columnName $.TableInfo.Name .Name}},
{{- end}}
}
var (
{{- range .TableInfo.Columns}}
{{columnName $.TableInfo.Name .Name}} = sqlbuilder.IntColumn("{{.Name}}", {{if .IsNullable}}sqlbuilder.Nullable{{else}}sqlbuilder.NotNullable{{end}})
{{- end}}
)
`