Skip to content

feat: add NewConnectorWithProcessQueryText for mssql driver compatibility#341

Merged
shueybubbles merged 3 commits intomainfrom
copilot/add-newconnectorconfig-alternative-mssql
Mar 23, 2026
Merged

feat: add NewConnectorWithProcessQueryText for mssql driver compatibility#341
shueybubbles merged 3 commits intomainfrom
copilot/add-newconnectorconfig-alternative-mssql

Conversation

Copy link
Copy Markdown

Copilot AI commented Mar 19, 2026

NewConnectorConfig always returns a connector backed by the sqlserver driver (no query text preprocessing), making it impossible to use a custom msdsn.Config (e.g., with custom TLS RootCAs) with libraries like sqlx that emit ?/:N parameter placeholders.

Changes

  • mssql.go: Adds NewConnectorWithProcessQueryText(config msdsn.Config) *Connector — mirrors NewConnectorConfig but uses driverInstance (processQueryText: true), converting ?, :N, and :Name placeholders to @pN names before sending to SQL Server.
  • queries_test.go: Adds TestConnectorWithProcessQueryText integration test covering ?, :1, and :Name placeholder styles.

Usage

config, err := msdsn.Parse(connectionString)
config.TLSConfig = &tls.Config{RootCAs: certificate.GetRootCAs()}

db := sql.OpenDB(mssql.NewConnectorWithProcessQueryText(config))
// now works correctly with sqlx and other libraries using "?" placeholders
Original prompt

This section details on the original issue you should resolve

<issue_title>Provide NewConnectorConfig alternative for the mssql driver</issue_title>
<issue_description>Is your feature request related to a problem? Please describe.

I am using sqlx and need to provide custom RootCA set to the TLS configuration.

I have attempted the following:

func (d SupportedDatabase) Connect(ctx context.Context, connectionString string) (db *sqlx.DB, err error) {
	switch d {
	case Mssql:
		return connectMssql(ctx, d, connectionString)
	default:
		return sqlx.ConnectContext(ctx, d.String(), connectionString)
	}
}

func connectMssql(ctx context.Context, driver SupportedDatabase, connectionString string) (db *sqlx.DB, err error) {
	config, err := msdsn.Parse(connectionString)
	if err != nil {
		return
	}

	if config.TLSConfig == nil {
		config.TLSConfig = &tls.Config{RootCAs: certificate.GetRootCAs()}
	} else {
		config.TLSConfig.RootCAs = certificate.GetRootCAs()
	}

	db = sqlx.NewDb(sql.OpenDB(mssqldb.NewConnectorConfig(config)), driver.String())
	err = db.PingContext(ctx)

	return
}

This works, but fails when I use a query with bound parameters.

In my understanding, there are two drivers:

  • sqlserver - without querytext.ParseParams invocation
  • mssql - with querytext.ParseParams invocation (to be used with sqlx)

Method mssqldb.NewConnectorConfig returns a connector for the sqlserver driver, there is no alternative for the mssql.

Describe the solution you'd like

A function, which can create a Connector from a Config for the mssql driver (with ParseParams invocation).

Describe alternatives you've considered

I am aware of the possibility to reference a file in the connection parameters, but this does not integrate well with our ecosystem (Kubernetes, Trust Store shared by multiple containers possibly containing multiple CAs).

If this feature is rejected, I will have to implement some hack on this basis, though.

</issue_description>

Comments on the Issue (you are @copilot in this section)


📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Mar 19, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.80%. Comparing base (d8bee06) to head (936c30a).

