From 043a0dc4c0fe999c981a2625342cab59be5b84e2 Mon Sep 17 00:00:00 2001 From: go-jet Date: Sat, 27 Jul 2019 10:40:30 +0200 Subject: [PATCH] MySQL generator support. --- cmd/jet/main.go | 67 +++++--- .../{postgresmeta => }/column_info.go | 53 ++++--- .../metadata/{postgresmeta => }/enum_info.go | 17 +-- .../metadata/postgresmeta/schema_info.go | 76 ---------- generator/internal/metadata/query.go | 143 ++++++++++++++++++ generator/internal/metadata/schema_info.go | 72 +++++++++ .../metadata/{postgresmeta => }/table_info.go | 20 +-- generator/internal/template/generate.go | 117 ++++++++++++++ .../template}/templates.go | 41 ++--- generator/mysql/mysql_generator.go | 65 ++++++++ generator/postgres/generator.go | 134 ---------------- generator/postgres/postgres_generator.go | 71 +++++++++ internal/utils/utils.go | 24 --- mysql/mysql_types.go | 38 +++++ postgres/postgres_types.go | 39 +++++ 15 files changed, 659 insertions(+), 318 deletions(-) rename generator/internal/metadata/{postgresmeta => }/column_info.go (66%) rename generator/internal/metadata/{postgresmeta => }/enum_info.go (60%) delete mode 100644 generator/internal/metadata/postgresmeta/schema_info.go create mode 100644 generator/internal/metadata/query.go create mode 100644 generator/internal/metadata/schema_info.go rename generator/internal/metadata/{postgresmeta => }/table_info.go (71%) create mode 100644 generator/internal/template/generate.go rename generator/{postgres => internal/template}/templates.go (88%) create mode 100644 generator/mysql/mysql_generator.go delete mode 100644 generator/postgres/generator.go create mode 100644 generator/postgres/postgres_generator.go create mode 100644 mysql/mysql_types.go create mode 100644 postgres/postgres_types.go diff --git a/cmd/jet/main.go b/cmd/jet/main.go index bedbe7d..8c06c49 100644 --- a/cmd/jet/main.go +++ b/cmd/jet/main.go @@ -3,12 +3,17 @@ package main import ( "flag" "fmt" + "github.com/go-jet/jet" + "github.com/go-jet/jet/generator/mysql" "github.com/go-jet/jet/generator/postgres" + _ "github.com/go-sql-driver/mysql" _ "github.com/lib/pq" "os" ) var ( + source string + host string port int user string @@ -22,6 +27,8 @@ var ( ) func init() { + flag.StringVar(&source, "source", jet.PostgreSQL, "Database name") + flag.StringVar(&host, "host", "", "Database host path (Example: localhost)") flag.IntVar(&port, "port", 0, "Database port") flag.StringVar(&user, "user", "", "Database user") @@ -62,26 +69,50 @@ Usage of jet: flag.Parse() - if host == "" || port == 0 || user == "" || dbName == "" || schemaName == "" { - fmt.Println("\njet: required flag missing") - flag.Usage() - os.Exit(-2) + var err error + + switch source { + case jet.PostgreSQL: + if host == "" || port == 0 || user == "" || dbName == "" || schemaName == "" { + fmt.Println("\njet: required flag missing") + flag.Usage() + os.Exit(-2) + } + + genData := postgres.DBConnection{ + Host: host, + Port: port, + User: user, + Password: password, + SslMode: sslmode, + Params: params, + + DBName: dbName, + SchemaName: schemaName, + } + + err = postgres.Generate(destDir, genData) + + case jet.MySql: + if host == "" || port == 0 || user == "" || dbName == "" { + fmt.Println("\njet: required flag missing") + flag.Usage() + os.Exit(-2) + } + + dbConn := mysql.DBConnection{ + Host: host, + Port: port, + User: user, + Password: password, + SslMode: sslmode, + Params: params, + DBName: dbName, + } + + err = mysql.Generate(destDir, dbConn) } - genData := postgres.DBConnection{ - Host: host, - Port: port, - User: user, - Password: password, - SslMode: sslmode, - Params: params, - - DBName: dbName, - SchemaName: schemaName, - } - - err := postgres.Generate(destDir, genData) - if err != nil { fmt.Println(err.Error()) os.Exit(-1) diff --git a/generator/internal/metadata/postgresmeta/column_info.go b/generator/internal/metadata/column_info.go similarity index 66% rename from generator/internal/metadata/postgresmeta/column_info.go rename to generator/internal/metadata/column_info.go index 2f842e7..b6a051f 100644 --- a/generator/internal/metadata/postgresmeta/column_info.go +++ b/generator/internal/metadata/column_info.go @@ -1,4 +1,4 @@ -package postgresmeta +package metadata import ( "database/sql" @@ -12,6 +12,7 @@ type ColumnInfo struct { Name string IsNullable bool DataType string + IsUnsigned bool EnumName string } @@ -20,11 +21,13 @@ func (c ColumnInfo) SqlBuilderColumnType() string { switch c.DataType { case "boolean": return "Bool" - case "smallint", "integer", "bigint": + case "smallint", "integer", "bigint", + "tinyint", "mediumint", "int", "year": //MySQL return "Integer" case "date": return "Date" - case "timestamp without time zone": + case "timestamp without time zone", + "timestamp": //MySQL: return "Timestamp" case "timestamp with time zone": return "Timestampz" @@ -32,10 +35,12 @@ func (c ColumnInfo) SqlBuilderColumnType() string { return "Time" case "time with time zone": return "Timez" - case "USER-DEFINED", "text", "character", "character varying", "bytea", "uuid", - "tsvector", "bit", "bit varying", "money", "json", "jsonb", "xml", "point", "interval", "line", "ARRAY": + case "USER-DEFINED", "enum", "text", "character", "character varying", "bytea", "uuid", + "tsvector", "bit", "bit varying", "money", "json", "jsonb", "xml", "point", "interval", "line", "ARRAY", + "char", "varchar", "binary", "varbinary", + "tinyblob", "blob", "mediumblob", "longblob", "tinytext", "mediumtext", "longtext": // MySQL return "String" - case "real", "numeric", "decimal", "double precision": + case "real", "numeric", "decimal", "double precision", "float": return "Float" default: fmt.Println("Unsupported sql type: " + c.DataType + ", using string column instead for sql builder.") @@ -46,22 +51,29 @@ func (c ColumnInfo) SqlBuilderColumnType() string { // GoBaseType returns model type for column info. func (c ColumnInfo) GoBaseType() string { switch c.DataType { - case "USER-DEFINED": + case "USER-DEFINED", "enum": return utils.ToGoIdentifier(c.EnumName) case "boolean": return "bool" - case "smallint": + case "tinyint": + return "int8" + case "smallint", + "year": return "int16" - case "integer": + case "integer", + "mediumint", "int": //MySQL return "int32" case "bigint": return "int64" - case "date", "timestamp without time zone", "timestamp with time zone", "time with time zone", "time without time zone": + case "date", "timestamp without time zone", "timestamp with time zone", "time with time zone", "time without time zone", + "timestamp": // MySQL return "time.Time" - case "bytea": + case "bytea", + "tinyblob", "blob", "mediumblob", "longblob": //MySQL return "[]byte" case "text", "character", "character varying", "tsvector", "bit", "bit varying", "money", "json", "jsonb", - "xml", "point", "interval", "line", "ARRAY": + "xml", "point", "interval", "line", "ARRAY", + "char", "varchar", "binary", "varbinary", "tinytext", "mediumtext", "longtext": // MySQL return "string" case "real": return "float32" @@ -79,6 +91,11 @@ func (c ColumnInfo) GoBaseType() string { // column can be NULL. func (c ColumnInfo) GoModelType() string { typeStr := c.GoBaseType() + + if strings.Contains(typeStr, "int") && c.IsUnsigned { + typeStr = "u" + typeStr + } + if c.IsNullable { return "*" + typeStr } @@ -101,15 +118,9 @@ func (c ColumnInfo) GoModelTag(isPrimaryKey bool) string { return "" } -func getColumnInfos(db *sql.DB, dbName, schemaName, tableName string) ([]ColumnInfo, error) { +func getColumnInfos(db *sql.DB, querySet MetaDataQuerySet, schemaName, tableName string) ([]ColumnInfo, error) { - query := ` -SELECT column_name, is_nullable, data_type, udt_name -FROM information_schema.columns -where table_catalog = $1 and table_schema = $2 and table_name = $3 -order by ordinal_position;` - - rows, err := db.Query(query, dbName, schemaName, tableName) + rows, err := db.Query(querySet.ListOfColumnsQuery(), schemaName, tableName) if err != nil { return nil, err @@ -121,7 +132,7 @@ order by ordinal_position;` for rows.Next() { columnInfo := ColumnInfo{} var isNullable string - err := rows.Scan(&columnInfo.Name, &isNullable, &columnInfo.DataType, &columnInfo.EnumName) + err := rows.Scan(&columnInfo.Name, &isNullable, &columnInfo.DataType, &columnInfo.EnumName, &columnInfo.IsUnsigned) columnInfo.IsNullable = isNullable == "YES" diff --git a/generator/internal/metadata/postgresmeta/enum_info.go b/generator/internal/metadata/enum_info.go similarity index 60% rename from generator/internal/metadata/postgresmeta/enum_info.go rename to generator/internal/metadata/enum_info.go index 3257129..21881be 100644 --- a/generator/internal/metadata/postgresmeta/enum_info.go +++ b/generator/internal/metadata/enum_info.go @@ -1,8 +1,7 @@ -package postgresmeta +package metadata import ( "database/sql" - "github.com/go-jet/jet/generator/internal/metadata" ) // EnumInfo struct @@ -16,17 +15,9 @@ func (e EnumInfo) Name() string { return e.name } -func getEnumInfos(db *sql.DB, schemaName string) ([]metadata.MetaData, error) { - query := ` -SELECT t.typname, - e.enumlabel -FROM pg_catalog.pg_type t - JOIN pg_catalog.pg_enum e on t.oid = e.enumtypid - JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace -WHERE n.nspname = $1 -ORDER BY n.nspname, t.typname, e.enumsortorder;` +func getEnumInfos(db *sql.DB, querySet MetaDataQuerySet, schemaName string) ([]MetaData, error) { - rows, err := db.Query(query, schemaName) + rows, err := db.Query(querySet.ListOfEnumsQuery(), schemaName) if err != nil { return nil, err @@ -55,7 +46,7 @@ ORDER BY n.nspname, t.typname, e.enumsortorder;` return nil, err } - ret := []metadata.MetaData{} + ret := []MetaData{} for enumName, enumValues := range enumsInfosMap { ret = append(ret, EnumInfo{ diff --git a/generator/internal/metadata/postgresmeta/schema_info.go b/generator/internal/metadata/postgresmeta/schema_info.go deleted file mode 100644 index 0b420c4..0000000 --- a/generator/internal/metadata/postgresmeta/schema_info.go +++ /dev/null @@ -1,76 +0,0 @@ -package postgresmeta - -import ( - "database/sql" - "github.com/go-jet/jet/generator/internal/metadata" -) - -// SchemaInfo metadata struct -type SchemaInfo struct { - DatabaseName string - Name string - TableInfos []metadata.MetaData - EnumInfos []metadata.MetaData -} - -// GetSchemaInfo returns schema information from db connection. -func GetSchemaInfo(db *sql.DB, databaseName, schemaName string) (schemaInfo SchemaInfo, err error) { - - schemaInfo.DatabaseName = databaseName - schemaInfo.Name = schemaName - - schemaInfo.TableInfos, err = getTableInfos(db, databaseName, schemaName) - - if err != nil { - return - } - - schemaInfo.EnumInfos, err = getEnumInfos(db, schemaName) - - if err != nil { - return - } - - return -} - -func getTableInfos(db *sql.DB, dbName, schemaName string) ([]metadata.MetaData, error) { - - query := ` -SELECT table_name -FROM information_schema.tables -where table_catalog = $1 and table_schema = $2 and table_type = 'BASE TABLE'; -` - - rows, err := db.Query(query, dbName, schemaName) - - if err != nil { - return nil, err - } - defer rows.Close() - - ret := []metadata.MetaData{} - for rows.Next() { - var tableName string - err = rows.Scan(&tableName) - if err != nil { - return nil, err - } - - tableInfo, err := GetTableInfo(db, dbName, schemaName, tableName) - - if err != nil { - return nil, err - } - - ret = append(ret, tableInfo) - } - - err = rows.Err() - - if err != nil { - return nil, err - } - - return ret, nil -} diff --git a/generator/internal/metadata/query.go b/generator/internal/metadata/query.go new file mode 100644 index 0000000..30021d3 --- /dev/null +++ b/generator/internal/metadata/query.go @@ -0,0 +1,143 @@ +package metadata + +import ( + "database/sql" + "fmt" + "strings" +) + +type MetaDataQuerySet interface { + ListOfTablesQuery() string + PrimaryKeysQuery() string + ListOfColumnsQuery() string + ListOfEnumsQuery() string + + GetEnumsMetaData(db *sql.DB, schemaName string) ([]MetaData, error) +} + +type PostgresQuerySet struct{} + +func (p *PostgresQuerySet) ListOfTablesQuery() string { + return ` +SELECT table_name +FROM information_schema.tables +where table_schema = $1 and table_type = 'BASE TABLE'; +` +} + +func (p *PostgresQuerySet) PrimaryKeysQuery() string { + return ` +SELECT c.column_name +FROM information_schema.key_column_usage AS c +LEFT JOIN information_schema.table_constraints AS t +ON t.constraint_name = c.constraint_name +WHERE t.table_schema = $1 AND t.table_name = $2 AND t.constraint_type = 'PRIMARY KEY'; +` +} + +func (p *PostgresQuerySet) ListOfColumnsQuery() string { + return ` +SELECT column_name, is_nullable, data_type, udt_name, FALSE +FROM information_schema.columns +where table_schema = $1 and table_name = $2 +order by ordinal_position;` +} + +func (p *PostgresQuerySet) ListOfEnumsQuery() string { + return ` +SELECT t.typname, + e.enumlabel +FROM pg_catalog.pg_type t + JOIN pg_catalog.pg_enum e on t.oid = e.enumtypid + JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace +WHERE n.nspname = $1 +ORDER BY n.nspname, t.typname, e.enumsortorder;` +} + +func (p *PostgresQuerySet) GetEnumsMetaData(db *sql.DB, schemaName string) ([]MetaData, error) { + return getEnumInfos(db, p, schemaName) +} + +// =======================================================================// + +type MySqlQuerySet struct{} + +func (m *MySqlQuerySet) ListOfTablesQuery() string { + return ` +SELECT table_name +FROM INFORMATION_SCHEMA.tables +WHERE table_schema = ? and table_type = 'BASE TABLE'; +` +} + +func (m *MySqlQuerySet) PrimaryKeysQuery() string { + return ` +SELECT k.column_name +FROM information_schema.table_constraints t +JOIN information_schema.key_column_usage k +USING(constraint_name,table_schema,table_name) +WHERE t.constraint_type='PRIMARY KEY' + AND t.table_schema= ? + AND t.table_name= ?; +` +} + +func (m *MySqlQuerySet) ListOfColumnsQuery() string { + return ` +SELECT COLUMN_NAME, + IS_NULLABLE, IF(COLUMN_TYPE = 'tinyint(1)', 'boolean', DATA_TYPE), + IF(DATA_TYPE = 'enum', CONCAT(TABLE_NAME, '_', COLUMN_NAME), ''), + COLUMN_TYPE LIKE '%unsigned%' +FROM information_schema.columns +WHERE table_schema = ? and table_name = ? +ORDER BY ordinal_position; +` +} + +func (m *MySqlQuerySet) ListOfEnumsQuery() string { + return ` +SELECT (CASE DATA_TYPE WHEN 'enum' then CONCAT(TABLE_NAME, '_', COLUMN_NAME) ELSE '' END ), SUBSTRING(COLUMN_TYPE,5) +FROM information_schema.columns +WHERE table_schema = ? +AND DATA_TYPE = 'enum'; +` +} + +func (m *MySqlQuerySet) GetEnumsMetaData(db *sql.DB, schemaName string) ([]MetaData, error) { + + rows, err := db.Query(m.ListOfEnumsQuery(), schemaName) + + if err != nil { + return nil, err + } + defer rows.Close() + + ret := []MetaData{} + + for rows.Next() { + var enumName string + var enumValues string + err = rows.Scan(&enumName, &enumValues) + if err != nil { + return nil, err + } + + fmt.Println(enumValues) + + enumValues = strings.Replace(enumValues[1:len(enumValues)-1], "'", "", -1) + + ret = append(ret, EnumInfo{ + name: enumName, + Values: strings.Split(enumValues, ","), + }) + } + + err = rows.Err() + + if err != nil { + return nil, err + } + + return ret, nil + +} diff --git a/generator/internal/metadata/schema_info.go b/generator/internal/metadata/schema_info.go new file mode 100644 index 0000000..24f7156 --- /dev/null +++ b/generator/internal/metadata/schema_info.go @@ -0,0 +1,72 @@ +package metadata + +import ( + "database/sql" + "fmt" +) + +// SchemaInfo metadata struct +type SchemaInfo struct { + TableInfos []MetaData + EnumInfos []MetaData +} + +// GetSchemaInfo returns schema information from db connection. +func GetSchemaInfo(db *sql.DB, schemaName string, querySet MetaDataQuerySet) (schemaInfo SchemaInfo, err error) { + + schemaInfo.TableInfos, err = getTableInfos(db, querySet, schemaName) + + if err != nil { + return + } + + schemaInfo.EnumInfos, err = querySet.GetEnumsMetaData(db, schemaName) + + if err != nil { + return + } + + fmt.Println(" FOUND", len(schemaInfo.TableInfos), "table(s), ", len(schemaInfo.EnumInfos), "enum(s)") + + return +} + +func getTableInfos(db *sql.DB, querySet MetaDataQuerySet, schemaName string) ([]MetaData, error) { + + fmt.Println(querySet.ListOfTablesQuery()) + + rows, err := db.Query(querySet.ListOfTablesQuery(), schemaName) + + if err != nil { + return nil, err + } + defer rows.Close() + + ret := []MetaData{} + for rows.Next() { + var tableName string + + err = rows.Scan(&tableName) + if err != nil { + return nil, err + } + + fmt.Println(tableName) + + tableInfo, err := GetTableInfo(db, querySet, schemaName, tableName) + + if err != nil { + return nil, err + } + + ret = append(ret, tableInfo) + } + + err = rows.Err() + + if err != nil { + return nil, err + } + + return ret, nil +} diff --git a/generator/internal/metadata/postgresmeta/table_info.go b/generator/internal/metadata/table_info.go similarity index 71% rename from generator/internal/metadata/postgresmeta/table_info.go rename to generator/internal/metadata/table_info.go index 5e35eb7..9d43d4a 100644 --- a/generator/internal/metadata/postgresmeta/table_info.go +++ b/generator/internal/metadata/table_info.go @@ -1,4 +1,4 @@ -package postgresmeta +package metadata import ( "database/sql" @@ -68,17 +68,17 @@ func (t TableInfo) GoStructName() string { } // GetTableInfo returns table info metadata -func GetTableInfo(db *sql.DB, dbName, schemaName, tableName string) (tableInfo TableInfo, err error) { +func GetTableInfo(db *sql.DB, querySet MetaDataQuerySet, schemaName, tableName string) (tableInfo TableInfo, err error) { tableInfo.SchemaName = schemaName tableInfo.name = tableName - tableInfo.PrimaryKeys, err = getPrimaryKeys(db, dbName, schemaName, tableName) + tableInfo.PrimaryKeys, err = getPrimaryKeys(db, querySet, schemaName, tableName) if err != nil { return } - tableInfo.Columns, err = getColumnInfos(db, dbName, schemaName, tableName) + tableInfo.Columns, err = getColumnInfos(db, querySet, schemaName, tableName) if err != nil { return @@ -87,15 +87,9 @@ func GetTableInfo(db *sql.DB, dbName, schemaName, tableName string) (tableInfo T return } -func getPrimaryKeys(db *sql.DB, dbName, schemaName, tableName string) (map[string]bool, error) { - query := ` -SELECT c.column_name -FROM information_schema.key_column_usage AS c -LEFT JOIN information_schema.table_constraints AS t -ON t.constraint_name = c.constraint_name -WHERE t.table_catalog = $1 AND t.table_schema = $2 AND t.table_name = $3 AND t.constraint_type = 'PRIMARY KEY'; -` - rows, err := db.Query(query, dbName, schemaName, tableName) +func getPrimaryKeys(db *sql.DB, querySet MetaDataQuerySet, schemaName, tableName string) (map[string]bool, error) { + + rows, err := db.Query(querySet.PrimaryKeysQuery(), schemaName, tableName) if err != nil { return nil, err diff --git a/generator/internal/template/generate.go b/generator/internal/template/generate.go new file mode 100644 index 0000000..628ea36 --- /dev/null +++ b/generator/internal/template/generate.go @@ -0,0 +1,117 @@ +package template + +import ( + "bytes" + "fmt" + "github.com/go-jet/jet/generator/internal/metadata" + "github.com/go-jet/jet/internal/utils" + "path/filepath" + "text/template" + "time" +) + +func GenerateFiles(destDir string, tables, enums []metadata.MetaData, dialect string) error { + if len(tables) == 0 && len(enums) == 0 { + return nil + } + + fmt.Println("Destination directory:", destDir) + fmt.Println("Cleaning up destination directory...") + err := utils.CleanUpGeneratedFiles(destDir) + + if err != nil { + return err + } + + fmt.Println("Generating table sql builder files...") + err = generate(destDir, "table", tableSQLBuilderTemplate, tables, dialect) + + if err != nil { + return err + } + + fmt.Println("Generating table model files...") + err = generate(destDir, "model", tableModelTemplate, tables, dialect) + + if err != nil { + return err + } + + if len(enums) > 0 { + fmt.Println("Generating enum sql builder files...") + err = generate(destDir, "enum", enumSQLBuilderTemplate, enums, dialect) + + if err != nil { + return err + } + + fmt.Println("Generating enum model files...") + err = generate(destDir, "model", enumModelTemplate, enums, dialect) + + if err != nil { + return err + } + } + + fmt.Println("Done") + + return nil + +} + +func generate(dirPath, packageName string, template string, metaDataList []metadata.MetaData, dialect string) error { + modelDirPath := filepath.Join(dirPath, packageName) + + err := utils.EnsureDirPath(modelDirPath) + + if err != nil { + return err + } + + autoGenWarning, err := GenerateTemplate(autoGenWarningTemplate, nil, dialect) + + if err != nil { + return err + } + + for _, metaData := range metaDataList { + text, err := GenerateTemplate(template, metaData, dialect) + + if err != nil { + return err + } + + err = utils.SaveGoFile(modelDirPath, utils.ToGoFileName(metaData.Name()), append(autoGenWarning, text...)) + + if err != nil { + return err + } + } + + return nil +} + +// GenerateTemplate generates template with template text and template data. +func GenerateTemplate(templateText string, templateData interface{}, dialect string) ([]byte, error) { + + t, err := template.New("sqlBuilderTableTemplate").Funcs(template.FuncMap{ + "ToGoIdentifier": utils.ToGoIdentifier, + "now": func() string { + return time.Now().Format(time.RFC850) + }, + "dialect": func() string { + return dialect + }, + }).Parse(templateText) + + if err != nil { + return nil, err + } + + var buf bytes.Buffer + if err := t.Execute(&buf, templateData); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} diff --git a/generator/postgres/templates.go b/generator/internal/template/templates.go similarity index 88% rename from generator/postgres/templates.go rename to generator/internal/template/templates.go index 87a8de5..c54c9b3 100644 --- a/generator/postgres/templates.go +++ b/generator/internal/template/templates.go @@ -1,4 +1,4 @@ -package postgres +package template var autoGenWarningTemplate = ` // @@ -11,7 +11,7 @@ var autoGenWarningTemplate = ` ` -var sqlBuilderTableTemplate = ` +var tableSQLBuilderTemplate = ` {{define "column-list" -}} {{- range $i, $c := . }} {{- if gt $i 0 }}, {{end}}{{ToGoIdentifier $c.Name}}Column @@ -22,6 +22,7 @@ package table import ( "github.com/go-jet/jet" + "github.com/go-jet/jet/{{dialect}}" ) var {{ToGoIdentifier .Name}} = new{{.GoStructName}}() @@ -31,7 +32,7 @@ type {{.GoStructName}} struct { //Columns {{- range .Columns}} - {{ToGoIdentifier .Name}} jet.Column{{.SqlBuilderColumnType}} + {{ToGoIdentifier .Name}} {{dialect}}.Column{{.SqlBuilderColumnType}} {{- end}} AllColumns jet.ColumnList @@ -50,7 +51,7 @@ func (a *{{.GoStructName}}) AS(alias string) *{{.GoStructName}} { func new{{.GoStructName}}() *{{.GoStructName}} { var ( {{- range .Columns}} - {{ToGoIdentifier .Name}}Column = jet.{{.SqlBuilderColumnType}}Column("{{.Name}}") + {{ToGoIdentifier .Name}}Column = {{dialect}}.{{.SqlBuilderColumnType}}Column("{{.Name}}") {{- end}} ) @@ -69,7 +70,7 @@ func new{{.GoStructName}}() *{{.GoStructName}} { ` -var dataModelTemplate = `package model +var tableModelTemplate = `package model {{ if .GetImports }} import ( @@ -85,6 +86,22 @@ type {{ToGoIdentifier .Name}} struct { {{ToGoIdentifier .Name}} {{.GoModelType}} ` + "{{.GoModelTag ($.IsPrimaryKey .Name)}}" + ` {{- end}} } + + +` +var enumSQLBuilderTemplate = `package enum + +import "github.com/go-jet/jet" + +var {{ToGoIdentifier $.Name}} = &struct { +{{- range $index, $element := .Values}} + {{ToGoIdentifier $element}} jet.StringExpression +{{- end}} +} { +{{- range $index, $element := .Values}} + {{ToGoIdentifier $element}}: jet.NewEnumValue("{{$element}}"), +{{- end}} +} ` var enumModelTemplate = `package model @@ -121,17 +138,3 @@ func (e {{ToGoIdentifier $.Name}}) String() string { } ` -var enumTypeTemplate = `package enum - -import "github.com/go-jet/jet" - -var {{ToGoIdentifier $.Name}} = &struct { -{{- range $index, $element := .Values}} - {{ToGoIdentifier $element}} jet.StringExpression -{{- end}} -} { -{{- range $index, $element := .Values}} - {{ToGoIdentifier $element}}: jet.NewEnumValue("{{$element}}"), -{{- end}} -} -` diff --git a/generator/mysql/mysql_generator.go b/generator/mysql/mysql_generator.go new file mode 100644 index 0000000..d3a8c65 --- /dev/null +++ b/generator/mysql/mysql_generator.go @@ -0,0 +1,65 @@ +package mysql + +import ( + "database/sql" + "fmt" + "github.com/go-jet/jet/generator/internal/metadata" + "github.com/go-jet/jet/generator/internal/template" + "path" +) + +type DBConnection struct { + Host string + Port int + User string + Password string + SslMode string + Params string + + DBName string +} + +// Generate generates jet files at destination dir from database connection details +func Generate(destDir string, dbConn DBConnection) error { + db, err := openConnection(dbConn) + if err != nil { + return err + } + defer db.Close() + + fmt.Println("Retrieving database information...") + // No schemas in MySQL + dbInfo, err := metadata.GetSchemaInfo(db, dbConn.DBName, &metadata.MySqlQuerySet{}) + + if err != nil { + return err + } + + genPath := path.Join(destDir, dbConn.DBName) + + err = template.GenerateFiles(genPath, dbInfo.TableInfos, dbInfo.EnumInfos, "mysql") + + if err != nil { + return err + } + + return nil +} + +// TODO reuse +func openConnection(dbConn DBConnection) (*sql.DB, error) { + var connString = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", dbConn.User, dbConn.Password, dbConn.Host, dbConn.Port, dbConn.DBName) + db, err := sql.Open("mysql", connString) + + if err != nil { + return nil, err + } + + err = db.Ping() + + if err != nil { + return nil, err + } + + return db, nil +} diff --git a/generator/postgres/generator.go b/generator/postgres/generator.go deleted file mode 100644 index 6c5b04b..0000000 --- a/generator/postgres/generator.go +++ /dev/null @@ -1,134 +0,0 @@ -package postgres - -import ( - "database/sql" - "fmt" - "github.com/go-jet/jet/generator/internal/metadata" - "github.com/go-jet/jet/generator/internal/metadata/postgresmeta" - "github.com/go-jet/jet/internal/utils" - "path" - "path/filepath" - "strconv" -) - -// DBConnection contains postgres connection details -type DBConnection struct { - Host string - Port int - User string - Password string - SslMode string - Params string - - DBName string - SchemaName string -} - -// Generate generates jet files at destination dir from database connection details -func Generate(destDir string, dbConn DBConnection) error { - - connectionString := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s %s", - dbConn.Host, strconv.Itoa(dbConn.Port), dbConn.User, dbConn.Password, dbConn.DBName, dbConn.SslMode, dbConn.Params) - - fmt.Println("Connecting to postgres database: " + connectionString) - - db, err := sql.Open("postgres", connectionString) - if err != nil { - return err - } - defer db.Close() - - err = db.Ping() - - if err != nil { - return err - } - - fmt.Println("Retrieving schema information...") - schemaInfo, err := postgresmeta.GetSchemaInfo(db, dbConn.DBName, dbConn.SchemaName) - - if err != nil { - return err - } - - fmt.Println(" FOUND", len(schemaInfo.TableInfos), "table(s), ", len(schemaInfo.EnumInfos), "enum(s)") - - if len(schemaInfo.TableInfos) == 0 && len(schemaInfo.EnumInfos) == 0 { - return nil - } - - schemaGenPath := path.Join(destDir, dbConn.DBName, dbConn.SchemaName) - fmt.Println("Destination directory:", schemaGenPath) - fmt.Println("Cleaning up destination directory...") - err = utils.CleanUpGeneratedFiles(schemaGenPath) - - if err != nil { - return err - } - - fmt.Println("Generating table sql builder files...") - err = generate(schemaInfo, destDir, "table", sqlBuilderTableTemplate, schemaInfo.TableInfos) - - if err != nil { - return err - } - - fmt.Println("Generating table model files...") - err = generate(schemaInfo, destDir, "model", dataModelTemplate, schemaInfo.TableInfos) - - if err != nil { - return err - } - - if len(schemaInfo.EnumInfos) > 0 { - fmt.Println("Generating enum sql builder files...") - err = generate(schemaInfo, destDir, "enum", enumTypeTemplate, schemaInfo.EnumInfos) - - if err != nil { - return err - } - - fmt.Println("Generating enum model files...") - err = generate(schemaInfo, destDir, "model", enumModelTemplate, schemaInfo.EnumInfos) - - if err != nil { - return err - } - } - - fmt.Println("Done") - - return nil -} - -func generate(schemaInfo postgresmeta.SchemaInfo, dirPath, packageName string, template string, metaDataList []metadata.MetaData) error { - modelDirPath := filepath.Join(dirPath, schemaInfo.DatabaseName, schemaInfo.Name, packageName) - - err := utils.EnsureDirPath(modelDirPath) - - if err != nil { - return err - } - - autoGenWarning, err := utils.GenerateTemplate(autoGenWarningTemplate, nil) - - if err != nil { - return err - } - - for _, metaData := range metaDataList { - text, err := utils.GenerateTemplate(template, metaData) - - if err != nil { - return err - } - - err = utils.SaveGoFile(modelDirPath, utils.ToGoFileName(metaData.Name()), append(autoGenWarning, text...)) - - if err != nil { - return err - } - } - - return nil -} diff --git a/generator/postgres/postgres_generator.go b/generator/postgres/postgres_generator.go new file mode 100644 index 0000000..18f3601 --- /dev/null +++ b/generator/postgres/postgres_generator.go @@ -0,0 +1,71 @@ +package postgres + +import ( + "database/sql" + "fmt" + "github.com/go-jet/jet/generator/internal/metadata" + "github.com/go-jet/jet/generator/internal/template" + "path" + "strconv" +) + +// DBConnection contains postgres connection details +type DBConnection struct { + Host string + Port int + User string + Password string + SslMode string + Params string + + DBName string + SchemaName string +} + +// Generate generates jet files at destination dir from database connection details +func Generate(destDir string, dbConn DBConnection) error { + + db, err := openConnection(dbConn) + defer db.Close() + + if err != nil { + return err + } + + fmt.Println("Retrieving schema information...") + schemaInfo, err := metadata.GetSchemaInfo(db, dbConn.SchemaName, &metadata.PostgresQuerySet{}) + + if err != nil { + return err + } + + genPath := path.Join(destDir, dbConn.DBName, dbConn.SchemaName) + + err = template.GenerateFiles(genPath, schemaInfo.TableInfos, schemaInfo.EnumInfos, "postgres") + + if err != nil { + return err + } + + return nil +} + +func openConnection(dbConn DBConnection) (*sql.DB, error) { + connectionString := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s %s", + dbConn.Host, strconv.Itoa(dbConn.Port), dbConn.User, dbConn.Password, dbConn.DBName, dbConn.SslMode, dbConn.Params) + + fmt.Println("Connecting to postgres database: " + connectionString) + + db, err := sql.Open("postgres", connectionString) + if err != nil { + return nil, err + } + + err = db.Ping() + + if err != nil { + return nil, err + } + + return db, nil +} diff --git a/internal/utils/utils.go b/internal/utils/utils.go index fab5baa..d56a153 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -1,7 +1,6 @@ package utils import ( - "bytes" "github.com/go-jet/jet/internal/3rdparty/snaker" "go/format" "os" @@ -9,7 +8,6 @@ import ( "reflect" "strconv" "strings" - "text/template" "time" ) @@ -62,28 +60,6 @@ func EnsureDirPath(dirPath string) error { return nil } -// GenerateTemplate generates template with template text and template data. -func GenerateTemplate(templateText string, templateData interface{}) ([]byte, error) { - - t, err := template.New("sqlBuilderTableTemplate").Funcs(template.FuncMap{ - "ToGoIdentifier": ToGoIdentifier, - "now": func() string { - return time.Now().Format(time.RFC850) - }, - }).Parse(templateText) - - if err != nil { - return nil, err - } - - var buf bytes.Buffer - if err := t.Execute(&buf, templateData); err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - // CleanUpGeneratedFiles deletes everything at folder dir. func CleanUpGeneratedFiles(dir string) error { exist, err := DirExists(dir) diff --git a/mysql/mysql_types.go b/mysql/mysql_types.go new file mode 100644 index 0000000..e74369c --- /dev/null +++ b/mysql/mysql_types.go @@ -0,0 +1,38 @@ +package mysql + +import "github.com/go-jet/jet" + +type ColumnBool jet.ColumnBool + +var BoolColumn = jet.BoolColumn +var Bool = jet.Bool + +type ColumnString jet.ColumnString + +var StringColumn = jet.StringColumn +var String = jet.String + +type ColumnInteger jet.ColumnInteger + +var IntegerColumn = jet.IntegerColumn +var Int = jet.Int + +type ColumnFloat jet.ColumnFloat + +var FloatColumn = jet.FloatColumn +var Float = jet.Float + +type ColumnDate jet.ColumnDate + +var DateColumn = jet.DateColumn +var Date = jet.Date + +type ColumnDateTime jet.ColumnTimestamp + +var DateTimeColumn = jet.TimestampColumn +var DateTime = jet.Timestamp + +type ColumnTimestamp jet.ColumnTimestamp + +var TimestampColumn = jet.TimestampColumn +var Timestamp = jet.Timestamp diff --git a/postgres/postgres_types.go b/postgres/postgres_types.go new file mode 100644 index 0000000..92d6456 --- /dev/null +++ b/postgres/postgres_types.go @@ -0,0 +1,39 @@ +package postgres + +import "github.com/go-jet/jet" + +type ColumnBool jet.ColumnBool + +var BoolColumn = jet.BoolColumn + +type ColumnString jet.ColumnString + +var StringColumn = jet.StringColumn + +type ColumnInteger jet.ColumnInteger + +var IntegerColumn = jet.IntegerColumn + +type ColumnFloat jet.ColumnFloat + +var FloatColumn = jet.FloatColumn + +type ColumnDate jet.ColumnDate + +var DateColumn = jet.DateColumn + +type ColumnTime jet.ColumnTime + +var TimeColumn = jet.TimeColumn + +type ColumnTimestamp jet.ColumnTimestamp + +var TimestampColumn = jet.TimestampColumn + +type ColumnTimez jet.ColumnTimez + +var TimezColumn = jet.TimezColumn + +type ColumnTimestampz jet.ColumnTimestampz + +var TimestampzColumn = jet.TimestampzColumn