Connection via DSN

This commit is contained in:
vetcher 2021-08-30 15:09:09 +03:00 committed by go-jet
parent b0838999d6
commit 5b7c34e333
8 changed files with 220 additions and 54 deletions

View file

@ -3,19 +3,22 @@ package main
import (
"flag"
"fmt"
"os"
"regexp"
"strings"
mysqlgen "github.com/go-jet/jet/v2/generator/mysql"
postgresgen "github.com/go-jet/jet/v2/generator/postgres"
"github.com/go-jet/jet/v2/mysql"
"github.com/go-jet/jet/v2/postgres"
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
"os"
"strings"
)
var (
source string
dsn string
host string
port int
user string
@ -31,6 +34,7 @@ var (
func init() {
flag.StringVar(&source, "source", "", "Database system name (PostgreSQL, MySQL or MariaDB)")
flag.StringVar(&dsn, "dsn", "", "Data source name connection string (Example: postgresql://user@localhost:5432/otherdb?sslmode=trust)")
flag.StringVar(&host, "host", "", "Database host path (Example: localhost)")
flag.IntVar(&port, "port", 0, "Database port")
flag.StringVar(&user, "user", "", "Database user")
@ -50,6 +54,12 @@ func main() {
Jet generator 2.5.0
Usage:
-dsn string
Data source name. Unified format for connecting to database.
PostgreSQL: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
Example: postgresql://user:pass@localhost:5432/dbname
MySQL: https://dev.mysql.com/doc/refman/8.0/en/connecting-using-uri-or-key-value-pairs.html
Example: mysql://jet:jet@tcp(localhost:3306)/dvds
-source string
Database system name (PostgreSQL, MySQL or MariaDB)
-host string
@ -75,15 +85,32 @@ Usage:
flag.Parse()
if dsn == "" {
// validations for separated connection flags.
if source == "" || host == "" || port == 0 || user == "" || dbName == "" {
printErrorAndExit("\nERROR: required flag(s) missing")
}
} else {
if source == "" {
// try to get source from schema
source = detectSchema(dsn)
}
// validations when dsn != ""
if source == "" {
printErrorAndExit("\nERROR: required -source flag missing.")
}
}
var err error
switch strings.ToLower(strings.TrimSpace(source)) {
case strings.ToLower(postgres.Dialect.Name()),
strings.ToLower(postgres.Dialect.PackageName()):
if dsn != "" {
err = postgresgen.GenerateDSN(dsn, schemaName, destDir)
break
}
genData := postgresgen.DBConnection{
Host: host,
Port: port,
@ -98,8 +125,19 @@ Usage:
err = postgresgen.Generate(destDir, genData)
case strings.ToLower(mysql.Dialect.Name()), "mariadb":
case strings.ToLower(mysql.Dialect.Name()), "mysqlx", "mariadb":
if dsn != "" {
// Special case for go mysql driver. It does not understand schema,
// so we need to trim it before passing to generator
// https://github.com/go-sql-driver/mysql#dsn-data-source-name
idx := strings.Index(dsn, "://")
if idx != -1 {
dsn = dsn[idx+len("://"):]
}
err = mysqlgen.GenerateDSN(dsn, destDir)
break
}
dbConn := mysqlgen.DBConnection{
Host: host,
Port: port,
@ -126,3 +164,12 @@ func printErrorAndExit(error string) {
flag.Usage()
os.Exit(-2)
}
func detectSchema(dsn string) (source string) {
schemeRe := regexp.MustCompile(`^(.+)://.*`)
match := schemeRe.FindStringSubmatch(dsn)
if len(match) < 2 { // not found
return ""
}
return match[1]
}

View file

@ -3,11 +3,13 @@ package mysql
import (
"database/sql"
"fmt"
"github.com/go-jet/jet/v2/generator/metadata"
"github.com/go-jet/jet/v2/generator/template"
"github.com/go-jet/jet/v2/internal/utils"
"github.com/go-jet/jet/v2/internal/utils/throw"
"github.com/go-jet/jet/v2/mysql"
mysqldr "github.com/go-sql-driver/mysql"
)
// DBConnection contains MySQL connection details
@ -25,28 +27,38 @@ type DBConnection struct {
func Generate(destDir string, dbConn DBConnection, generatorTemplate ...template.Template) (err error) {
defer utils.ErrorCatch(&err)
db := openConnection(dbConn)
defer utils.DBClose(db)
fmt.Println("Retrieving database information...")
// No schemas in MySQL
schemaMetaData := metadata.GetSchema(db, &mySqlQuerySet{}, dbConn.DBName)
genTemplate := template.Default(mysql.Dialect)
if len(generatorTemplate) > 0 {
genTemplate = generatorTemplate[0]
connectionString := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", dbConn.User, dbConn.Password, dbConn.Host, dbConn.Port, dbConn.DBName)
if dbConn.Params != "" {
connectionString += "?" + dbConn.Params
}
template.ProcessSchema(destDir, schemaMetaData, genTemplate)
db := openConnection(connectionString)
defer utils.DBClose(db)
generate(db, dbConn.DBName, destDir, generatorTemplate...)
return nil
}
func openConnection(dbConn DBConnection) *sql.DB {
var connectionString = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", dbConn.User, dbConn.Password, dbConn.Host, dbConn.Port, dbConn.DBName)
if dbConn.Params != "" {
connectionString += "?" + dbConn.Params
// GenerateDSN opens connection via DSN string and does everything what Generate does.
func GenerateDSN(dsn, destDir string, templates ...template.Template) (err error) {
defer utils.ErrorCatch(&err)
cfg, err := mysqldr.ParseDSN(dsn)
throw.OnError(err)
if cfg.DBName == "" {
panic("database name is required")
}
db := openConnection(dsn)
defer utils.DBClose(db)
generate(db, cfg.DBName, destDir, templates...)
return nil
}
func openConnection(connectionString string) *sql.DB {
fmt.Println("Connecting to MySQL database: " + connectionString)
db, err := sql.Open("mysql", connectionString)
throw.OnError(err)
@ -56,3 +68,16 @@ func openConnection(dbConn DBConnection) *sql.DB {
return db
}
func generate(db *sql.DB, dbName, destDir string, templates ...template.Template) {
fmt.Println("Retrieving database information...")
// No schemas in MySQL
schemaMetaData := metadata.GetSchema(db, &mySqlQuerySet{}, dbName)
genTemplate := template.Default(mysql.Dialect)
if len(templates) > 0 {
genTemplate = templates[0]
}
template.ProcessSchema(destDir, schemaMetaData, genTemplate)
}

View file

@ -3,13 +3,15 @@ package postgres
import (
"database/sql"
"fmt"
"path"
"strconv"
"github.com/go-jet/jet/v2/generator/metadata"
"github.com/go-jet/jet/v2/generator/template"
"github.com/go-jet/jet/v2/internal/utils"
"github.com/go-jet/jet/v2/internal/utils/throw"
"github.com/go-jet/jet/v2/postgres"
"path"
"strconv"
"github.com/jackc/pgconn"
)
// DBConnection contains postgres connection details
@ -29,32 +31,37 @@ type DBConnection struct {
func Generate(destDir string, dbConn DBConnection, genTemplate ...template.Template) (err error) {
defer utils.ErrorCatch(&err)
db := openConnection(dbConn)
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)
db := openConnection(connectionString)
defer utils.DBClose(db)
fmt.Println("Retrieving schema information...")
generatorTemplate := template.Default(postgres.Dialect)
if len(genTemplate) > 0 {
generatorTemplate = genTemplate[0]
}
schemaMetadata := metadata.GetSchema(db, &postgresQuerySet{}, dbConn.SchemaName)
dirPath := path.Join(destDir, dbConn.DBName)
template.ProcessSchema(dirPath, schemaMetadata, generatorTemplate)
generate(db, dbConn.DBName, dbConn.SchemaName, destDir, genTemplate...)
return
}
func openConnection(dbConn DBConnection) *sql.DB {
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)
func GenerateDSN(dsn, schema, destDir string, templates ...template.Template) (err error) {
defer utils.ErrorCatch(&err)
fmt.Println("Connecting to postgres database: " + connectionString)
cfg, err := pgconn.ParseConfig(dsn)
throw.OnError(err)
if cfg.Database == "" {
panic("database name is required")
}
db := openConnection(dsn)
defer utils.DBClose(db)
db, err := sql.Open("postgres", connectionString)
generate(db, cfg.Database, schema, destDir, templates...)
return
}
func openConnection(dsn string) *sql.DB {
fmt.Println("Connecting to postgres database: " + dsn)
db, err := sql.Open("postgres", dsn)
throw.OnError(err)
err = db.Ping()
@ -62,3 +69,17 @@ func openConnection(dbConn DBConnection) *sql.DB {
return db
}
func generate(db *sql.DB, dbName, schema, destDir string, templates ...template.Template) {
fmt.Println("Retrieving schema information...")
generatorTemplate := template.Default(postgres.Dialect)
if len(templates) > 0 {
generatorTemplate = templates[0]
}
schemaMetadata := metadata.GetSchema(db, &postgresQuerySet{}, schema)
dirPath := path.Join(destDir, dbName)
template.ProcessSchema(dirPath, schemaMetadata, generatorTemplate)
}

1
go.mod
View file

@ -6,6 +6,7 @@ require (
github.com/go-sql-driver/mysql v1.5.0
github.com/google/go-cmp v0.5.0 //tests
github.com/google/uuid v1.1.1
github.com/jackc/pgconn v1.8.1
github.com/jackc/pgx/v4 v4.11.0 //tests
github.com/lib/pq v1.7.0
github.com/pkg/profile v1.5.0 //tests

2
go.sum
View file

@ -42,7 +42,6 @@ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -457,7 +456,6 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View file

@ -4,16 +4,17 @@ import (
"database/sql"
"flag"
"fmt"
"io/ioutil"
"os"
"os/exec"
"strings"
"github.com/go-jet/jet/v2/generator/mysql"
"github.com/go-jet/jet/v2/generator/postgres"
"github.com/go-jet/jet/v2/internal/utils/throw"
"github.com/go-jet/jet/v2/tests/dbconfig"
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
"io/ioutil"
"os"
"os/exec"
"strings"
)
var testSuite string

View file

@ -1,14 +1,16 @@
package mysql
import (
"github.com/go-jet/jet/v2/generator/mysql"
"github.com/go-jet/jet/v2/internal/testutils"
"github.com/go-jet/jet/v2/tests/dbconfig"
"github.com/stretchr/testify/require"
"fmt"
"io/ioutil"
"os"
"os/exec"
"testing"
"github.com/go-jet/jet/v2/generator/mysql"
"github.com/go-jet/jet/v2/internal/testutils"
"github.com/go-jet/jet/v2/tests/dbconfig"
"github.com/stretchr/testify/require"
)
const genTestDirRoot = "./.gentestdata3"
@ -30,6 +32,21 @@ func TestGenerator(t *testing.T) {
assertGeneratedFiles(t)
}
for i := 0; i < 3; i++ {
dsn := fmt.Sprintf("%[1]s:%[2]s@tcp(%[3]s:%[4]d)/%[5]s",
dbconfig.MySQLUser,
dbconfig.MySQLPassword,
dbconfig.MySqLHost,
dbconfig.MySQLPort,
"dvds",
)
err := mysql.GenerateDSN(dsn, genTestDir3)
require.NoError(t, err)
assertGeneratedFiles(t)
}
err := os.RemoveAll(genTestDirRoot)
require.NoError(t, err)
}
@ -51,6 +68,25 @@ func TestCmdGenerator(t *testing.T) {
err = os.RemoveAll(genTestDirRoot)
require.NoError(t, err)
// check that generation via DSN works
dsn := fmt.Sprintf("mysql://%[1]s:%[2]s@tcp(%[3]s:%[4]d)/%[5]s",
dbconfig.MySQLUser,
dbconfig.MySQLPassword,
dbconfig.MySqLHost,
dbconfig.MySQLPort,
"dvds",
)
cmd = exec.Command("jet", "-dsn="+dsn, "-path="+genTestDir3)
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
err = cmd.Run()
require.NoError(t, err)
err = os.RemoveAll(genTestDirRoot)
require.NoError(t, err)
}
func assertGeneratedFiles(t *testing.T) {

View file

@ -1,16 +1,18 @@
package postgres
import (
"github.com/go-jet/jet/v2/generator/postgres"
"github.com/go-jet/jet/v2/internal/testutils"
"github.com/go-jet/jet/v2/tests/dbconfig"
"github.com/stretchr/testify/require"
"fmt"
"io/ioutil"
"os"
"os/exec"
"reflect"
"testing"
"github.com/go-jet/jet/v2/generator/postgres"
"github.com/go-jet/jet/v2/internal/testutils"
"github.com/go-jet/jet/v2/tests/dbconfig"
"github.com/stretchr/testify/require"
"github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/model"
)
@ -61,6 +63,26 @@ func TestCmdGenerator(t *testing.T) {
err = os.RemoveAll(genTestDir2)
require.NoError(t, err)
// Check that connection via DSN works
dsn := fmt.Sprintf("postgresql://%s:%s@%s:%d/%s?sslmode=disable",
dbconfig.PgUser,
dbconfig.PgPassword,
dbconfig.PgHost,
dbconfig.PgPort,
"jetdb",
)
cmd = exec.Command("jet", "-dsn="+dsn, "-schema=dvds", "-path="+genTestDir2)
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
err = cmd.Run()
require.NoError(t, err)
assertGeneratedFiles(t)
err = os.RemoveAll(genTestDir2)
require.NoError(t, err)
}
func TestGenerator(t *testing.T) {
@ -83,6 +105,21 @@ func TestGenerator(t *testing.T) {
assertGeneratedFiles(t)
}
for i := 0; i < 3; i++ {
dsn := fmt.Sprintf("postgresql://%[1]s:%[2]s@%[3]s:%[4]d/%[5]s?sslmode=disable",
dbconfig.PgUser,
dbconfig.PgPassword,
dbconfig.PgHost,
dbconfig.PgPort,
dbconfig.PgDBName,
)
err := postgres.GenerateDSN(dsn, "dvds", genTestDir2)
require.NoError(t, err)
assertGeneratedFiles(t)
}
err := os.RemoveAll(genTestDir2)
require.NoError(t, err)
}