❌ Your project status has failed because the head coverage (79.80%) is below the target coverage (80.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #341      +/-   ##
==========================================
- Coverage   79.84%   79.80%   -0.04%     
==========================================
  Files          34       34              
  Lines        6598     6600       +2     
==========================================
- Hits         5268     5267       -1     
- Misses       1060     1063       +3     
  Partials      270      270              
Files with missing lines Coverage Δ
mssql.go 87.88% <100.00%> (+0.33%) ⬆️

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…lity

Co-authored-by: shueybubbles <2224906+shueybubbles@users.noreply.github.com>
Copilot AI changed the title [WIP] Add NewConnectorConfig alternative for the mssql driver feat: add NewConnectorWithProcessQueryText for mssql driver compatibility Mar 19, 2026
Copilot AI requested a review from shueybubbles March 19, 2026 14:52
@shueybubbles
Copy link
Copy Markdown
Collaborator

@copilot there's a test failure
set GOCACHE=C:\Users\appveyor\AppData\Local\go-build
set GOCACHEPROG=
set GODEBUG=
set GOENV=C:\Users\appveyor\AppData\Roaming\go\env
set GOEXE=.exe
set GOEXPERIMENT=
set GOFIPS140=off
set GOFLAGS=
set GOGCCFLAGS=-m64 -fno-caret-diagnostics -Qunused-arguments -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=C:\Users\appveyor\AppData\Local\Temp\1\go-build2635732589=/tmp/go-build -gno-record-gcc-switches
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMOD=C:\gopath\src\github.com\microsoft\go-mssqldb\go.mod
set GOMODCACHE=c:\gopath\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=c:\gopath
set GOPRIVATE=
set GOPROXY=https://proxy.golang.org,direct
set GOROOT=c:\gopath\pkg\mod\golang.org\toolchain@v0.0.1-go1.25.7.windows-amd64
set GOSUMDB=sum.golang.org
set GOTELEMETRY=local
set GOTELEMETRYDIR=C:\Users\appveyor\AppData\Roaming\go\telemetry
set GOTMPDIR=
set GOTOOLCHAIN=auto
set GOTOOLDIR=c:\gopath\pkg\mod\golang.org\toolchain@v0.0.1-go1.25.7.windows-amd64\pkg\tool\windows_amd64
set GOVCS=
set GOVERSION=go1.25.7
set GOWORK=
set PKG_CONFIG=pkg-config
go build
go: downloading github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9
go: downloading github.com/golang-sql/sqlexp v0.1.0
go: downloading github.com/google/uuid v1.6.0
go: downloading github.com/shopspring/decimal v1.4.0
go: downloading golang.org/x/text v0.35.0
go: downloading golang.org/x/crypto v0.49.0
[reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null
[reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.SqlWmiManagement") | Out-Null
$smo = 'Microsoft.SqlServer.Management.Smo.'
$wmi = new-object ($smo + 'Wmi.ManagedComputer')
$serverName = $env:COMPUTERNAME
$instanceName = $env:SQLINSTANCE

Enable named pipes

$uri = "ManagedComputer[@name='$serverName']/ServerInstance[@name='$instanceName']/ServerProtocol[@name='Np']"
$Np = $wmi.GetSmoObject($uri)
$Np.IsEnabled = $true
$Np.Alter()
Start-Service "SQLBrowser"
Start-Service "MSSQL`$$instanceName"
Start-Sleep -Seconds 10
Waiting for service 'SQL Server (SQL2017) (MSSQL$SQL2017)' to start...
sqlcmd -S "(local)%SQLINSTANCE%" -Q "Use [master]; CREATE DATABASE test;"
Changed database context to 'master'.
sqlcmd -S "np:.%SQLINSTANCE%" -h -1 -Q "set nocount on; Select @@Version"
Microsoft SQL Server 2017 (RTM) - 14.0.1000.169 (X64)
Aug 22 2017 17:04:49
Copyright (C) 2017 Microsoft Corporation
Developer Edition (64-bit) on Windows Server 2019 Datacenter 10.0 (Build 17763: ) (Hypervisor)

pip install codecov
Collecting codecov
Downloading codecov-2.1.13-py2.py3-none-any.whl.metadata (1.3 kB)
Collecting requests>=2.7.9 (from codecov)
Downloading requests-2.32.5-py3-none-any.whl.metadata (4.9 kB)
Collecting coverage (from codecov)
Downloading coverage-7.13.5-cp312-cp312-win32.whl.metadata (8.7 kB)
Collecting charset_normalizer<4,>=2 (from requests>=2.7.9->codecov)
Downloading charset_normalizer-3.4.6-cp312-cp312-win32.whl.metadata (41 kB)
Collecting idna<4,>=2.5 (from requests>=2.7.9->codecov)
Downloading idna-3.11-py3-none-any.whl.metadata (8.4 kB)
Collecting urllib3<3,>=1.21.1 (from requests>=2.7.9->codecov)
Downloading urllib3-2.6.3-py3-none-any.whl.metadata (6.9 kB)
Collecting certifi>=2017.4.17 (from requests>=2.7.9->codecov)
Downloading certifi-2026.2.25-py3-none-any.whl.metadata (2.5 kB)
Downloading codecov-2.1.13-py2.py3-none-any.whl (16 kB)
Downloading requests-2.32.5-py3-none-any.whl (64 kB)
Downloading charset_normalizer-3.4.6-cp312-cp312-win32.whl (144 kB)
Downloading idna-3.11-py3-none-any.whl (71 kB)
Downloading urllib3-2.6.3-py3-none-any.whl (131 kB)
Downloading certifi-2026.2.25-py3-none-any.whl (153 kB)
Downloading coverage-7.13.5-cp312-cp312-win32.whl (222 kB)
Installing collected packages: urllib3, idna, coverage, charset_normalizer, certifi, requests, codecov
Successfully installed certifi-2026.2.25 charset_normalizer-3.4.6 codecov-2.1.13 coverage-7.13.5 idna-3.11 requests-2.32.5 urllib3-2.6.3
[notice] A new release of pip is available: 25.2 -> 26.0.1
[notice] To update, run: python.exe -m pip install --upgrade pip
go test -coverprofile=coverage.txt -covermode=atomic %TAGS%
go: downloading github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0
go: downloading github.com/stretchr/testify v1.11.1
go: downloading github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0
go: downloading github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1
go: downloading golang.org/x/sys v0.42.0
go: downloading github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0
go: downloading github.com/davecgh/go-spew v1.1.1
go: downloading github.com/pmezard/go-difflib v1.0.0
go: downloading github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2
go: downloading github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0
go: downloading golang.org/x/net v0.51.0
go: downloading gopkg.in/yaml.v3 v3.0.1
go: downloading github.com/kylelemons/godebug v1.1.0
go: downloading github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
go: downloading github.com/golang-jwt/jwt/v5 v5.3.1
--- FAIL: TestConnectorWithProcessQueryText (0.00s)
tds_test.go:423: 2026-03-19 14:55:51.2569557 +0000 UTC m=+20.784619901 [session.go:66]: Creating prelogin packet with connection id 'DF2B82BF-39A2-1843-B985-C30EC953F636' and activity id '00000000-0000-0000-0000-000000000000'
--- FAIL: TestConnectorWithProcessQueryText/question-mark-placeholder (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered, repanicked]
[signal 0xc0000005 code=0x0 addr=0x50 pc=0x7ff7b4340081]
goroutine 3019 [running]:
testing.tRunner.func1.2({0x7ff7b44e6c80, 0x7ff7b4b4c7b0})
c:/gopath/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.windows-amd64/src/testing/testing.go:1872 +0x239
testing.tRunner.func1()
c:/gopath/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.windows-amd64/src/testing/testing.go:1875 +0x35b
panic({0x7ff7b44e6c80?, 0x7ff7b4b4c7b0?})
c:/gopath/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.windows-amd64/src/runtime/panic.go:783 +0x132
github.com/microsoft/go-mssqldb.(*timeoutConn).Write(0x24?, {0xc000410000?, 0x0?, 0xc00059b0b0?})
C:/gopath/src/github.com/microsoft/go-mssqldb/net.go:38 +0xe1
github.com/microsoft/go-mssqldb.(*tdsBuffer).flush(0xc0000b6c00)
C:/gopath/src/github.com/microsoft/go-mssqldb/buf.go:92 +0xf7
github.com/microsoft/go-mssqldb.(*tdsBuffer).FinishPacket(...)
C:/gopath/src/github.com/microsoft/go-mssqldb/buf.go:153
github.com/microsoft/go-mssqldb.writePrelogin(0x0?, 0xc0000b6c00, 0xc000251680)
C:/gopath/src/github.com/microsoft/go-mssqldb/tds.go:274 +0x579
github.com/microsoft/go-mssqldb.connect({_, }, , {, }, {0x0, {0xc00000baa0, 0x9}, {0xc00000bad0, 0x7}, ...})
C:/gopath/src/github.com/microsoft/go-mssqldb/tds.go:1204 +0x848
github.com/microsoft/go-mssqldb.(*Driver).connect(
, {
, _}, _, {0x0, {0xc00000baa0, 0x9}, {0xc00000bad0, 0x7}, {0xc00000baac, ...}, ...})
C:/gopath/src/github.com/microsoft/go-mssqldb/mssql.go:424 +0xdc
github.com/microsoft/go-mssqldb.(*Connector).Connect(0x0?, {0x7ff7b46b02a8, 0x7ff7b4bbb360})
C:/gopath/src/github.com/microsoft/go-mssqldb/mssql_go110.go:39 +0xde
database/sql.(*DB).conn(0xc000ac2b60, {0x7ff7b46b02a8, 0x7ff7b4bbb360}, 0x1)
c:/gopath/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.windows-amd64/src/database/sql/sql.go:1431 +0x87d
database/sql.(*DB).query(0xc000ac2b60, {0x7ff7b46b02a8, 0x7ff7b4bbb360}, {0x7ff7b45a6c67, 0xb}, {0xc000096b30, 0x1, 0x1}, 0x20?)
c:/gopath/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.windows-amd64/src/database/sql/sql.go:1759 +0x57
database/sql.(*DB).QueryContext.func1(0x4d?)
c:/gopath/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.windows-amd64/src/database/sql/sql.go:1742 +0x4f
database/sql.(*DB).retry(0x7ff7b4600800?, 0xc000659db0)
c:/gopath/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.windows-amd64/src/database/sql/sql.go:1576 +0x42
database/sql.(*DB).QueryContext(0x7ff7b4a47d28?, {0x7ff7b46b02a8?, 0x7ff7b4bbb360?}, {0x7ff7b45a6c67?, 0x5b?}, {0xc000096b30?, 0x6ee?, 0x7ff7b406f420?})
c:/gopath/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.windows-amd64/src/database/sql/sql.go:1741 +0xc5
database/sql.(*DB).Query(...)
c:/gopath/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.windows-amd64/src/database/sql/sql.go:1755
github.com/microsoft/go-mssqldb.TestConnectorWithProcessQueryText.func1(0xc0001f16c0)
C:/gopath/src/github.com/microsoft/go-mssqldb/queries_test.go:2454 +0x67
testing.tRunner(0xc0001f16c0, 0xc000425860)
c:/gopath/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.windows-amd64/src/testing/testing.go:1934 +0xc3
created by testing.(*T).Run in goroutine 3017
c:/gopath/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.windows-amd64/src/testing/testing.go:1997 +0x44b

…ulate Protocols field

Co-authored-by: shueybubbles <2224906+shueybubbles@users.noreply.github.com>
Agent-Logs-Url: https://github.com/microsoft/go-mssqldb/sessions/8415cdb1-180f-4a64-94b6-0f9c6928ebe2
Copy link
Copy Markdown
Author

Copilot AI commented Mar 21, 2026

@copilot there's a test failure
set GOCACHE=C:\Users\appveyor\AppData\Local\go-build
set GOCACHEPROG=
...

The panic was caused by the test using testConnParams(t) directly to get the config. In AppVeyor, when HOST/DATABASE env vars are used (instead of SQLSERVER_DSN), the config is built manually without calling msdsn.Parse, so Protocols is empty. dialConnection loops over zero protocols, returns (nil, nil), and then newTimeoutConn(nil, ...) causes the nil pointer dereference when writePrelogin tries to flush the buffer.

Fixed in 936c30a by replacing testConnParams(t) with msdsn.Parse(makeConnStr(t).String()), which round-trips the config through the parser to ensure all fields including Protocols are populated — the same pattern real users follow.

Copilot AI requested a review from shueybubbles March 21, 2026 01:07
@shueybubbles shueybubbles marked this pull request as ready for review March 21, 2026 02:18
@shueybubbles shueybubbles merged commit 2be611f into main Mar 23, 2026
12 checks passed
@shueybubbles shueybubbles deleted the copilot/add-newconnectorconfig-alternative-mssql branch March 23, 2026 19:47
AbbasLB pushed a commit to AbbasLB/go-mssqldb that referenced this pull request Apr 10, 2026
…lity (microsoft#341)

* Initial plan

* feat: add NewConnectorWithProcessQueryText for mssql driver compatibility

Co-authored-by: shueybubbles <2224906+shueybubbles@users.noreply.github.com>

* fix: parse connection URL in TestConnectorWithProcessQueryText to populate Protocols field

Co-authored-by: shueybubbles <2224906+shueybubbles@users.noreply.github.com>
Agent-Logs-Url: https://github.com/microsoft/go-mssqldb/sessions/8415cdb1-180f-4a64-94b6-0f9c6928ebe2

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: shueybubbles <2224906+shueybubbles@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Provide NewConnectorConfig alternative for the mssql driver

4 participants