@@ -71,18 +71,26 @@ type Sqlcmd struct {
71
71
echoFileLines bool
72
72
// Exitcode is returned to the operating system when the process exits
73
73
Exitcode int
74
- Connect * ConnectSettings
75
- vars * Variables
76
- Format Formatter
77
- Query string
78
- Cmd Commands
74
+ // Connect controls how Sqlcmd connects to the database
75
+ Connect * ConnectSettings
76
+ vars * Variables
77
+ // Format renders the query output
78
+ Format Formatter
79
+ // Query is the TSQL query to run
80
+ Query string
81
+ // Cmd provides the implementation of commands like :list and GO
82
+ Cmd Commands
79
83
// PrintError allows the host to redirect errors away from the default output. Returns false if the error is not redirected by the host.
80
- PrintError func (msg string , severity uint8 ) bool
84
+ PrintError func (msg string , severity uint8 ) bool
85
+ // UnicodeOutputFile is true when UTF16 file output is needed
81
86
UnicodeOutputFile bool
82
87
colorizer color.Colorizer
88
+ termchan chan os.Signal
83
89
}
84
90
85
- // New creates a new Sqlcmd instance
91
+ // New creates a new Sqlcmd instance.
92
+ // The Console instane must be non-nil for Sqlcmd to run in interactive mode.
93
+ // The hosting application is responsible for calling Close() on the Console instance before process exit.
86
94
func New (l Console , workingDirectory string , vars * Variables ) * Sqlcmd {
87
95
s := & Sqlcmd {
88
96
lineIo : l ,
@@ -107,8 +115,9 @@ func (s *Sqlcmd) scanNext() (string, error) {
107
115
// Run processes all available batches.
108
116
// When once is true it stops after the first query runs.
109
117
// When processAll is true it executes any remaining batch content when reaching EOF
118
+ // The error returned from Run is mainly of informational value. Its Message will have been printed
119
+ // before Run returns.
110
120
func (s * Sqlcmd ) Run (once bool , processAll bool ) error {
111
- setupCloseHandler (s )
112
121
iactive := s .lineIo != nil
113
122
var lastError error
114
123
for {
@@ -160,8 +169,10 @@ func (s *Sqlcmd) Run(once bool, processAll bool) error {
160
169
}
161
170
}
162
171
172
+ // Some Console implementations catch the ctrl-c so s.termchan isn't signalled
163
173
if err == ErrCtrlC {
164
- os .Exit (0 )
174
+ s .Exitcode = 0
175
+ return err
165
176
}
166
177
if err != nil && err != io .EOF && (s .Connect .ExitOnError && ! s .Connect .IgnoreError ) {
167
178
// If the error were due to a SQL error, the GO command handler
@@ -392,16 +403,6 @@ func (s *Sqlcmd) getRunnableQuery(q string) string {
392
403
return b .String ()
393
404
}
394
405
395
- func setupCloseHandler (s * Sqlcmd ) {
396
- c := make (chan os.Signal , 1 )
397
- signal .Notify (c , os .Interrupt , syscall .SIGTERM )
398
- go func () {
399
- <- c
400
- s .WriteError (s .GetOutput (), ErrCtrlC )
401
- os .Exit (0 )
402
- }()
403
- }
404
-
405
406
// runQuery runs the query and prints the results
406
407
// The return value is based on the first cell of the last column of the last result set.
407
408
// If it's numeric, it will be converted to int
@@ -540,3 +541,26 @@ func (s Sqlcmd) Log(_ context.Context, _ msdsn.Log, msg string) {
540
541
_ , _ = s .GetOutput ().Write ([]byte ("DRIVER:" + msg ))
541
542
_ , _ = s .GetOutput ().Write ([]byte (SqlcmdEol ))
542
543
}
544
+
545
+ // SetupCloseHandler subscribes to the os.Signal channel for SIGTERM.
546
+ // When it receives the event due to the user pressing ctrl-c or ctrl-break
547
+ // that isn't handled directly by the Console or hosting application,
548
+ // it will call Close() on the Console and exit the application.
549
+ // Use StopCloseHandler to remove the subscription
550
+ func (s * Sqlcmd ) SetupCloseHandler () {
551
+ s .termchan = make (chan os.Signal , 1 )
552
+ signal .Notify (s .termchan , os .Interrupt , syscall .SIGTERM )
553
+ go func () {
554
+ <- s .termchan
555
+ s .WriteError (s .GetOutput (), ErrCtrlC )
556
+ if s .lineIo != nil {
557
+ s .lineIo .Close ()
558
+ }
559
+ os .Exit (0 )
560
+ }()
561
+ }
562
+
563
+ // StopCloseHandler unsubscribes the Sqlcmd from the SIGTERM signal
564
+ func (s * Sqlcmd ) StopCloseHandler () {
565
+ signal .Stop (s .termchan )
566
+ }
0 commit comments