From cc7df57c4d29381c2dfce3241196f66f513e9665 Mon Sep 17 00:00:00 2001 From: David Levy Date: Tue, 28 Apr 2026 17:18:28 -0500 Subject: [PATCH 1/4] feat: add ADO.Net connection string synonyms Add 13 missing ADO.Net connection string synonyms to the adoSynonyms map: App, Connect Timeout, Timeout, Failover Partner, Failover Partner SPN, Application Intent, Trust Server Certificate, Multi Subnet Failover, Host Name In Certificate, Server SPN, Server Certificate, WSID. Includes 22 new test cases in TestValidConnectionString covering all new synonyms plus combined synonym usage. Closes #373 Related to #288 --- msdsn/conn_str.go | 14 +++++++++++++- msdsn/conn_str_test.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/msdsn/conn_str.go b/msdsn/conn_str.go index 9d5eb9e9..f5311018 100644 --- a/msdsn/conn_str.go +++ b/msdsn/conn_str.go @@ -759,8 +759,9 @@ 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 +// ADO connection string keywords at https://learn.microsoft.com/en-us/dotnet/api/microsoft.data.sqlclient.sqlconnection.connectionstring var adoSynonyms = map[string]string{ + "app": AppName, "application name": AppName, "data source": Server, "address": Server, @@ -770,6 +771,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, "column encryption setting": "columnencryption", } diff --git a/msdsn/conn_str_test.go b/msdsn/conn_str_test.go index 9d23ebf8..dd5d4be9 100644 --- a/msdsn/conn_str_test.go +++ b/msdsn/conn_str_test.go @@ -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=false;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 }}, + {"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 + canonical key both work in 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 }}, @@ -299,6 +326,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 { From 3cc2805e0ee29bd414fa7182047b76e133090606 Mon Sep 17 00:00:00 2001 From: David Levy Date: Tue, 28 Apr 2026 17:44:18 -0500 Subject: [PATCH 2/4] docs: update README ADO synonym list --- README.md | 16 ++++++++++++++-- msdsn/conn_str.go | 3 ++- msdsn/conn_str_test.go | 4 ++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0397fa13..01515546 100644 --- a/README.md +++ b/README.md @@ -191,11 +191,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: diff --git a/msdsn/conn_str.go b/msdsn/conn_str.go index f5311018..f7d1e3a1 100644 --- a/msdsn/conn_str.go +++ b/msdsn/conn_str.go @@ -759,7 +759,8 @@ func (p Config) URL() *url.URL { return &res } -// ADO connection string keywords at https://learn.microsoft.com/en-us/dotnet/api/microsoft.data.sqlclient.sqlconnection.connectionstring +// adoSynonyms maps ADO.Net alternate keyword forms to this driver's canonical keys. +// See https://learn.microsoft.com/en-us/dotnet/api/microsoft.data.sqlclient.sqlconnection.connectionstring var adoSynonyms = map[string]string{ "app": AppName, "application name": AppName, diff --git a/msdsn/conn_str_test.go b/msdsn/conn_str_test.go index dd5d4be9..1538a847 100644 --- a/msdsn/conn_str_test.go +++ b/msdsn/conn_str_test.go @@ -202,13 +202,13 @@ func TestValidConnectionString(t *testing.T) { {"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=false;encrypt=true", func(p Config) bool { return !p.TrustServerCertificate }}, + {"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 }}, {"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 + canonical key both work in same connection string + // 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 }}, From 6163270db964095ddece3451b9f6b1d66c9ad4b9 Mon Sep 17 00:00:00 2001 From: David Levy Date: Thu, 11 Jun 2026 11:53:52 -0500 Subject: [PATCH 3/4] Remove duplicate comment in conn_str.go Removed /en-us/ from comment --- msdsn/conn_str.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msdsn/conn_str.go b/msdsn/conn_str.go index f7d1e3a1..cb63f528 100644 --- a/msdsn/conn_str.go +++ b/msdsn/conn_str.go @@ -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 From 0f937d9484368fcd0fc7cfab674b7a5c84165cb0 Mon Sep 17 00:00:00 2001 From: David Levy Date: Thu, 11 Jun 2026 11:56:28 -0500 Subject: [PATCH 4/4] Update MSDN links in connection string documentation Remove remaining /en-us/ tags in URLs --- msdsn/conn_str.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/msdsn/conn_str.go b/msdsn/conn_str.go index cb63f528..27adaffe 100644 --- a/msdsn/conn_str.go +++ b/msdsn/conn_str.go @@ -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. @@ -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) @@ -760,7 +760,7 @@ func (p Config) URL() *url.URL { } // adoSynonyms maps ADO.Net alternate keyword forms to this driver's canonical keys. -// See https://learn.microsoft.com/en-us/dotnet/api/microsoft.data.sqlclient.sqlconnection.connectionstring +// See https://learn.microsoft.com/dotnet/api/microsoft.data.sqlclient.sqlconnection.connectionstring var adoSynonyms = map[string]string{ "app": AppName, "application name": AppName,