Skip to content

Commit 18f0001

Browse files
authored
implement -L for local instances (#483)
1 parent fb920dc commit 18f0001

File tree

1 file changed

+78
-1
lines changed

1 file changed

+78
-1
lines changed

cmd/sqlcmd/sqlcmd.go

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,19 @@
55
package sqlcmd
66

77
import (
8+
"context"
89
"errors"
910
"fmt"
11+
"net"
1012
"os"
1113
"regexp"
1214
"strconv"
1315
"strings"
16+
"time"
1417

1518
mssql "github.com/microsoft/go-mssqldb"
1619
"github.com/microsoft/go-mssqldb/azuread"
20+
"github.com/microsoft/go-mssqldb/msdsn"
1721
"github.com/microsoft/go-sqlcmd/internal/localizer"
1822
"github.com/microsoft/go-sqlcmd/pkg/console"
1923
"github.com/microsoft/go-sqlcmd/pkg/sqlcmd"
@@ -216,7 +220,7 @@ func Execute(version string) {
216220
fmt.Println()
217221
fmt.Println(localizer.Sprintf("Servers:"))
218222
}
219-
fmt.Println(" ;UID:Login ID=?;PWD:Password=?;Trusted_Connection:Use Integrated Security=?;*APP:AppName=?;*WSID:WorkStation ID=?;")
223+
listLocalServers()
220224
os.Exit(0)
221225
}
222226
if len(argss) > 0 {
@@ -820,3 +824,76 @@ func run(vars *sqlcmd.Variables, args *SQLCmdArguments) (int, error) {
820824
s.SetError(nil)
821825
return s.Exitcode, err
822826
}
827+
828+
func listLocalServers() {
829+
bmsg := []byte{byte(msdsn.BrowserAllInstances)}
830+
resp := make([]byte, 16*1024-1)
831+
dialer := &net.Dialer{}
832+
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
833+
defer cancel()
834+
conn, err := dialer.DialContext(ctx, "udp", ":1434")
835+
// silently ignore failures to connect, same as ODBC
836+
if err != nil {
837+
return
838+
}
839+
defer conn.Close()
840+
dl, _ := ctx.Deadline()
841+
_ = conn.SetDeadline(dl)
842+
_, err = conn.Write(bmsg)
843+
if err != nil {
844+
if !errors.Is(err, os.ErrDeadlineExceeded) {
845+
fmt.Println(err)
846+
}
847+
return
848+
}
849+
read, err := conn.Read(resp)
850+
if err != nil {
851+
if !errors.Is(err, os.ErrDeadlineExceeded) {
852+
fmt.Println(err)
853+
}
854+
return
855+
}
856+
857+
data := parseInstances(resp[:read])
858+
instances := make([]string, 0, len(data))
859+
for s := range data {
860+
if s == "MSSQLSERVER" {
861+
862+
instances = append(instances, "(local)", data[s]["ServerName"])
863+
} else {
864+
instances = append(instances, fmt.Sprintf(`%s\%s`, data[s]["ServerName"], s))
865+
}
866+
}
867+
for _, s := range instances {
868+
fmt.Println(" ", s)
869+
}
870+
}
871+
872+
func parseInstances(msg []byte) msdsn.BrowserData {
873+
results := msdsn.BrowserData{}
874+
if len(msg) > 3 && msg[0] == 5 {
875+
out_s := string(msg[3:])
876+
tokens := strings.Split(out_s, ";")
877+
instdict := map[string]string{}
878+
got_name := false
879+
var name string
880+
for _, token := range tokens {
881+
if got_name {
882+
instdict[name] = token
883+
got_name = false
884+
} else {
885+
name = token
886+
if len(name) == 0 {
887+
if len(instdict) == 0 {
888+
break
889+
}
890+
results[strings.ToUpper(instdict["InstanceName"])] = instdict
891+
instdict = map[string]string{}
892+
continue
893+
}
894+
got_name = true
895+
}
896+
}
897+
}
898+
return results
899+
}

0 commit comments

Comments
 (0)