@@ -26,6 +26,8 @@ type Command struct {
2626 action func (* Sqlcmd , []string , uint ) error
2727 // Name of the command
2828 name string
29+ // whether the command is a system command
30+ isSystem bool
2931}
3032
3133// Commands is the set of sqlcmd command implementations
@@ -89,9 +91,16 @@ func newCommands() Commands {
8991 name : "CONNECT" ,
9092 },
9193 "EXEC" : {
92- regex : regexp .MustCompile (`(?im)^[ \t]*?:?!!(?:[ \t]+(.*$)|$)` ),
93- action : execCommand ,
94- name : "EXEC" ,
94+ regex : regexp .MustCompile (`(?im)^[ \t]*?:?!!(?:[ \t]+(.*$)|$)` ),
95+ action : execCommand ,
96+ name : "EXEC" ,
97+ isSystem : true ,
98+ },
99+ "EDIT" : {
100+ regex : regexp .MustCompile (`(?im)^[\t ]*?:?ED(?:[ \t]+(.*$)|$)` ),
101+ action : editCommand ,
102+ name : "EDIT" ,
103+ isSystem : true ,
95104 },
96105 }
97106}
@@ -103,8 +112,13 @@ func (c Commands) DisableSysCommands(exitOnCall bool) {
103112 if exitOnCall {
104113 f = errorDisabled
105114 }
106- c ["EXEC" ].action = f
115+ for _ , cmd := range c {
116+ if cmd .isSystem {
117+ cmd .action = f
118+ }
119+ }
107120}
121+
108122func (c Commands ) matchCommand (line string ) (* Command , []string ) {
109123 for _ , cmd := range c {
110124 matchedCommand := cmd .regex .FindStringSubmatch (line )
@@ -411,6 +425,40 @@ func execCommand(s *Sqlcmd, args []string, line uint) error {
411425 return nil
412426}
413427
428+ func editCommand (s * Sqlcmd , args []string , line uint ) error {
429+ if args != nil && strings .TrimSpace (args [0 ]) != "" {
430+ return InvalidCommandError ("ED" , line )
431+ }
432+ file , err := os .CreateTemp ("" , "sq*.sql" )
433+ if err != nil {
434+ return err
435+ }
436+ fileName := file .Name ()
437+ defer os .Remove (fileName )
438+ text := s .batch .String ()
439+ if s .batch .State () == "-" {
440+ text = fmt .Sprintf ("%s%s" , text , SqlcmdEol )
441+ }
442+ _ , err = file .WriteString (text )
443+ if err != nil {
444+ return err
445+ }
446+ file .Close ()
447+ cmd := sysCommand (s .vars .TextEditor () + " " + `"` + fileName + `"` )
448+ cmd .Stderr = s .GetError ()
449+ cmd .Stdout = s .GetOutput ()
450+ err = cmd .Run ()
451+ if err != nil {
452+ return err
453+ }
454+ wasEcho := s .echoFileLines
455+ s .echoFileLines = true
456+ s .batch .Reset (nil )
457+ _ = s .IncludeFile (fileName , false )
458+ s .echoFileLines = wasEcho
459+ return nil
460+ }
461+
414462func resolveArgumentVariables (s * Sqlcmd , arg []rune , failOnUnresolved bool ) (string , error ) {
415463 var b * strings.Builder
416464 end := len (arg )
0 commit comments