1- package cmd
1+ // This file runs a database-engine plugin as an external process (parse RPC over stdin/stdout).
2+ // It is used only by the plugin-engine generate path (runPluginQuerySet). Vet does not support plugin engines.
23
3- // This file implements the plugin-**engine** path only (runPluginQuerySet).
4- // When engine is not builtin, generate calls runPluginQuerySet: schema+query → engine plugin ParseRequest → compiler.Result → ProcessResult/codegen.
5- // Codegen plugins are handled in generate.go/shim; this file is only for external database engines.
4+ package cmd
65
76import (
7+ "bytes"
88 "context"
9+ "errors"
910 "fmt"
1011 "os"
12+ "os/exec"
1113 "path/filepath"
1214 "strings"
1315
@@ -18,9 +20,72 @@ import (
1820 "github.com/sqlc-dev/sqlc/internal/source"
1921 "github.com/sqlc-dev/sqlc/internal/sql/catalog"
2022 "github.com/sqlc-dev/sqlc/internal/sql/sqlpath"
23+ "google.golang.org/protobuf/proto"
24+
25+ "github.com/sqlc-dev/sqlc/internal/info"
2126 pb "github.com/sqlc-dev/sqlc/pkg/engine"
2227)
2328
29+ // engineProcessRunner runs an engine plugin as an external process.
30+ type engineProcessRunner struct {
31+ Cmd string
32+ Dir string // Working directory for the plugin (config file directory)
33+ Env []string
34+ }
35+
36+ func newEngineProcessRunner (cmd , dir string , env []string ) * engineProcessRunner {
37+ return & engineProcessRunner {Cmd : cmd , Dir : dir , Env : env }
38+ }
39+
40+ func (r * engineProcessRunner ) invoke (ctx context.Context , method string , req , resp proto.Message ) error {
41+ stdin , err := proto .Marshal (req )
42+ if err != nil {
43+ return fmt .Errorf ("failed to encode request: %w" , err )
44+ }
45+
46+ cmdParts := strings .Fields (r .Cmd )
47+ if len (cmdParts ) == 0 {
48+ return fmt .Errorf ("engine plugin not found: %s\n \n Make sure the plugin is installed and available in PATH.\n Install with: go install <plugin-module>@latest" , r .Cmd )
49+ }
50+
51+ path , err := exec .LookPath (cmdParts [0 ])
52+ if err != nil {
53+ return fmt .Errorf ("engine plugin not found: %s\n \n Make sure the plugin is installed and available in PATH.\n Install with: go install <plugin-module>@latest" , r .Cmd )
54+ }
55+
56+ args := append (cmdParts [1 :], method )
57+ cmd := exec .CommandContext (ctx , path , args ... )
58+ cmd .Stdin = bytes .NewReader (stdin )
59+ if r .Dir != "" {
60+ cmd .Dir = r .Dir
61+ }
62+ cmd .Env = append (os .Environ (), fmt .Sprintf ("SQLC_VERSION=%s" , info .Version ))
63+
64+ out , err := cmd .Output ()
65+ if err != nil {
66+ stderr := err .Error ()
67+ var exit * exec.ExitError
68+ if errors .As (err , & exit ) {
69+ stderr = string (exit .Stderr )
70+ }
71+ return fmt .Errorf ("engine plugin error: %s" , stderr )
72+ }
73+
74+ if err := proto .Unmarshal (out , resp ); err != nil {
75+ return fmt .Errorf ("failed to decode response: %w" , err )
76+ }
77+ return nil
78+ }
79+
80+ // parseRequest invokes the plugin's Parse RPC. Used by runPluginQuerySet.
81+ func (r * engineProcessRunner ) parseRequest (ctx context.Context , req * pb.ParseRequest ) (* pb.ParseResponse , error ) {
82+ resp := & pb.ParseResponse {}
83+ if err := r .invoke (ctx , "parse" , req , resp ); err != nil {
84+ return nil , err
85+ }
86+ return resp , nil
87+ }
88+
2489// defaultCommentSyntax is used when parsing query names from plugin-engine query files.
2590var defaultCommentSyntax = metadata .CommentSyntax (source.CommentSyntax {Dash : true , SlashStar : true , Hash : false })
2691
0 commit comments