@@ -88,6 +88,11 @@ func newCommands() Commands {
8888 action : connectCommand ,
8989 name : "CONNECT" ,
9090 },
91+ "EXEC" : {
92+ regex : regexp .MustCompile (`(?im)^[ \t]*?:?!!(?:[ \t]+(.*$)|$)` ),
93+ action : execCommand ,
94+ name : "EXEC" ,
95+ },
9196 }
9297
9398}
@@ -172,6 +177,9 @@ func goCommand(s *Sqlcmd, args []string, line uint) error {
172177 if len (args ) > 0 {
173178 cnt := strings .TrimSpace (args [0 ])
174179 if cnt != "" {
180+ if cnt , err = resolveArgumentVariables (s , []rune (cnt ), true ); err != nil {
181+ return err
182+ }
175183 _ , err = fmt .Sscanf (cnt , "%d" , & n )
176184 }
177185 }
@@ -245,7 +253,8 @@ func readFileCommand(s *Sqlcmd, args []string, line uint) error {
245253 if args == nil || len (args ) != 1 {
246254 return InvalidCommandError (":R" , line )
247255 }
248- return s .IncludeFile (resolveArgumentVariables (s , []rune (args [0 ])), false )
256+ fileName , _ := resolveArgumentVariables (s , []rune (args [0 ]), false )
257+ return s .IncludeFile (fileName , false )
249258}
250259
251260// setVarCommand parses a variable setting and applies it to the current Sqlcmd variables
@@ -345,10 +354,10 @@ func connectCommand(s *Sqlcmd, args []string, line uint) error {
345354 }
346355
347356 connect := s .Connect
348- connect .UserName = resolveArgumentVariables (s , []rune (arguments .Username ))
349- connect .Password = resolveArgumentVariables (s , []rune (arguments .Password ))
350- connect .ServerName = resolveArgumentVariables (s , []rune (arguments .Server ))
351- timeout := resolveArgumentVariables (s , []rune (arguments .LoginTimeout ))
357+ connect .UserName , _ = resolveArgumentVariables (s , []rune (arguments .Username ), false )
358+ connect .Password , _ = resolveArgumentVariables (s , []rune (arguments .Password ), false )
359+ connect .ServerName , _ = resolveArgumentVariables (s , []rune (arguments .Server ), false )
360+ timeout , _ := resolveArgumentVariables (s , []rune (arguments .LoginTimeout ), false )
352361 if timeout != "" {
353362 if timeoutSeconds , err := strconv .ParseInt (timeout , 10 , 32 ); err == nil {
354363 if timeoutSeconds < 0 {
@@ -364,7 +373,26 @@ func connectCommand(s *Sqlcmd, args []string, line uint) error {
364373 return nil
365374}
366375
367- func resolveArgumentVariables (s * Sqlcmd , arg []rune ) string {
376+ func execCommand (s * Sqlcmd , args []string , line uint ) error {
377+ if len (args ) == 0 {
378+ return InvalidCommandError ("EXEC" , line )
379+ }
380+ cmdLine := strings .TrimSpace (args [0 ])
381+ if cmdLine == "" {
382+ return InvalidCommandError ("EXEC" , line )
383+ }
384+ if cmdLine , err := resolveArgumentVariables (s , []rune (cmdLine ), true ); err != nil {
385+ return err
386+ } else {
387+ cmd := sysCommand (cmdLine )
388+ cmd .Stderr = s .GetError ()
389+ cmd .Stdout = s .GetOutput ()
390+ _ = cmd .Run ()
391+ }
392+ return nil
393+ }
394+
395+ func resolveArgumentVariables (s * Sqlcmd , arg []rune , failOnUnresolved bool ) (string , error ) {
368396 var b * strings.Builder
369397 end := len (arg )
370398 for i := 0 ; i < end ; {
@@ -383,6 +411,9 @@ func resolveArgumentVariables(s *Sqlcmd, arg []rune) string {
383411 }
384412 b .WriteString (val )
385413 } else {
414+ if failOnUnresolved {
415+ return "" , UndefinedVariable (varName )
416+ }
386417 _ , _ = s .GetError ().Write ([]byte (UndefinedVariable (varName ).Error () + SqlcmdEol ))
387418 if b != nil {
388419 b .WriteString (string (arg [i : vl + 1 ]))
@@ -403,7 +434,7 @@ func resolveArgumentVariables(s *Sqlcmd, arg []rune) string {
403434 }
404435 }
405436 if b == nil {
406- return string (arg )
437+ return string (arg ), nil
407438 }
408- return b .String ()
439+ return b .String (), nil
409440}
0 commit comments