diff --git a/Makefile b/Makefile index 160df6e4e5..9739114f11 100644 --- a/Makefile +++ b/Makefile @@ -46,3 +46,9 @@ mysqlsh: proto: buf generate + +remote-proto: + protoc \ + --go_out=. --go_opt="Minternal/remote/gen.proto=github.com/kyleconroy/sqlc/internal/remote" --go_opt=module=github.com/kyleconroy/sqlc \ + --go-grpc_out=. --go-grpc_opt="Minternal/remote/gen.proto=github.com/kyleconroy/sqlc/internal/remote" --go-grpc_opt=module=github.com/kyleconroy/sqlc \ + internal/remote/gen.proto diff --git a/go.mod b/go.mod index 06caecf0d8..f3a665f112 100644 --- a/go.mod +++ b/go.mod @@ -15,20 +15,17 @@ require ( github.com/lib/pq v1.10.7 github.com/mattn/go-sqlite3 v1.14.16 github.com/pganalyze/pg_query_go/v4 v4.2.0 + github.com/riza-io/grpc-go v0.1.0 github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 golang.org/x/sync v0.1.0 + google.golang.org/grpc v1.54.0 google.golang.org/protobuf v1.30.0 gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect - github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect - golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect -) - -require ( github.com/golang/protobuf v1.5.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect @@ -41,10 +38,15 @@ require ( github.com/pingcap/log v0.0.0-20210906054005-afc726e70354 // indirect github.com/pingcap/tidb/parser v0.0.0-20220725134311-c80026e61f00 github.com/pkg/errors v0.9.1 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.7.0 // indirect go.uber.org/zap v1.19.1 // indirect golang.org/x/crypto v0.6.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect + golang.org/x/net v0.8.0 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect + google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect ) diff --git a/go.sum b/go.sum index 228ae869d7..c0d6e6aece 100644 --- a/go.sum +++ b/go.sum @@ -134,6 +134,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/riza-io/grpc-go v0.1.0 h1:qm0j1YT0mqvtaGQ4A+sFe5odQSEE2OGnXGjTJAzQdUM= +github.com/riza-io/grpc-go v0.1.0/go.mod h1:2bDvR9KkKC3KhtlSHfR3dAXjUMT86kg4UfWFyVGWqi8= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= @@ -216,6 +218,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -238,6 +242,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -248,8 +254,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -268,6 +275,10 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index ec7fd76d82..1c872095ee 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -33,6 +33,7 @@ func Do(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) int rootCmd := &cobra.Command{Use: "sqlc", SilenceUsage: true} rootCmd.PersistentFlags().StringP("file", "f", "", "specify an alternate config file (default: sqlc.yaml)") rootCmd.PersistentFlags().BoolP("experimental", "x", false, "DEPRECATED: enable experimental features (default: false)") + rootCmd.PersistentFlags().BoolP("no-remote", "nr", false, "disable remote execution (default: false)") rootCmd.AddCommand(checkCmd) rootCmd.AddCommand(diffCmd) @@ -107,15 +108,18 @@ var initCmd = &cobra.Command{ } type Env struct { - DryRun bool - Debug opts.Debug + DryRun bool + Debug opts.Debug + NoRemote bool } func ParseEnv(c *cobra.Command) Env { dr := c.Flag("dry-run") + nr := c.Flag("no-remote") return Env{ - DryRun: dr != nil && dr.Changed, - Debug: opts.DebugFromEnv(), + DryRun: dr != nil && dr.Changed, + Debug: opts.DebugFromEnv(), + NoRemote: nr != nil && nr.Value.String() == "true", } } diff --git a/internal/cmd/generate.go b/internal/cmd/generate.go index b828c21193..1d0f4a9754 100644 --- a/internal/cmd/generate.go +++ b/internal/cmd/generate.go @@ -13,6 +13,7 @@ import ( "sync" "golang.org/x/sync/errgroup" + "google.golang.org/grpc/status" "github.com/kyleconroy/sqlc/internal/codegen/golang" "github.com/kyleconroy/sqlc/internal/codegen/json" @@ -23,9 +24,12 @@ import ( "github.com/kyleconroy/sqlc/internal/ext" "github.com/kyleconroy/sqlc/internal/ext/process" "github.com/kyleconroy/sqlc/internal/ext/wasm" + "github.com/kyleconroy/sqlc/internal/info" "github.com/kyleconroy/sqlc/internal/multierr" "github.com/kyleconroy/sqlc/internal/opts" "github.com/kyleconroy/sqlc/internal/plugin" + "github.com/kyleconroy/sqlc/internal/remote" + "github.com/kyleconroy/sqlc/internal/sql/sqlpath" ) const errMessageNoVersion = `The configuration file must have a version number. @@ -140,6 +144,10 @@ func Generate(ctx context.Context, e Env, dir, filename string, stderr io.Writer return nil, err } + if conf.Cloud.Hostname != "" && !e.NoRemote { + return remoteGenerate(ctx, configPath, conf, dir, stderr) + } + output := map[string]string{} errored := false @@ -258,6 +266,69 @@ func Generate(ctx context.Context, e Env, dir, filename string, stderr io.Writer return output, nil } +func remoteGenerate(ctx context.Context, configPath string, conf *config.Config, dir string, stderr io.Writer) (map[string]string, error) { + rpcClient, err := remote.NewClient(conf.Cloud) + if err != nil { + fmt.Fprintf(stderr, "error creating rpc client: %s\n", err) + return nil, err + } + + configBytes, err := os.ReadFile(configPath) + if err != nil { + fmt.Fprintf(stderr, "error reading config file %s: %s\n", configPath, err) + return nil, err + } + + rpcReq := remote.GenerateRequest{ + Version: info.Version, + Inputs: []*remote.File{{Path: filepath.Base(configPath), Bytes: configBytes}}, + } + + for _, pkg := range conf.SQL { + for _, paths := range []config.Paths{pkg.Schema, pkg.Queries} { + for i, relFilePath := range paths { + paths[i] = filepath.Join(dir, relFilePath) + } + files, err := sqlpath.Glob(paths) + if err != nil { + fmt.Fprintf(stderr, "error globbing paths: %s\n", err) + return nil, err + } + for _, filePath := range files { + fileBytes, err := os.ReadFile(filePath) + if err != nil { + fmt.Fprintf(stderr, "error reading file %s: %s\n", filePath, err) + return nil, err + } + fileRelPath, _ := filepath.Rel(dir, filePath) + rpcReq.Inputs = append(rpcReq.Inputs, &remote.File{Path: fileRelPath, Bytes: fileBytes}) + } + } + } + + rpcResp, err := rpcClient.Generate(ctx, &rpcReq) + if err != nil { + rpcStatus, ok := status.FromError(err) + if !ok { + return nil, err + } + fmt.Fprintf(stderr, "rpc error: %s", rpcStatus.Message()) + return nil, rpcStatus.Err() + } + + if rpcResp.ExitCode != 0 { + fmt.Fprintf(stderr, "%s", rpcResp.Stderr) + return nil, errors.New("remote execution returned with non-zero exit code") + } + + output := map[string]string{} + for _, file := range rpcResp.Outputs { + output[filepath.Join(dir, file.Path)] = string(file.Bytes) + } + + return output, nil +} + func parse(ctx context.Context, name, dir string, sql config.SQL, combo config.CombinedSettings, parserOpts opts.Parser, stderr io.Writer) (*compiler.Result, bool) { defer trace.StartRegion(ctx, "parse").End() c := compiler.NewCompiler(sql, combo) diff --git a/internal/endtoend/endtoend_test.go b/internal/endtoend/endtoend_test.go index 12f8aa5b21..6f7e888ed7 100644 --- a/internal/endtoend/endtoend_test.go +++ b/internal/endtoend/endtoend_test.go @@ -112,7 +112,8 @@ func TestReplay(t *testing.T) { } env := cmd.Env{ - Debug: opts.DebugFromString(args.Env["SQLCDEBUG"]), + Debug: opts.DebugFromString(args.Env["SQLCDEBUG"]), + NoRemote: true, } switch args.Command { case "diff": diff --git a/internal/endtoend/testdata/remote_missing_semicolon/mysql/go/db.go b/internal/endtoend/testdata/remote_missing_semicolon/mysql/go/db.go new file mode 100644 index 0000000000..02974bda59 --- /dev/null +++ b/internal/endtoend/testdata/remote_missing_semicolon/mysql/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.17.2 + +package querytest + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/remote_missing_semicolon/mysql/go/models.go b/internal/endtoend/testdata/remote_missing_semicolon/mysql/go/models.go new file mode 100644 index 0000000000..c512e16560 --- /dev/null +++ b/internal/endtoend/testdata/remote_missing_semicolon/mysql/go/models.go @@ -0,0 +1,15 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.17.2 + +package querytest + +import ( + "database/sql" +) + +type Author struct { + ID int32 + Name string + Bio sql.NullString +} diff --git a/internal/endtoend/testdata/remote_missing_semicolon/mysql/go/query.sql.go b/internal/endtoend/testdata/remote_missing_semicolon/mysql/go/query.sql.go new file mode 100644 index 0000000000..b037740485 --- /dev/null +++ b/internal/endtoend/testdata/remote_missing_semicolon/mysql/go/query.sql.go @@ -0,0 +1,26 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.17.2 +// source: query.sql + +package querytest + +import ( + "context" +) + +const setAuthor = `-- name: SetAuthor :exec +UPDATE authors +SET name = ? +WHERE id = ? +` + +type SetAuthorParams struct { + Name string + ID int32 +} + +func (q *Queries) SetAuthor(ctx context.Context, arg SetAuthorParams) error { + _, err := q.db.ExecContext(ctx, setAuthor, arg.Name, arg.ID) + return err +} diff --git a/internal/endtoend/testdata/remote_missing_semicolon/mysql/query.sql b/internal/endtoend/testdata/remote_missing_semicolon/mysql/query.sql new file mode 100644 index 0000000000..b3440e7330 --- /dev/null +++ b/internal/endtoend/testdata/remote_missing_semicolon/mysql/query.sql @@ -0,0 +1,11 @@ +-- https://github.com/kyleconroy/sqlc/issues/1198 +CREATE TABLE authors ( + id INT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + bio text +); + +-- name: SetAuthor :exec +UPDATE authors +SET name = ? +WHERE id = ? diff --git a/internal/endtoend/testdata/remote_missing_semicolon/mysql/sqlc.json b/internal/endtoend/testdata/remote_missing_semicolon/mysql/sqlc.json new file mode 100644 index 0000000000..c890ba19e3 --- /dev/null +++ b/internal/endtoend/testdata/remote_missing_semicolon/mysql/sqlc.json @@ -0,0 +1,17 @@ +{ + "version": "1", + "cloud": { + "hostname": "localhost", + "organization": "foo", + "project": "bar" + }, + "packages": [ + { + "path": "go", + "engine": "mysql", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} diff --git a/internal/endtoend/testdata/remote_missing_semicolon/pgx/v4/query.sql b/internal/endtoend/testdata/remote_missing_semicolon/pgx/v4/query.sql new file mode 100644 index 0000000000..cdd62d5ad7 --- /dev/null +++ b/internal/endtoend/testdata/remote_missing_semicolon/pgx/v4/query.sql @@ -0,0 +1,7 @@ +CREATE TABLE foo (email text not null); + +-- name: FirstQuery :many +SELECT * FROM foo; + +-- name: SecondQuery :many +SELECT * FROM foo WHERE email = $1 diff --git a/internal/endtoend/testdata/remote_missing_semicolon/pgx/v4/sqlc.json b/internal/endtoend/testdata/remote_missing_semicolon/pgx/v4/sqlc.json new file mode 100644 index 0000000000..b637f89f3c --- /dev/null +++ b/internal/endtoend/testdata/remote_missing_semicolon/pgx/v4/sqlc.json @@ -0,0 +1,18 @@ +{ + "version": "1", + "cloud": { + "hostname": "localhost", + "organization": "foo", + "project": "bar" + }, + "packages": [ + { + "path": "go", + "engine": "postgresql", + "sql_package": "pgx/v4", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} diff --git a/internal/endtoend/testdata/remote_missing_semicolon/pgx/v4/stderr.txt b/internal/endtoend/testdata/remote_missing_semicolon/pgx/v4/stderr.txt new file mode 100644 index 0000000000..5cd2f160b8 --- /dev/null +++ b/internal/endtoend/testdata/remote_missing_semicolon/pgx/v4/stderr.txt @@ -0,0 +1,2 @@ +# package querytest +query.sql:7:1: missing semicolon at end of file diff --git a/internal/endtoend/testdata/remote_missing_semicolon/pgx/v5/query.sql b/internal/endtoend/testdata/remote_missing_semicolon/pgx/v5/query.sql new file mode 100644 index 0000000000..cdd62d5ad7 --- /dev/null +++ b/internal/endtoend/testdata/remote_missing_semicolon/pgx/v5/query.sql @@ -0,0 +1,7 @@ +CREATE TABLE foo (email text not null); + +-- name: FirstQuery :many +SELECT * FROM foo; + +-- name: SecondQuery :many +SELECT * FROM foo WHERE email = $1 diff --git a/internal/endtoend/testdata/remote_missing_semicolon/pgx/v5/sqlc.json b/internal/endtoend/testdata/remote_missing_semicolon/pgx/v5/sqlc.json new file mode 100644 index 0000000000..9081e9d42b --- /dev/null +++ b/internal/endtoend/testdata/remote_missing_semicolon/pgx/v5/sqlc.json @@ -0,0 +1,18 @@ +{ + "version": "1", + "cloud": { + "hostname": "localhost", + "organization": "foo", + "project": "bar" + }, + "packages": [ + { + "path": "go", + "engine": "postgresql", + "sql_package": "pgx/v5", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} diff --git a/internal/endtoend/testdata/remote_missing_semicolon/pgx/v5/stderr.txt b/internal/endtoend/testdata/remote_missing_semicolon/pgx/v5/stderr.txt new file mode 100644 index 0000000000..5cd2f160b8 --- /dev/null +++ b/internal/endtoend/testdata/remote_missing_semicolon/pgx/v5/stderr.txt @@ -0,0 +1,2 @@ +# package querytest +query.sql:7:1: missing semicolon at end of file diff --git a/internal/endtoend/testdata/remote_missing_semicolon/stdlib/query.sql b/internal/endtoend/testdata/remote_missing_semicolon/stdlib/query.sql new file mode 100644 index 0000000000..cdd62d5ad7 --- /dev/null +++ b/internal/endtoend/testdata/remote_missing_semicolon/stdlib/query.sql @@ -0,0 +1,7 @@ +CREATE TABLE foo (email text not null); + +-- name: FirstQuery :many +SELECT * FROM foo; + +-- name: SecondQuery :many +SELECT * FROM foo WHERE email = $1 diff --git a/internal/endtoend/testdata/remote_missing_semicolon/stdlib/sqlc.json b/internal/endtoend/testdata/remote_missing_semicolon/stdlib/sqlc.json new file mode 100644 index 0000000000..71d2dc0bd3 --- /dev/null +++ b/internal/endtoend/testdata/remote_missing_semicolon/stdlib/sqlc.json @@ -0,0 +1,16 @@ +{ + "version": "1", + "cloud": { + "hostname": "localhost", + "organization": "foo", + "project": "bar" + }, + "packages": [ + { + "path": "go", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} diff --git a/internal/endtoend/testdata/remote_missing_semicolon/stdlib/stderr.txt b/internal/endtoend/testdata/remote_missing_semicolon/stdlib/stderr.txt new file mode 100644 index 0000000000..5cd2f160b8 --- /dev/null +++ b/internal/endtoend/testdata/remote_missing_semicolon/stdlib/stderr.txt @@ -0,0 +1,2 @@ +# package querytest +query.sql:7:1: missing semicolon at end of file diff --git a/internal/remote/gen.pb.go b/internal/remote/gen.pb.go new file mode 100644 index 0000000000..bba3d16444 --- /dev/null +++ b/internal/remote/gen.pb.go @@ -0,0 +1,340 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc v3.21.0 +// source: internal/remote/gen.proto + +package remote + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type GenerateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` + Inputs []*File `protobuf:"bytes,2,rep,name=inputs,proto3" json:"inputs,omitempty"` +} + +func (x *GenerateRequest) Reset() { + *x = GenerateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_remote_gen_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GenerateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenerateRequest) ProtoMessage() {} + +func (x *GenerateRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_remote_gen_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenerateRequest.ProtoReflect.Descriptor instead. +func (*GenerateRequest) Descriptor() ([]byte, []int) { + return file_internal_remote_gen_proto_rawDescGZIP(), []int{0} +} + +func (x *GenerateRequest) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *GenerateRequest) GetInputs() []*File { + if x != nil { + return x.Inputs + } + return nil +} + +type GenerateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Outputs []*File `protobuf:"bytes,1,rep,name=outputs,proto3" json:"outputs,omitempty"` + ExitCode int64 `protobuf:"varint,2,opt,name=exit_code,json=exitCode,proto3" json:"exit_code,omitempty"` + Stdout []byte `protobuf:"bytes,3,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr []byte `protobuf:"bytes,4,opt,name=stderr,proto3" json:"stderr,omitempty"` +} + +func (x *GenerateResponse) Reset() { + *x = GenerateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_remote_gen_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GenerateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenerateResponse) ProtoMessage() {} + +func (x *GenerateResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_remote_gen_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenerateResponse.ProtoReflect.Descriptor instead. +func (*GenerateResponse) Descriptor() ([]byte, []int) { + return file_internal_remote_gen_proto_rawDescGZIP(), []int{1} +} + +func (x *GenerateResponse) GetOutputs() []*File { + if x != nil { + return x.Outputs + } + return nil +} + +func (x *GenerateResponse) GetExitCode() int64 { + if x != nil { + return x.ExitCode + } + return 0 +} + +func (x *GenerateResponse) GetStdout() []byte { + if x != nil { + return x.Stdout + } + return nil +} + +func (x *GenerateResponse) GetStderr() []byte { + if x != nil { + return x.Stderr + } + return nil +} + +type File struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + ContentType string `protobuf:"bytes,2,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"` + Bytes []byte `protobuf:"bytes,3,opt,name=bytes,proto3" json:"bytes,omitempty"` +} + +func (x *File) Reset() { + *x = File{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_remote_gen_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *File) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*File) ProtoMessage() {} + +func (x *File) ProtoReflect() protoreflect.Message { + mi := &file_internal_remote_gen_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use File.ProtoReflect.Descriptor instead. +func (*File) Descriptor() ([]byte, []int) { + return file_internal_remote_gen_proto_rawDescGZIP(), []int{2} +} + +func (x *File) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *File) GetContentType() string { + if x != nil { + return x.ContentType + } + return "" +} + +func (x *File) GetBytes() []byte { + if x != nil { + return x.Bytes + } + return nil +} + +var File_internal_remote_gen_proto protoreflect.FileDescriptor + +var file_internal_remote_gen_proto_rawDesc = []byte{ + 0x0a, 0x19, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x16, 0x72, 0x65, 0x6d, + 0x6f, 0x74, 0x65, 0x2e, 0x73, 0x71, 0x6c, 0x63, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x47, 0x65, 0x6e, + 0x2e, 0x76, 0x31, 0x22, 0x61, 0x0a, 0x0f, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x34, 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x73, 0x71, 0x6c, 0x63, 0x2e, 0x64, + 0x65, 0x76, 0x2e, 0x47, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x06, + 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x22, 0x97, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x07, 0x6f, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x73, 0x71, 0x6c, 0x63, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x47, + 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x64, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x65, 0x78, 0x69, 0x74, 0x43, 0x6f, 0x64, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x06, 0x73, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x64, 0x65, + 0x72, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x74, 0x64, 0x65, 0x72, 0x72, + 0x22, 0x53, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x32, 0x64, 0x0a, 0x03, 0x47, 0x65, 0x6e, 0x12, 0x5d, 0x0a, 0x08, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x12, 0x27, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x2e, 0x73, 0x71, 0x6c, 0x63, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x47, 0x65, 0x6e, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x28, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x73, 0x71, 0x6c, 0x63, 0x2e, + 0x64, 0x65, 0x76, 0x2e, 0x47, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_internal_remote_gen_proto_rawDescOnce sync.Once + file_internal_remote_gen_proto_rawDescData = file_internal_remote_gen_proto_rawDesc +) + +func file_internal_remote_gen_proto_rawDescGZIP() []byte { + file_internal_remote_gen_proto_rawDescOnce.Do(func() { + file_internal_remote_gen_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_remote_gen_proto_rawDescData) + }) + return file_internal_remote_gen_proto_rawDescData +} + +var file_internal_remote_gen_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_internal_remote_gen_proto_goTypes = []interface{}{ + (*GenerateRequest)(nil), // 0: remote.sqlc.dev.Gen.v1.GenerateRequest + (*GenerateResponse)(nil), // 1: remote.sqlc.dev.Gen.v1.GenerateResponse + (*File)(nil), // 2: remote.sqlc.dev.Gen.v1.File +} +var file_internal_remote_gen_proto_depIdxs = []int32{ + 2, // 0: remote.sqlc.dev.Gen.v1.GenerateRequest.inputs:type_name -> remote.sqlc.dev.Gen.v1.File + 2, // 1: remote.sqlc.dev.Gen.v1.GenerateResponse.outputs:type_name -> remote.sqlc.dev.Gen.v1.File + 0, // 2: remote.sqlc.dev.Gen.v1.Gen.Generate:input_type -> remote.sqlc.dev.Gen.v1.GenerateRequest + 1, // 3: remote.sqlc.dev.Gen.v1.Gen.Generate:output_type -> remote.sqlc.dev.Gen.v1.GenerateResponse + 3, // [3:4] is the sub-list for method output_type + 2, // [2:3] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_internal_remote_gen_proto_init() } +func file_internal_remote_gen_proto_init() { + if File_internal_remote_gen_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_internal_remote_gen_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GenerateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_internal_remote_gen_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GenerateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_internal_remote_gen_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*File); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_internal_remote_gen_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_internal_remote_gen_proto_goTypes, + DependencyIndexes: file_internal_remote_gen_proto_depIdxs, + MessageInfos: file_internal_remote_gen_proto_msgTypes, + }.Build() + File_internal_remote_gen_proto = out.File + file_internal_remote_gen_proto_rawDesc = nil + file_internal_remote_gen_proto_goTypes = nil + file_internal_remote_gen_proto_depIdxs = nil +} diff --git a/internal/remote/gen.proto b/internal/remote/gen.proto new file mode 100644 index 0000000000..dc85465154 --- /dev/null +++ b/internal/remote/gen.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; + +package remote.sqlc.dev.Gen.v1; + +service Gen { + rpc Generate(GenerateRequest) returns (GenerateResponse); +} + +message GenerateRequest { + string version = 1; + repeated File inputs = 2; +} + +message GenerateResponse { + repeated File outputs = 1; + int32 exit_code = 2; + bytes stdout = 3; + bytes stderr = 4; +} + +message File { + string path = 1; + string content_type = 2; + bytes bytes = 3; +} \ No newline at end of file diff --git a/internal/remote/gen_grpc.pb.go b/internal/remote/gen_grpc.pb.go new file mode 100644 index 0000000000..a2fb3d76a0 --- /dev/null +++ b/internal/remote/gen_grpc.pb.go @@ -0,0 +1,105 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.21.0 +// source: internal/remote/gen.proto + +package remote + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// GenClient is the client API for Gen service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type GenClient interface { + Generate(ctx context.Context, in *GenerateRequest, opts ...grpc.CallOption) (*GenerateResponse, error) +} + +type genClient struct { + cc grpc.ClientConnInterface +} + +func NewGenClient(cc grpc.ClientConnInterface) GenClient { + return &genClient{cc} +} + +func (c *genClient) Generate(ctx context.Context, in *GenerateRequest, opts ...grpc.CallOption) (*GenerateResponse, error) { + out := new(GenerateResponse) + err := c.cc.Invoke(ctx, "/remote.sqlc.dev.Gen.v1.Gen/Generate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// GenServer is the server API for Gen service. +// All implementations must embed UnimplementedGenServer +// for forward compatibility +type GenServer interface { + Generate(context.Context, *GenerateRequest) (*GenerateResponse, error) + mustEmbedUnimplementedGenServer() +} + +// UnimplementedGenServer must be embedded to have forward compatible implementations. +type UnimplementedGenServer struct { +} + +func (UnimplementedGenServer) Generate(context.Context, *GenerateRequest) (*GenerateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Generate not implemented") +} +func (UnimplementedGenServer) mustEmbedUnimplementedGenServer() {} + +// UnsafeGenServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to GenServer will +// result in compilation errors. +type UnsafeGenServer interface { + mustEmbedUnimplementedGenServer() +} + +func RegisterGenServer(s grpc.ServiceRegistrar, srv GenServer) { + s.RegisterService(&Gen_ServiceDesc, srv) +} + +func _Gen_Generate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GenerateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GenServer).Generate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/remote.sqlc.dev.Gen.v1.Gen/Generate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GenServer).Generate(ctx, req.(*GenerateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Gen_ServiceDesc is the grpc.ServiceDesc for Gen service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Gen_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "remote.sqlc.dev.Gen.v1.Gen", + HandlerType: (*GenServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Generate", + Handler: _Gen_Generate_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "internal/remote/gen.proto", +} diff --git a/internal/remote/rpc.go b/internal/remote/rpc.go new file mode 100644 index 0000000000..de150d616a --- /dev/null +++ b/internal/remote/rpc.go @@ -0,0 +1,26 @@ +package remote + +import ( + "crypto/tls" + "os" + + "github.com/riza-io/grpc-go/credentials/bearer" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + + "github.com/kyleconroy/sqlc/internal/config" +) + +func NewClient(cloudConfig config.Cloud) (GenClient, error) { + opts := []grpc.DialOption{ + grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})), + grpc.WithPerRPCCredentials(bearer.NewPerRPCCredentials(os.Getenv("SQLC_AUTH_TOKEN"))), + } + + conn, err := grpc.Dial(cloudConfig.Hostname+":443", opts...) + if err != nil { + return nil, err + } + + return NewGenClient(conn), nil +}