From a17b6ef5a54c37f043538bdd0b3243e7fbd3473b Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Thu, 29 Jun 2023 15:31:44 -0700 Subject: [PATCH] feat(cmd/vet): Simplify environment variable substiution --- examples/authors/sqlc.json | 6 +++--- examples/batch/sqlc.json | 2 +- examples/booktest/sqlc.json | 6 +++--- examples/jets/sqlc.json | 2 +- examples/ondeck/sqlc.json | 6 +++--- internal/cmd/vet.go | 39 ++++-------------------------------- internal/shfmt/shfmt.go | 16 +++++++++++++++ internal/shfmt/shfmt_test.go | 17 ++++++++++++++++ 8 files changed, 48 insertions(+), 46 deletions(-) create mode 100644 internal/shfmt/shfmt.go create mode 100644 internal/shfmt/shfmt_test.go diff --git a/examples/authors/sqlc.json b/examples/authors/sqlc.json index ca263556e8..105d591628 100644 --- a/examples/authors/sqlc.json +++ b/examples/authors/sqlc.json @@ -6,7 +6,7 @@ "queries": "postgresql/query.sql", "engine": "postgresql", "database": { - "url": "'postgresql://%s:%s@%s:%s/authors'.format([env.PG_USER, env.PG_PASSWORD, env.PG_HOST, env.PG_PORT])" + "url": "postgresql://${PG_USER}:${PG_PASSWORD}@${PG_HOST}:${PG_PORT}/authors" }, "gen": { "go": { @@ -20,7 +20,7 @@ "queries": "mysql/query.sql", "engine": "mysql", "database": { - "url": "'root:%s@tcp(%s:%s)/authors?multiStatements=true&parseTime=true'.format([env.MYSQL_ROOT_PASSWORD, env.MYSQL_HOST, env.MYSQL_PORT])" + "url": "root:${MYSQL_ROOT_PASSWORD}@tcp(${MYSQL_HOST}:${MYSQL_PORT})/authors?multiStatements=true&parseTime=true" }, "gen": { "go": { @@ -34,7 +34,7 @@ "queries": "sqlite/query.sql", "engine": "sqlite", "database": { - "url": "'file:authors?mode=memory&cache=shared'" + "url": "file:authors?mode=memory&cache=shared" }, "gen": { "go": { diff --git a/examples/batch/sqlc.json b/examples/batch/sqlc.json index b994f3b945..899d116f5f 100644 --- a/examples/batch/sqlc.json +++ b/examples/batch/sqlc.json @@ -8,7 +8,7 @@ "queries": "postgresql/query.sql", "engine": "postgresql", "database": { - "url": "'postgresql://%s:%s@%s:%s/batch'.format([env.PG_USER, env.PG_PASSWORD, env.PG_HOST, env.PG_PORT])" + "url": "postgresql://${PG_USER}:${PG_PASSWORD}@${PG_HOST}:${PG_PORT}/batch" }, "sql_package": "pgx/v4", "emit_json_tags": true, diff --git a/examples/booktest/sqlc.json b/examples/booktest/sqlc.json index 7b58f921ad..da33d78f54 100644 --- a/examples/booktest/sqlc.json +++ b/examples/booktest/sqlc.json @@ -8,7 +8,7 @@ "queries": "postgresql/query.sql", "engine": "postgresql", "database": { - "url": "'postgresql://%s:%s@%s:%s/booktest'.format([env.PG_USER, env.PG_PASSWORD, env.PG_HOST, env.PG_PORT])" + "url": "postgresql://${PG_USER}:${PG_PASSWORD}@${PG_HOST}:${PG_PORT}/booktest" } }, { @@ -18,7 +18,7 @@ "queries": "mysql/query.sql", "engine": "mysql", "database": { - "url": "'root:%s@tcp(%s:%s)/booktest?multiStatements=true&parseTime=true'.format([env.MYSQL_ROOT_PASSWORD, env.MYSQL_HOST, env.MYSQL_PORT])" + "url": "root:${MYSQL_ROOT_PASSWORD}@tcp(${MYSQL_HOST}:${MYSQL_PORT})/booktest?multiStatements=true&parseTime=true" } }, { @@ -28,7 +28,7 @@ "queries": "sqlite/query.sql", "engine": "sqlite", "database": { - "url": "'file:booktest?mode=memory&cache=shared'" + "url": "file:booktest?mode=memory&cache=shared" } } ] diff --git a/examples/jets/sqlc.json b/examples/jets/sqlc.json index 7b15009422..412de61761 100644 --- a/examples/jets/sqlc.json +++ b/examples/jets/sqlc.json @@ -8,7 +8,7 @@ "queries": "postgresql/query-building.sql", "engine": "postgresql", "database": { - "url": "'postgresql://%s:%s@%s:%s/jets'.format([env.PG_USER, env.PG_PASSWORD, env.PG_HOST, env.PG_PORT])" + "url": "postgresql://${PG_USER}:${PG_PASSWORD}@${PG_HOST}:${PG_PORT}/jets" } } ] diff --git a/examples/ondeck/sqlc.json b/examples/ondeck/sqlc.json index e12d00743a..d6139b580c 100644 --- a/examples/ondeck/sqlc.json +++ b/examples/ondeck/sqlc.json @@ -8,7 +8,7 @@ "queries": "postgresql/query", "engine": "postgresql", "database": { - "url": "'postgresql://%s:%s@%s:%s/ondeck'.format([env.PG_USER, env.PG_PASSWORD, env.PG_HOST, env.PG_PORT])" + "url": "postgresql://${PG_USER}:${PG_PASSWORD}@${PG_HOST}:${PG_PORT}/ondeck" }, "emit_json_tags": true, "emit_prepared_queries": true, @@ -21,7 +21,7 @@ "queries": "mysql/query", "engine": "mysql", "database": { - "url": "'root:%s@tcp(%s:%s)/ondeck?multiStatements=true&parseTime=true'.format([env.MYSQL_ROOT_PASSWORD, env.MYSQL_HOST, env.MYSQL_PORT])" + "url": "root:${MYSQL_ROOT_PASSWORD}@tcp(${MYSQL_HOST}:${MYSQL_PORT})/ondeck?multiStatements=true&parseTime=true" }, "emit_json_tags": true, "emit_prepared_queries": true, @@ -34,7 +34,7 @@ "queries": "sqlite/query", "engine": "sqlite", "database": { - "url": "'file:ondeck?mode=memory&cache=shared'" + "url": "file:ondeck?mode=memory&cache=shared" }, "emit_json_tags": true, "emit_prepared_queries": true, diff --git a/internal/cmd/vet.go b/internal/cmd/vet.go index 5968b47d8d..f6e213300b 100644 --- a/internal/cmd/vet.go +++ b/internal/cmd/vet.go @@ -23,6 +23,7 @@ import ( "github.com/kyleconroy/sqlc/internal/debug" "github.com/kyleconroy/sqlc/internal/opts" "github.com/kyleconroy/sqlc/internal/plugin" + "github.com/kyleconroy/sqlc/internal/shfmt" "github.com/kyleconroy/sqlc/internal/sql/ast" ) @@ -107,21 +108,9 @@ func Vet(ctx context.Context, e Env, dir, filename string, stderr io.Writer) err msgs[c.Name] = c.Msg } - dbenv, err := cel.NewEnv( - cel.StdLib(), - ext.Strings(ext.StringsVersion(1)), - cel.Variable("env", - cel.MapType(cel.StringType, cel.StringType), - ), - ) - if err != nil { - return fmt.Errorf("new dbenv; %s", err) - } - c := checker{ Checks: checks, Conf: conf, - Dbenv: dbenv, Dir: dir, Env: env, Envmap: map[string]string{}, @@ -197,7 +186,6 @@ func (p *dbPreparer) Prepare(ctx context.Context, name, query string) error { type checker struct { Checks map[string]cel.Program Conf *config.Config - Dbenv *cel.Env Dir string Env *cel.Env Envmap map[string]string @@ -205,15 +193,7 @@ type checker struct { Stderr io.Writer } -func (c *checker) DSN(expr string) (string, error) { - ast, issues := c.Dbenv.Compile(expr) - if issues != nil && issues.Err() != nil { - return "", fmt.Errorf("type-check error: database url %s", issues.Err()) - } - prg, err := c.Dbenv.Program(ast) - if err != nil { - return "", fmt.Errorf("program construction error: database url %s", err) - } +func (c *checker) DSN(dsn string) (string, error) { // Populate the environment variable map if it is empty if len(c.Envmap) == 0 { for _, e := range os.Environ() { @@ -221,17 +201,7 @@ func (c *checker) DSN(expr string) (string, error) { c.Envmap[k] = v } } - out, _, err := prg.Eval(map[string]any{ - "env": c.Envmap, - }) - if err != nil { - return "", fmt.Errorf("expression error: %s", err) - } - dsn, ok := out.Value().(string) - if !ok { - return "", fmt.Errorf("expression returned non-string value: %v", out.Value()) - } - return dsn, nil + return shfmt.Replace(dsn, c.Envmap), nil } func (c *checker) checkSQL(ctx context.Context, s config.SQL) error { @@ -312,9 +282,8 @@ func (c *checker) checkSQL(ctx context.Context, s config.SQL) error { if prep != nil && prepareable(s, original.RawStmt) { name := fmt.Sprintf("sqlc_vet_%d_%d", time.Now().Unix(), i) if err := prep.Prepare(ctx, name, query.Text); err != nil { - fmt.Fprintf(c.Stderr, "%s: error preparing %s: %s\n", query.Filename, query.Name, err) + fmt.Fprintf(c.Stderr, "%s: error preparing %s on %s: %s\n", query.Filename, query.Name, s.Engine, err) errored = true - continue } } q := vetQuery(query) diff --git a/internal/shfmt/shfmt.go b/internal/shfmt/shfmt.go new file mode 100644 index 0000000000..a3f1c5bbff --- /dev/null +++ b/internal/shfmt/shfmt.go @@ -0,0 +1,16 @@ +package shfmt + +import ( + "regexp" + "strings" +) + +var pat = regexp.MustCompile(`\$\{[A-Z_]+\}`) + +func Replace(f string, vars map[string]string) string { + return pat.ReplaceAllStringFunc(f, func(s string) string { + s = strings.TrimPrefix(s, "${") + s = strings.TrimSuffix(s, "}") + return vars[s] + }) +} diff --git a/internal/shfmt/shfmt_test.go b/internal/shfmt/shfmt_test.go new file mode 100644 index 0000000000..ce5c29ea5a --- /dev/null +++ b/internal/shfmt/shfmt_test.go @@ -0,0 +1,17 @@ +package shfmt + +import "testing" + +func TestReplace(t *testing.T) { + s := "POSTGRES_SQL://${PG_USER}:${PG_PASSWORD}@${PG_HOST}:${PG_PORT}/AUTHORS" + env := map[string]string{ + "PG_USER": "user", + "PG_PASSWORD": "password", + "PG_HOST": "host", + "PG_PORT": "port", + } + e := "POSTGRES_SQL://user:password@host:port/AUTHORS" + if v := Replace(s, env); v != e { + t.Errorf("%s != %s", v, e) + } +}