Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,23 @@ For further information on usage:
* `server=localhost;user id=sa;database=master;app name=MyAppName;krb5-configfile=path/to/file;krb5-realm=domain.com;krb5-keytabfile=path/to/keytabfile;authenticator=krb5`


ADO strings support synonyms for database, app name, user id, and server
ADO strings support synonyms for common connection parameters:
* server <= addr, address, network address, data source
* user id <= user, uid
* password <= pwd
* database <= initial catalog
* app name <= application name
* app name <= application name, app
* connection timeout <= connect timeout, timeout
* failoverpartner <= failover partner
* failoverpartnerspn <= failover partner spn
* applicationintent <= application intent
* trustservercertificate <= trust server certificate
* multisubnetfailover <= multi subnet failover
* hostnameincertificate <= host name in certificate
* serverspn <= server spn
* servercertificate <= server certificate
* workstation id <= wsid
* columnencryption <= column encryption setting

3. ODBC: Prefix with `odbc`, `key=value` pairs separated by `;`. Allow `;` by wrapping
values in `{}`. Examples:
Expand Down
21 changes: 17 additions & 4 deletions msdsn/conn_str.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ func Parse(dsn string) (Config, error) {
}
}

// https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/configure-the-network-packet-size-server-configuration-option\
// https://docs.microsoft.com/sql/database-engine/configure-windows/configure-the-network-packet-size-server-configuration-option
strpsize, ok := params[PacketSize]
if ok {
var err error
Expand All @@ -462,7 +462,7 @@ func Parse(dsn string) (Config, error) {
}
}

// https://msdn.microsoft.com/en-us/library/dd341108.aspx
// https://msdn.microsoft.com/library/dd341108.aspx
//
// Do not set a connection timeout. Use Context to manage such things.
// Default to zero, but still allow it to be set.
Expand All @@ -476,7 +476,7 @@ func Parse(dsn string) (Config, error) {
}

// default keep alive should be 30 seconds according to spec:
// https://msdn.microsoft.com/en-us/library/dd341108.aspx
// https://msdn.microsoft.com/library/dd341108.aspx
p.KeepAlive = 30 * time.Second
if keepAlive, ok := params[KeepAlive]; ok {
timeout, err := strconv.ParseUint(keepAlive, 10, 64)
Expand Down Expand Up @@ -759,8 +759,10 @@ func (p Config) URL() *url.URL {
return &res
}

// ADO connection string keywords at https://github.com/dotnet/SqlClient/blob/main/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs
// adoSynonyms maps ADO.Net alternate keyword forms to this driver's canonical keys.
// See https://learn.microsoft.com/dotnet/api/microsoft.data.sqlclient.sqlconnection.connectionstring
var adoSynonyms = map[string]string{
"app": AppName,
"application name": AppName,
"data source": Server,
"address": Server,
Expand All @@ -770,6 +772,17 @@ var adoSynonyms = map[string]string{
"uid": UserID,
"pwd": Password,
"initial catalog": Database,
"connect timeout": ConnectionTimeout,
"timeout": ConnectionTimeout,
"failover partner": FailoverPartner,
"failover partner spn": FailoverPartnerSpn,
"application intent": ApplicationIntent,
"trust server certificate": TrustServerCertificate,
"multi subnet failover": MultiSubnetFailover,
"host name in certificate": HostNameInCertificate,
"server spn": ServerSpn,
"server certificate": ServerCertificate,
"wsid": WorkstationID,
Comment thread
dlevy-msft-sql marked this conversation as resolved.
"column encryption setting": "columnencryption",
}

Expand Down
37 changes: 37 additions & 0 deletions msdsn/conn_str_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,33 @@ func TestValidConnectionString(t *testing.T) {
{"password=\"测试\"\"密码\"\"\"", func(p Config) bool { return p.Password == "测试\"密码\"" }}, // Chinese chars with escaped quotes
{"password=\"café;naïve;résumé\"", func(p Config) bool { return p.Password == "café;naïve;résumé" }}, // Accented characters

// ADO.Net synonym tests
{"App=myapp", func(p Config) bool { return p.AppName == "myapp" }},
{"Application Name=myapp", func(p Config) bool { return p.AppName == "myapp" }},
{"Data Source=somehost", func(p Config) bool { return p.Host == "somehost" }},
{"Address=somehost", func(p Config) bool { return p.Host == "somehost" }},
{"Network Address=somehost", func(p Config) bool { return p.Host == "somehost" }},
{"Addr=somehost", func(p Config) bool { return p.Host == "somehost" }},
{"User=someuser", func(p Config) bool { return p.User == "someuser" }},
{"UID=someuser", func(p Config) bool { return p.User == "someuser" }},
{"PWD=somepass", func(p Config) bool { return p.Password == "somepass" }},
{"Initial Catalog=mydb", func(p Config) bool { return p.Database == "mydb" }},
{"Connect Timeout=60", func(p Config) bool { return p.ConnTimeout == 60*time.Second }},
{"Timeout=45", func(p Config) bool { return p.ConnTimeout == 45*time.Second }},
{"Failover Partner=mirror", func(p Config) bool { return p.FailOverPartner == "mirror" }},
{"Failover Partner SPN=MSSQLSvc/mirror:1433", func(p Config) bool { return p.FailOverPartnerSPN == "MSSQLSvc/mirror:1433" }},
{"Application Intent=ReadOnly;database=mydb", func(p Config) bool { return p.ReadOnlyIntent }},
{"Trust Server Certificate=true;encrypt=true", func(p Config) bool { return p.TrustServerCertificate }},
{"Multi Subnet Failover=false", func(p Config) bool { return !p.MultiSubnetFailover }},
{"Host Name In Certificate=myhost", func(p Config) bool { return p.HostInCertificateProvided }},
Comment thread
dlevy-msft-sql marked this conversation as resolved.
{"Server SPN=MSSQLSvc/myhost:1433", func(p Config) bool { return p.ServerSPN == "MSSQLSvc/myhost:1433" }},
{"WSID=myworkstation", func(p Config) bool { return p.Workstation == "myworkstation" }},
{"Column Encryption Setting=true", func(p Config) bool { return p.ColumnEncryption }},
// Verify synonym keys work together in the same connection string
{"Data Source=somehost;Initial Catalog=mydb;Connect Timeout=30", func(p Config) bool {
return p.Host == "somehost" && p.Database == "mydb" && p.ConnTimeout == 30*time.Second
}},

// those are supported currently, but maybe should not be
{"someparam", func(p Config) bool { return true }},
{";;=;", func(p Config) bool { return true }},
Expand Down Expand Up @@ -302,6 +329,16 @@ func TestValidConnectionString(t *testing.T) {
}
}

func TestAdoSynonymServerCertificate(t *testing.T) {
// Server Certificate can't be tested through Parse() because parseTLS
// tries to read the cert file. Verify the synonym mapping at the
// splitConnectionString level instead.
params := splitConnectionString("Server Certificate=myfile.pem")
if v := params[ServerCertificate]; v != "myfile.pem" {
t.Fatalf("expected %s=myfile.pem, got %q", ServerCertificate, v)
}
}

func TestSplitConnectionStringURL(t *testing.T) {
_, err := splitConnectionStringURL("http://bad")
if err == nil {
Expand Down
Loading