|
5 | 5 | package sqlcmd |
6 | 6 |
|
7 | 7 | import ( |
| 8 | + "context" |
8 | 9 | "errors" |
9 | 10 | "fmt" |
| 11 | + "net" |
10 | 12 | "os" |
11 | 13 | "regexp" |
12 | 14 | "strconv" |
13 | 15 | "strings" |
| 16 | + "time" |
14 | 17 |
|
15 | 18 | mssql "github.com/microsoft/go-mssqldb" |
16 | 19 | "github.com/microsoft/go-mssqldb/azuread" |
| 20 | + "github.com/microsoft/go-mssqldb/msdsn" |
17 | 21 | "github.com/microsoft/go-sqlcmd/internal/localizer" |
18 | 22 | "github.com/microsoft/go-sqlcmd/pkg/console" |
19 | 23 | "github.com/microsoft/go-sqlcmd/pkg/sqlcmd" |
@@ -216,7 +220,7 @@ func Execute(version string) { |
216 | 220 | fmt.Println() |
217 | 221 | fmt.Println(localizer.Sprintf("Servers:")) |
218 | 222 | } |
219 | | - fmt.Println(" ;UID:Login ID=?;PWD:Password=?;Trusted_Connection:Use Integrated Security=?;*APP:AppName=?;*WSID:WorkStation ID=?;") |
| 223 | + listLocalServers() |
220 | 224 | os.Exit(0) |
221 | 225 | } |
222 | 226 | if len(argss) > 0 { |
@@ -820,3 +824,76 @@ func run(vars *sqlcmd.Variables, args *SQLCmdArguments) (int, error) { |
820 | 824 | s.SetError(nil) |
821 | 825 | return s.Exitcode, err |
822 | 826 | } |
| 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