Code Monkey home page Code Monkey logo

go-mssqldb's Introduction

Microsoft's official Go MSSQL driver

Go Reference Build status codecov

Install

Requires Go 1.17 or above.

Install with go install github.com/microsoft/go-mssqldb@latest.

Connection Parameters and DSN

The recommended connection string uses a URL format: sqlserver://username:password@host/instance?param1=value&param2=value Other supported formats are listed below.

Common parameters

  • user id - enter the SQL Server Authentication user id or the Windows Authentication user id in the DOMAIN\User format. On Windows, if user id is empty or missing Single-Sign-On is used. The user domain sensitive to the case which is defined in the connection string.
  • password
  • database
  • connection timeout - in seconds (default is 0 for no timeout), set to 0 for no timeout. Recommended to set to 0 and use context to manage query and connection timeouts.
  • dial timeout - in seconds (default is 15 times the number of registered protocols), set to 0 for no timeout.
  • encrypt
    • strict - Data sent between client and server is encrypted E2E using TDS8.
    • disable - Data send between client and server is not encrypted.
    • false/optional/no/0/f - Data sent between client and server is not encrypted beyond the login packet. (Default)
    • true/mandatory/yes/1/t - Data sent between client and server is encrypted.
  • app name - The application name (default is go-mssqldb)
  • authenticator - Can be used to specify use of a registered authentication provider. (e.g. ntlm, winsspi (on windows) or krb5 (on linux))

Connection parameters for ODBC and ADO style connection strings

  • server - host or host\instance (default localhost)
  • port - specifies the host\instance port (default 1433). If instance name is provided but no port, the driver will use SQL Server Browser to discover the port.

Less common parameters

  • keepAlive - in seconds; 0 to disable (default is 30)
  • failoverpartner - host or host\instance (default is no partner).
  • failoverport - used only when there is no instance in failoverpartner (default 1433)
  • packet size - in bytes; 512 to 32767 (default is 4096)
  • log - logging flags (default 0/no logging, 255 for full logging)
    • 1 log errors
    • 2 log messages
    • 4 log rows affected
    • 8 trace sql statements
    • 16 log statement parameters
    • 32 log transaction begin/end
    • 64 additional debug logs
    • 128 log retries
  • TrustServerCertificate
    • false - Server certificate is checked. Default is false if encrypt is specified.
    • true - Server certificate is not checked. Default is true if encrypt is not specified. If trust server certificate is true, driver accepts any certificate presented by the server and any host name in that certificate. In this mode, TLS is susceptible to man-in-the-middle attacks. This should be used only for testing.
  • certificate - The file that contains the public key certificate of the CA that signed the SQL Server certificate. The specified certificate overrides the go platform specific CA certificates. Currently, certificates of PEM type are supported.
  • hostNameInCertificate - Specifies the Common Name (CN) in the server certificate. Default value is the server host.
  • tlsmin - Specifies the minimum TLS version for negotiating encryption with the server. Recognized values are 1.0, 1.1, 1.2, 1.3. If not set to a recognized value the default value for the tls package will be used. The default is currently 1.2.
  • ServerSPN - The kerberos SPN (Service Principal Name) for the server. Default is MSSQLSvc/host:port.
  • Workstation ID - The workstation name (default is the host name)
  • ApplicationIntent - Can be given the value ReadOnly to initiate a read-only connection to an Availability Group listener. The database must be specified when connecting with Application Intent set to ReadOnly.
  • protocol - forces use of a protocol. Make sure the corresponding package is imported.
  • columnencryption or column encryption setting - a boolean value indicating whether Always Encrypted should be enabled on the connection.
  • multisubnetfailover
    • true (Default) Client attempt to connect to all IPs simultaneously.
    • false Client attempts to connect to IPs in serial.

Connection parameters for namedpipe package

  • pipe - If set, no Browser query is made and named pipe used will be \\<host>\pipe\<pipe>
  • protocol can be set to np
  • For a non-URL DSN, the server parameter can be set to the full pipe name like \\host\pipe\sql\query

If no pipe name can be derived from the DSN, connection attempts will first query the SQL Browser service to find the pipe name for the instance.

DNS Resolution through a Custom Dialer

Custom Dialers can be used to resolve DNS if the Connection's Dialer implements the HostDialer interface. This is helpful when the dialer is proxying requests to a different, private network and the DNS record is local to the private network.

Protocol configuration

To force a specific protocol for the connection there two several options:

  1. Prepend the server name in a DSN with the protocol and a colon, like np:host or lpc:host or tcp:host
  2. Set the protocol parameter to the protocol name

msdsn.ProtocolParsers can be reordered to prioritize other protocols ahead of tcp

The admin protocol will not be used for dialing unless the connection string explicitly specifies it. Note SQL Server allows only 1 admin (or DAC) connection to be active at a time.

Kerberos Active Directory authentication outside Windows

To connect with kerberos authentication from a Linux server you can use the optional krb5 package. Imported krb alongside the main driver

package main

import (
    ...
    _ "github.com/microsoft/go-mssqldb"
    _ "github.com/microsoft/go-mssqldb/integratedauth/krb5"
)

func main() {
    ...
}

It will become available for use when the connection string parameter "authenticator=krb5" is used.

The package supports authentication via 3 methods.

  • Keytabs - Specify the username, keytab file, the krb5.conf file, and realm.

    authenticator=krb5;server=DatabaseServerName;database=DBName;user id=MyUserName;krb5-realm=domain.com;krb5-configfile=/etc/krb5.conf;krb5-keytabfile=~/MyUserName.keytab
    
  • Credential Cache - Specify the krb5.conf file path and credential cache file path.

    authenticator=krb5;server=DatabaseServerName;database=DBName;krb5-configfile=/etc/krb5.conf;krb5-credcachefile=~/MyUserNameCachedCreds 
    
  • Raw credentials - Specity krb5.confg, Username, Password and Realm.

    authenticator=krb5;server=DatabaseServerName;database=DBName;user id=MyUserName;password=foo;krb5-realm=comani.com;krb5-configfile=/etc/krb5.conf;
    

Kerberos Parameters

  • authenticator - set this to krb5 to enable kerberos authentication. If this is not present, the default provider would be ntlm for unix and winsspi for windows.
  • krb5-configfile (optional) - path to kerberos configuration file. Defaults to /etc/krb5.conf. Can also be set using KRB5_CONFIG environment variable.
  • krb5-realm (required with keytab and raw credentials) - Domain name for kerberos authentication. Omit this parameter if the realm is part of the user name like username@REALM.
  • krb5-keytabfile - path to Keytab file. Can also be set using environment variable KRB5_KTNAME. If no parameter or environment variable is set, the DefaultClientKeytabName value from the krb5 config file is used.
  • krb5-credcachefile - path to Credential cache. Can also be set using environment variable KRBCCNAME.
  • krb5-dnslookupkdc - Optional parameter in all contexts. Set to lookup KDCs in DNS. Boolean. Default is true.
  • krb5-udppreferencelimit - Optional parameter in all contexts. 1 means to always use tcp. MIT krb5 has a default value of 1465, and it prevents user setting more than 32700. Integer. Default is 1.

For further information on usage:

The connection string can be specified in one of three formats

  1. URL: with sqlserver scheme. username and password appears before the host. Any instance appears as the first segment in the path. All other options are query parameters. Examples:

    • sqlserver://username:password@host/instance?param1=value&param2=value
    • sqlserver://username:password@host:port?param1=value&param2=value
    • sqlserver://sa@localhost/SQLExpress?database=master&connection+timeout=30 // `SQLExpress instance.
    • sqlserver://sa:mypass@localhost?database=master&connection+timeout=30 // username=sa, password=mypass.
    • sqlserver://sa:mypass@localhost:1234?database=master&connection+timeout=30 // port 1234 on localhost.
    • sqlserver://sa:my%7Bpass@somehost?connection+timeout=30 // password is "my{pass" A string of this format can be constructed using the URL type in the net/url package.
    query := url.Values{}
    query.Add("app name", "MyAppName")
    
    u := &url.URL{
    	Scheme:   "sqlserver",
    	User:     url.UserPassword(username, password),
    	Host:     fmt.Sprintf("%s:%d", hostname, port),
    	// Path:  instance, // if connecting to an instance instead of a port
    	RawQuery: query.Encode(),
    }
    db, err := sql.Open("sqlserver", u.String())
  • sqlserver://username@host/instance?krb5-configfile=path/to/file&krb5-credcachefile=/path/to/cache
    • sqlserver://username@host/instance?krb5-configfile=path/to/file&krb5-realm=domain.com&krb5-keytabfile=/path/to/keytabfile
  1. ADO: key=value pairs separated by ;. Values may not contain ;, leading and trailing whitespace is ignored. Examples:

    • server=localhost\\SQLExpress;user id=sa;database=master;app name=MyAppName
    • server=localhost;user id=sa;database=master;app name=MyAppName
    • server=localhost;user id=sa;database=master;app name=MyAppName;krb5-configfile=path/to/file;krb5-credcachefile=path/to/cache;authenticator=krb5
    • 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

    • server <= addr, address, network address, data source
    • user id <= user, uid
    • database <= initial catalog
    • app name <= application name
  2. ODBC: Prefix with odbc, key=value pairs separated by ;. Allow ; by wrapping values in {}. Examples:

    • odbc:server=localhost\\SQLExpress;user id=sa;database=master;app name=MyAppName
    • odbc:server=localhost;user id=sa;database=master;app name=MyAppName
    • odbc:server=localhost;user id=sa;password={foo;bar} // Value marked with {}, password is "foo;bar"
    • odbc:server=localhost;user id=sa;password={foo{bar} // Value marked with {}, password is "foo{bar"
    • odbc:server=localhost;user id=sa;password={foobar } // Value marked with {}, password is "foobar "
    • odbc:server=localhost;user id=sa;password=foo{bar // Literal {, password is "foo{bar"
    • odbc:server=localhost;user id=sa;password=foo}bar // Literal }, password is "foo}bar"
    • odbc:server=localhost;user id=sa;password={foo{bar} // Literal {, password is "foo{bar"
    • odbc:server=localhost;user id=sa;password={foo}}bar} // Escaped } with}}`, password is "foo}bar"
    • odbc:server=localhost;user id=sa;database=master;app name=MyAppName;krb5-configfile=path/to/file;krb5-credcachefile=path/to/cache;authenticator=krb5
    • odbc: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

Azure Active Directory authentication

Azure Active Directory authentication uses temporary authentication tokens to authenticate. The mssql package does not provide an implementation to obtain tokens: instead, import the azuread package and use driver name azuresql. This driver uses azidentity to acquire tokens using a variety of credential types.

To reduce friction in local development, ActiveDirectoryDefault can authenticate as the user signed into the Azure CLI.

Run the following command to sign into the Azure CLI before running your application using the ActiveDirectoryDefault connection string parameter:

az login

Azure CLI authentication isn't recommended for applications running in Azure. More details are available via the Azure authentication with the Azure Identity module for Go tutorial.

The credential type is determined by the new fedauth connection string parameter.

  • fedauth=ActiveDirectoryServicePrincipal or fedauth=ActiveDirectoryApplication - authenticates using an Azure Active Directory application client ID and client secret or certificate. Implemented using ClientSecretCredential or CertificateCredential
    • clientcertpath=<path to certificate file>;password=<certificate password> or
    • password=<client secret>
    • user id=<application id>[@tenantid] Note the @tenantid component can be omitted if the server's tenant is the same as the application's tenant.
  • fedauth=ActiveDirectoryPassword - authenticates using a user name and password.
    • user id=username@domain
    • password=<password>
    • applicationclientid=<application id> - This guid identifies an Azure Active Directory enterprise application that the AAD admin has approved for accessing Azure SQL database resources in the tenant. This driver does not have an associated application id of its own.
  • fedauth=ActiveDirectoryDefault - authenticates using a chained set of credentials. The chain is built from EnvironmentCredential -> ManagedIdentityCredential->AzureCLICredential. See DefaultAzureCredential docs for instructions on setting up your host environment to use it. Using this option allows you to have the same connection string in a service deployment as on your interactive development machine.
  • fedauth=ActiveDirectoryManagedIdentity or fedauth=ActiveDirectoryMSI - authenticates using a system-assigned or user-assigned Azure Managed Identity.
    • user id=<identity id> - optional id of user-assigned managed identity. If empty, system-assigned managed identity is used.
    • resource id=<resource id> - optional resource id of user-assigned managed identity. If empty, system-assigned managed identity or user id are used (if both user id and resource id are provided, resource id will be used)
  • fedauth=ActiveDirectoryInteractive - authenticates using credentials acquired from an external web browser. Only suitable for use with human interaction.
    • applicationclientid=<application id> - This guid identifies an Azure Active Directory enterprise application that the AAD admin has approved for accessing Azure SQL database resources in the tenant. This driver does not have an associated application id of its own.
  • fedauth=ActiveDirectoryDeviceCode - prints a message to stdout giving the user a URL and code to authenticate. Connection continues after user completes the login separately.
  • fedauth=ActiveDirectoryAzCli - reuses local authentication the user already performed using Azure CLI.
import (
  "database/sql"
  "net/url"

  // Import the Azure AD driver module (also imports the regular driver package)
  "github.com/microsoft/go-mssqldb/azuread"
)

func ConnectWithMSI() (*sql.DB, error) {
  return sql.Open(azuread.DriverName, "sqlserver://azuresql.database.windows.net?database=yourdb&fedauth=ActiveDirectoryMSI")
}

Executing Stored Procedures

To run a stored procedure, set the query text to the procedure name:

var account = "abc"
_, err := db.ExecContext(ctx, "sp_RunMe",
	sql.Named("ID", 123),
	sql.Named("Account", sql.Out{Dest: &account}),
)

Reading Output Parameters from a Stored Procedure with Resultset

To read output parameters from a stored procedure with resultset, make sure you read all the rows before reading the output parameters:

sqltextcreate := `
CREATE PROCEDURE spwithoutputandrows
	@bitparam BIT OUTPUT
AS BEGIN
	SET @bitparam = 1
	SELECT 'Row 1'
END
`
var bitout int64
rows, err := db.QueryContext(ctx, "spwithoutputandrows", sql.Named("bitparam", sql.Out{Dest: &bitout}))
var strrow string
for rows.Next() {
	err = rows.Scan(&strrow)
}
fmt.Printf("bitparam is %d", bitout)

Caveat for local temporary tables

Due to protocol limitations, temporary tables will only be allocated on the connection as a result of executing a query with zero parameters. The following query will, due to the use of a parameter, execute in its own session, and #mytemp will be de-allocated right away:

conn, err := pool.Conn(ctx)
defer conn.Close()
_, err := conn.ExecContext(ctx, "select @p1 as x into #mytemp", 1)
// at this point #mytemp is already dropped again as the session of the ExecContext is over

To work around this, always explicitly create the local temporary table in a query without any parameters. As a special case, the driver will then be able to execute the query directly on the connection-scoped session. The following example works:

conn, err := pool.Conn(ctx)

// Set us up so that temp table is always cleaned up, since conn.Close()
// merely returns conn to pool, rather than actually closing the connection.
defer func() {
	_, _ = conn.ExecContext(ctx, "drop table #mytemp")  // always clean up
	conn.Close() // merely returns conn to pool
}()


// Since we not pass any parameters below, the query will execute on the scope of
// the connection and succeed in creating the table.
_, err := conn.ExecContext(ctx, "create table #mytemp ( x int )")

// #mytemp is now available even if you pass parameters
_, err := conn.ExecContext(ctx, "insert into #mytemp (x) values (@p1)", 1)

Return Status

To get the procedure return status, pass into the parameters a *mssql.ReturnStatus. For example:

var rs mssql.ReturnStatus
_, err := db.ExecContext(ctx, "theproc", &rs)
log.Printf("status=%d", rs)

or

var rs mssql.ReturnStatus
_, err := db.QueryContext(ctx, "theproc", &rs)
for rows.Next() {
	err = rows.Scan(&val)
}
log.Printf("status=%d", rs)

Limitation: ReturnStatus cannot be retrieved using QueryRow.

Parameters

The sqlserver driver uses normal MS SQL Server syntax and expects parameters in the sql query to be in the form of either @Name or @p1 to @pN (ordinal position).

db.QueryContext(ctx, `select * from t where ID = @ID and Name = @p2;`, sql.Named("ID", 6), "Bob")

Parameter Types

To pass specific types to the query parameters, say varchar or date types, you must convert the types to the type before passing in. The following types are supported:

  • string -> nvarchar
  • mssql.VarChar -> varchar
  • time.Time -> datetimeoffset or datetime (TDS version dependent)
  • mssql.DateTime1 -> datetime
  • mssql.DateTimeOffset -> datetimeoffset
  • "github.com/golang-sql/civil".Date -> date
  • "github.com/golang-sql/civil".DateTime -> datetime2
  • "github.com/golang-sql/civil".Time -> time
  • mssql.TVP -> Table Value Parameter (TDS version dependent)

Using an int parameter will send a 4 byte value (int) from a 32bit app and an 8 byte value (bigint) from a 64bit app. To make sure your integer parameter matches the size of the SQL parameter, use the appropriate sized type like int32 or int8.

// If this is passed directly as a parameter, 
// the SQL parameter generated would be nvarchar
name := "Bob"
// If the user_name is defined as varchar,
// it needs to be converted like this:
db.QueryContext(ctx, `select * from t2 where user_name = @p1;`, mssql.VarChar(name))
// Note: Mismatched data types on table and parameter may cause long running queries

Using Always Encrypted

The protocol and cryptography details for AE are detailed elsewhere.

Enablement

To enable AE on a connection, set the ColumnEncryption value to true on a config or pass columnencryption=true in the connection string.

Decryption and encryption won't succeed, however, without also including a decryption key provider. To avoid code size impacts on non-AE applications, key providers are not included by default.

Include the local certificate providers:

 import (
  "github.com/microsoft/go-mssqldb/aecmk/localcert"
 )

You can also instantiate a key provider directly in code and hand it to a Connector instance.

c := mssql.NewConnectorConfig(myconfig)
c.RegisterCekProvider(providerName, MyProviderType{})

Decryption

If the correct key provider is included in your application, decryption of encrypted cells happens automatically with no extra server round trips.

Encryption

Encryption of parameters passed to Exec and Query variants requires an extra round trip per query to fetch the encryption metadata. If the error returned by a query attempt indicates a type mismatch between the parameter and the destination table, most likely your input type is not a strict match for the SQL Server data type of the destination. You may be using a Go string when you need to use one of the driver-specific aliases like VarChar or NVarCharMax.

*** NOTE *** - Currently char and varchar types do not include a collation parameter component so can't be used for inserting encrypted values. #129

Local certificate AE key provider

Key provider configuration is managed separately without any properties in the connection string. The pfx provider exposes its instance as the variable PfxKeyProvider. You can give it passwords for certificates using SetCertificatePassword(pathToCertificate, path). Use an empty string or "*" as the path to use the same password for all certificates.

The MSSQL_CERTIFICATE_STORE provider exposes its instance as the variable WindowsCertificateStoreKeyProvider.

Both providers can be constrained to an allowed list of encryption key paths by appending paths to provider.AllowedLocations.

Azure Key Vault (AZURE_KEY_VAULT) key provider

Import this provider using github.com/microsoft/go-mssqldb/aecmk/akv

Constrain the provider to an allowed list of key vaults by appending vault host strings like "mykeyvault.vault.azure.net" to akv.KeyProvider.AllowedLocations.

Important Notes

  • LastInsertId should not be used with this driver (or SQL Server) due to how the TDS protocol works. Please use the OUTPUT Clause or add a select ID = convert(bigint, SCOPE_IDENTITY()); to the end of your query (ref SCOPE_IDENTITY). This will ensure you are getting the correct ID and will prevent a network round trip.
  • NewConnector may be used with OpenDB.
  • Connector.SessionInitSQL may be set to set any driver specific session settings after the session has been reset. If empty the session will still be reset but use the database defaults in Go1.10+.

Features

  • Can be used with SQL Server 2005 or newer
  • Can be used with Microsoft Azure SQL Database
  • Can be used on all go supported platforms (e.g. Linux, Mac OS X and Windows)
  • Supports new date/time types: date, time, datetime2, datetimeoffset
  • Supports string parameters longer than 8000 characters
  • Supports encryption using SSL/TLS
  • Supports SQL Server and Windows Authentication
  • Supports Single-Sign-On on Windows
  • Supports connections to AlwaysOn Availability Group listeners, including re-direction to read-only replicas.
  • Supports query notifications
  • Supports Kerberos Authentication
  • Supports handling the uniqueidentifier data type with the UniqueIdentifier and NullUniqueIdentifier go types
  • Pluggable Dialer implementations through msdsn.ProtocolParsers and msdsn.ProtocolDialers
  • A namedpipe package to support connections using named pipes (np:) on Windows
  • A sharedmemory package to support connections using shared memory (lpc:) on Windows
  • Dedicated Administrator Connection (DAC) is supported using admin protocol
  • Always Encrypted
    • MSSQL_CERTIFICATE_STORE provider on Windows
    • pfx provider on Linux and Windows

Tests

go test is used for testing. A running instance of MSSQL server is required. Environment variables are used to pass login information.

Example:

    env SQLSERVER_DSN=sqlserver://user:pass@hostname/instance?database=test1 go test

AZURESERVER_DSN environment variable provides the connection string for Azure Active Directory-based authentication. If it's not set the AAD test will be skipped.

Deprecated

These features still exist in the driver, but they are are deprecated.

Query Parameter Token Replace (driver "mssql")

If you use the driver name "mssql" (rather then "sqlserver") the SQL text will be loosly parsed and an attempt to extract identifiers using one of

  • ?
  • ?nnn
  • :nnn
  • $nnn

will be used. This is not recommended with SQL Server. There is at least one existing won't fix issue with the query parsing.

Use the native "@Name" parameters instead with the "sqlserver" driver name.

Known Issues

  • SQL Server 2008 and 2008 R2 engine cannot handle login records when SSL encryption is not disabled. To fix SQL Server 2008 R2 issue, install SQL Server 2008 R2 Service Pack 2. To fix SQL Server 2008 issue, install Microsoft SQL Server 2008 Service Pack 3 and Cumulative update package 3 for SQL Server 2008 SP3. More information: http://support.microsoft.com/kb/2653857

  • Bulk copy does not yet support encrypting column values using Always Encrypted. Tracked in #127

Contributing

This project is a fork of https://github.com/denisenkom/go-mssqldb and welcomes new and previous contributors. For more informaton on contributing to this project, please see Contributing.

For more information on the roadmap for go-mssqldb, project plans are available for viewing and discussion.

Microsoft Open Source Code of Conduct

This project has adopted the Microsoft Open Source Code of Conduct.

Resources:

go-mssqldb's People

Contributors

andybalholm avatar apoorvdeshmukh avatar bbigras avatar bhcleek avatar chris-rossi avatar denisenkom avatar dimdin avatar dlevy-msft avatar doun avatar fineol avatar forfuncsake avatar gambtho avatar gaspardle avatar glebteterin avatar hochhaus avatar jamesraybould avatar kardianos avatar mattn avatar mikewilliamson avatar nikitadef avatar petebassettbet365 avatar rudranirvan avatar shogo82148 avatar shueybubbles avatar srdan-bozovic-msft avatar tc-hib avatar vlastahajek avatar wingyplus avatar xhit avatar yukiwongky avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-mssqldb's Issues

build on linux-arm: [email protected]/tds.go:489:24: cannot convert 0xFFFFFFF8 (untyped int constant 4294967288) to type int

Describe the bug
A clear and concise description of what the bug is.

If you are seeing an exception, include the full exceptions details (message and stack trace).

Exception message:
Stack trace:

To Reproduce
Include a complete code listing that we can run to reproduce the issue.

Partial code listings, or multiple fragments of code, will slow down our response or cause us to push the issue back to you to provide code to reproduce the issue.

Expected behavior
A clear and concise description of what you expected to happen.

Further technical details

SQL Server version: (e.g. SQL Server 2017)
Operating system: (e.g. Windows 2019, Ubuntu 18.04, macOS 10.13, Docker container)
Table schema

Additional context
Add any other context about the problem here.

Extend sqlexp.MsgNotice to capture SQL notice fields like state

Is your feature request related to a problem? Please describe.
The driver discards state/class/number fields from tokenInfo.
sqlcmd needs access to those fields, per microsoft/go-sqlcmd#76
The driver should extend sqlexp.MsgNotice in a way that preserves the existing contract while provided the remaining fields to apps that know about them.

Describe the solution you'd like
Either:
Implement a new type that extends sqlexp.MsgNotice, and either:

  1. Post both the mssql-specific message type and the MsgNotice struct and rely on the client to only use one of the two, or
  2. Extend MsgNotice in a way that existing switch statements on the type will work for clients that don't know about the new type.

UniqueIdentifier MarshalText Signature

There was an open issue in the old repo about this here.
Essentially, a number of standard library functions (e.g in the encoding/json module) expect the MarshalText signature to be MarshalText() (text []byte, err error), and since the current signature of this function provided by the UniqueIdentifier type is not compatible, in order to have human readable guids you have to extend the type manually and provide a new MarshalText func.

I know this is a breaking change, but I think it is worthwhile to have consistency with the built in libraries.

TLS Handshake failed on CentOS 8 Stream when accessing Azure SQL MI

Describe the bug
No issues with CentOS 7. However, with CentOS 8 Stream, when connecting to Azure SQL MI, I'm seeing (from at least two applications):

TLS Handshake failed: cannot read handshake packet: EOF

To Reproduce

./sqlcmd -S xxxx.yyy.database.windows.net -U username

Expected behavior
TLS connection should be established.

Further technical details

SQL Server version: Azure SQL MI

prelogin fields data structure breaks when using access token for federated auth with AWS RDS Proxy MS SQL support

Describe the bug

  1. When i use connection string:

odbc:server=%s;password={%s};port=%d;fedauth=ActiveDirectoryServicePrincipalAccessToken;
A valid token as passed as the password, and an AWS RDS MS SQL Proxy configured as the server, I get this error:

2023/01/29 12:16:08 Error preparing SQL statement:federated authentication is not supported by the server

  1. Below extract from the TDS spec v33 found here:

CleanShot 2023-01-30 at 11 59 58@2x

Says that If the server received preloginFEDAUTHREQUIRED - it MUST respond as above.
  1. In the prelogin response, as there is no valid preloginFEDAUTHREQUIRED index (6) the check here passes through to the else statement. (see below for debugger screenshots)
else if fe.FedAuthLibrary != FedAuthLibraryReserved {
			return 0, fmt.Errorf("federated authentication is not supported by the server")

I worked around this issue by removing the else. I am not sure if this is a problem with this library, or if its an issue with the AWS implementation of the protocol. (see below for example dotnet program which works fine - maybe the dotnet implementation doesn't have a similar check?)

See diffs for fix:

Exception message: 2023/01/29 12:16:08 Error preparing SQL statement:federated authentication is not supported by the server

To Reproduce
Include a complete code listing that we can run to reproduce the issue.
See here for gist

Instructions as per comment in gist:

  1. Create an RDS MS SQL Server (Express is fine for cheapness)
  2. Create an RDS Proxy (plug in your requirements)
aws rds create-db-proxy \
    --db-proxy-name sqlproxy \
    --engine-family SQLSERVER  \
    --auth Description="MS SQL RDS Proxy",AuthScheme="SECRETS",SecretArn="arn:aws:secretsmanager:ap-southeast-2:1234567890:secret:rdsad5de9b2-a9be-4052-a448-ec5112025942-kyUTPL",IAMAuth="ENABLED",ClientPasswordAuthType="SQL_SERVER_AUTHENTICATION" \
    --role-arn "arn:aws:iam::1234567890:role/service-role/rds-proxy-role-1674792657645"\
    --vpc-subnet-ids "subnet-xxx" "subnet-yyy" \
    --vpc-security-group-ids sg-xxx
  1. Register your RDS DB with the proxy:
aws rds register-db-proxy-targets \
    --db-proxy-name sqlproxy \
    --db-instance-identifiers "sqlexpress"

  1. Ensure your IAM User/Role allows rds-db:connect as per AWS IAM docs
  2. Enter resulting Proxy FQDN below in server variable
  3. go run test.go

Expected behavior

I expect the following output (with my patch i get the correct output):

$ go run test.go
Connected!
Microsoft SQL Server 2019 (RTM-CU16-GDR) (KB5014353) - 15.0.4236.7 (X64) 
        May 29 2022 15:55:47 
        Copyright (C) 2019 Microsoft Corporation
        Express Edition (64-bit) on Windows Server 2016 Datacenter 10.0 <X64> (Build 14393: ) (Hypervisor)

Instead I get this output (without my patch):

$ go run query.go
Connected!
2023/01/29 12:04:02 Error preparing SQL statement:federated authentication is not supported by the server
exit status 1

Further technical details

SQL Server version: Any (SQL 2019 Express)
Operating system: Any (MacOS Ventura 13.1 M1)

Additional context

prelogin definitions are here:
https://github.com/microsoft/go-mssqldb/blob/main/tds.go#L104

.NET 6 works just fine with Microsoft.Data.SqlClient which implies to me its not an issue with the implementation of the protocol.
Working .NET code here.

Debugger screenshot prior to readPrelogin response:

CleanShot 2023-01-31 at 11 27 22@2x

And after readPrelogin (no key for preloginFEDAUTHREQUIRED):

CleanShot 2023-01-31 at 11 30 01@2x

Cannot connect to On-Premises SQL Server Express Instance

I have ported a ETL utility I wrote over from C# to Go that performs a reporting task. The C# app is able to connect to the SQL Express server just fine but the Go app is struggling to find the named instance of SQLEXPRESS.

I have used both versions:

server=<server>\<instance>;user id=<user>;password=<passw>;database=<database>;
sqlserver://<user>:<passw>@<server>/<instance>?database=<database>

...in various iterations when executing:

db, err := sql.Open("sqlserver", connString)
if err != nil {
panic(err)
}
... and in both cases I get the the following error back:

no instance matching 'SQLEXPRESS' returned from host 'localhost'

Cannot insert sql.Varchar into varchar column

Describe the bug
A clear and concise description of what the bug is.

If you are seeing an exception, include the full exceptions details (message and stack trace).

Exception message:
Stack trace:
mssql: invalid type for varchar column: mssql.VarChar

Expected behavior
mssql.VarChar should be inserted into varchar column

TLS server name not set when hostInCertificate not set

Describe the bug
I have issues with connecting to a MSSQL server using TLS. I get the error

could not ping db	{"error": "TLS Handshake failed: tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config"}

To Reproduce
using a connection string comparable to this:

sqlserver://someuser:somepass@somehost?TrustServerCertificate=false&encrypt=true

and the code

db, err := sqlx.Open("sqlserver", connectionString)
db.Ping()

I get the error shown above.

The issue can be reproduced by this test (add it to msdsn/conn_str_test.go)

func TestServerNameInTLSConfig(t *testing.T) {
	connStr := "sqlserver://someuser:somepass@somehost?TrustServerCertificate=false&encrypt=true"
	cfg, err := Parse(connStr)
	if err != nil {
		t.Errorf("Could not parse valid connection string %s: %v", connStr, err)
	}
	if cfg.TLSConfig.ServerName != "somehost" || cfg.Host != "somehost" {
		t.Errorf("Expected somehost as host %s and TLS server, but got %s", cfg.Host, cfg.TLSConfig.ServerName)
	}
}

Expected behavior
db.Ping() works

Additional context
I think the bug was introduced in fd44003

The code for setting p.Host was moved down in the parse function, resulting in an empty p.Host value at the time of the creation of p.TLSConfig.

Recent changes to tds.go mean compilation on GOARCH=386 is no longer possible

Describe the bug
Recent changes to tds.go mean compilation on GOARCH=386 is no longer possible. As far as I can tell, it is only this file currently preventing support for 386.

To Reproduce

set GOARCH=386
go build any_go-mssql_based_prog.go

Expected behavior
Compilation should be able to succeed, regardless of GOARCH

Further technical details

Line 489 (as of v0.15) reads...

nlen8 := len(s) & 0xFFFFFFF8

The len(s) infers int, which is 32 bits on x86 platforms, but 0xFFFFFFF8 does not coerce to a 32-bit int. I can change this to var nlen8 int64 = int64(len(s)) & 0xFFFFFFF8 and add some extra casts further down to resolve simple comparison mismatches, and it will compile, but there is some unsafe.Pointer math going on in/around this variable, which I don't immediately understand, and I am not confident it won't lead to a problem further down the line if I PR based on this simplistic fix.

Additional context
In the meantime, I have had to vendor the module, and cherry-pick func ucs22str from the previous version. If the pointer math is not trivial to resolve in x86, perhaps the sanest approach is to move this function out of tds.go into its own code files ucs22str.go and ucs22str_386.go and use +build tags to conditionally compile both versions -- I could PR that if it suits?

Bad placeholder counting with insert statement

A valid insert statement requiring zero placeholder values is getting rejected due to want = ds.si.NumInput() saying there should be 1 input.

CREATE TABLE [foo].[blocks]
(
    [id]    UNIQUEIDENTIFIER NOT NULL,
    [geoid] CHAR(12) NOT NULL,
    [block] GEOGRAPHY NOT NULL,

    CONSTRAINT [pk_blocks] PRIMARY KEY CLUSTERED (id ASC),
    CONSTRAINT [unq_bgeo] UNIQUE (geoid)
);
conn, err := sql.Open("mssql", connString)
conn.Exec("INSERT INTO [foo].[blocks] (id, geoid, [block]) VALUES('16a2c7de-162c-4557-9694-c249be49b4cd', 'oXRztaWFVAvD', geography::STPolyFromText('POLYGON((-85.204636 32.875304, -85.204636 32.860315, -85.185062 32.860315, -85.185062 32.875304, -85.204636 32.875304))', 4326))")
2022/10/31 11:01:31 sql: expected 1 arguments, got 0

v1.19.2
src/database/sql/convert.go L119

want = ds.si.NumInput() // reports as wanting 1 arg for a placeholder that doesn't exist

Expected behavior
The driver should report -1 or the counting bug should be fixed for geography::XX functions in statements.

Further technical details

SQL Server version: SQL Server 2022
Operating system: macOS (Docker container)

bulkcopy: mssql: type 6e not implemented when bulkcopy money field.

Describe the bug
A clear and concise description of what the bug is.

If you are seeing an exception, include the full exceptions details (message and stack trace).

[Exception message:
Stack trace:](bulkcopy: mssql: type 6e not implemented)

To Reproduce
Include a complete code listing that we can run to reproduce the issue.

Partial code listings, or multiple fragments of code, will slow down our response or cause us to push the issue back to you to provide code to reproduce the issue.

Expected behavior
A clear and concise description of what you expected to happen.

Further technical details

SQL Server version: (e.g. SQL Server 2017)
Operating system: (e.g. Windows 2019, Ubuntu 18.04, macOS 10.13, Docker container)
Table schema

Additional context
Add any other context about the problem here.

How can I use the IN keyword?

Hi guys

I need a little help for this query.

How can I fix it?

Thanks

// GetByIds ...
func (repo *brandRepository) GetByIds(ctx context.Context, ids []int) ([]*entity.Brand, error) {
	const query = "SELECT * FROM Brands WHERE Id IN(@Ids)"

	cursor, err := repo.db.QueryContext(ctx, query,
		sql.Named("Ids", ids),
	)
	if err != nil {
		return nil, err
	}
       defer cursor.Close()
       ...

Add support to read sqlcmd config file

Is your feature request related to a problem? Please describe.
To establish connection in user application, user needs to specify all the connection related properties and then establish a connection.
If the connection information can be referred by a single keyname then it simplifies specifying connection info in the application and can also be shared by just referring to the keyname

Describe the solution you'd like
The modern CLI mode of go-sqlcmd introduces a config file where it stores the information related to connection against a context name. Therefore, it is possible to just use this context name internally in driver to get the info required to establish connection.
User applications will then only need to specify the context name to establish the connection.

Describe alternatives you've considered
An alternate solution is to use application specific config file which user applications need to maintain however with sqlcmd config file it is possible to introduce a common format which all the drivers can consume.

Additional context

Add named pipes support

Similar to the .Net SqlClient behavior:

  1. Automatically infer named pipe connection from the np: prefix on the server name or from a name formatted as a UNC path like \\servername\pipe\sql\query
  2. Allow packages to register a Dialer instance associated with a protocol. EG the core package will register a tcp: dialer and a new namedpipe package will register a np: dialer during init()
  3. Enable the app to specify a global setting that is the order of which dialers to try when there is no prefix on the server name. The default will be tcp only.

unable to open tcp connection with host

I goth the below error:

2022/07/18 10:15:38 Prepare failed:unable to open tcp connection with host 'my-server:1433': dial tcp 172.16.20.185:1433: connectex: No connection could be made because the target machine actively refused it.

Though that I did the following:

  1. Installed fresh version of MSSQL dev, first time MSSQL is installed at this machine, and confirmed it is running using sqlcm

image

  1. Installed MSSQL studio (I'm to confirm MSSQLSERVER) is connected

image

  1. At Windows definder firewall, I allowed the connection of port 1433

image

  1. At SQL server network connection -> protocols for MSSQLSERVER I enabled both Named Pipes and TCP/IP

image

The code I'm trying to test is the simple one provided in the examples:

package main

import (
	"database/sql"
	"flag"
	"fmt"
	"log"

	_ "github.com/microsoft/go-mssqldb"
)

var (
	debug         = flag.Bool("debug", false, "enable debugging")
	password      = flag.String("password", "", "the database password")
	port     *int = flag.Int("port", 1433, "the database port")
	server        = flag.String("server", "my-server", "the database server") 
	user          = flag.String("user", "sa", "the database user")                  
)

func main() {
	flag.Parse()

	if *debug {
		fmt.Printf(" password:%s\n", *password)
		fmt.Printf(" port:%d\n", *port)
		fmt.Printf(" server:%s\n", *server)
		fmt.Printf(" user:%s\n", *user)
	}

	connString := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%d", *server, *user, *password, *port)
	if *debug {
		fmt.Printf(" connString:%s\n", connString)
	}
	conn, err := sql.Open("mssql", connString)
	if err != nil {
		log.Fatal("Open connection failed:", err.Error())
	}
	defer conn.Close()

	stmt, err := conn.Prepare("select 1, 'abc'")
	if err != nil {
		log.Fatal("Prepare failed:", err.Error())
	}
	defer stmt.Close()

	row := stmt.QueryRow()
	var somenumber int64
	var somechars string
	err = row.Scan(&somenumber, &somechars)
	if err != nil {
		log.Fatal("Scan failed:", err.Error())
	}
	fmt.Printf("somenumber:%d\n", somenumber)
	fmt.Printf("somechars:%s\n", somechars)

	fmt.Printf("bye\n")
}

Implement TDS 8

TDS level 8 describes a mechanism to require encryption for all protocol negotiation. SQL 2022 and eventually Azure SQL Database and Managed Instance will implement TDS 8, so the driver should be updated accordingly.

The first implementation of a TDS 8 client was in SqlClient dotnet/SqlClient#1608

Tasks

panic: not implemented makeGoLangScanType for type 240

I got this error when querying a table with geometry type๏ผŒAfter reading the code, the geometry and geography types should not be supported. As a driver library officially recognized by Microsoft, I think it should be supported. thanks

Driver hangs on empty result set

Reproducer available. Execute via ./go-mssql-repro-1 --hostname <ip> --username <username> --password <password>

When executing queries (with certain attributes, detailed below) that do return an empty result set the driver hangs, expecting to read data that isn't ever coming.

Using this query:

SELECT  TOP 50 'Top 50 queries' as Description,
	                  a.*,
	                  SUBSTRING(SqlText, (qs.statement_start_offset/2)+1,
	   	((CASE qs.statement_end_offset
	   		WHEN -1 THEN DATALENGTH(SqlText)
	   		ELSE qs.statement_end_offset
	   		END - qs.statement_start_offset)/2) + 1) as statement,
	       		qs.*,
	       		queryplan.query_plan as query_plan_ext_xml
	   FROM (SELECT DB_NAME(dbid) as [Database],
	                plan_handle,
	                UseCounts,
	                RefCounts,
	                size_in_bytes,
	                Cacheobjtype,
	                Objtype,
	                st.text as SqlText
	         FROM sys.dm_exec_cached_plans cp
	                  CROSS APPLY sys.dm_exec_sql_text(plan_handle) st
	         WHERE (LEFT(TEXT,300) LIKE '%SOME_MATCHING_TEXT%')) a
	            CROSS APPLY sys.dm_exec_query_plan(a.plan_handle) queryplan
	            INNER JOIN sys.dm_exec_query_stats qs on qs.plan_handle = a.plan_handle
	   	  WHERE queryplan.query_plan IS NOT NULL AND DATEDIFF(hour,qs.last_execution_time,GETDATE()) < 12
	         ORDER BY qs.total_elapsed_time DESC

As written the driver hangs executing that query, never to return. Removing the ORDER BY or TOP clause prevents hanging. Otherwise identical queries (with WHERE clauses that return results) do not hang.

To Reproduce
Reproducer available. Execute via ./go-mssql-repro-1 --hostname <ip> --username <username> --password <password>

Expected behavior

Expecting that empty result sets are returned successfully, with rows.Next() returning false on first call.

Further technical details

SQL Server version: SQL Server 2019
Operating system: MacOS 13.1 M1 Pro

Stack trace (from kill -3)

SIGQUIT: quit
PC=0x184381564 m=0 sigcode=0

goroutine 0 [idle]:
runtime.pthread_cond_wait(0x10327d580, 0x10327d540)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/sys_darwin.go:450 +0x20 fp=0x16d02afe0 sp=0x16d02afb0 pc=0x102e26720
runtime.semasleep(0xffffffffffffffff)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/os_darwin.go:66 +0x78 fp=0x16d02b040 sp=0x16d02afe0 pc=0x102e050c8
runtime.notesleep(0x10327d348)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/lock_sema.go:181 +0xc0 fp=0x16d02b080 sp=0x16d02b040 pc=0x102ddf620
runtime.mPark(...)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:1457
runtime.stopm()
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:2247 +0x84 fp=0x16d02b0b0 sp=0x16d02b080 pc=0x102e0eb04
runtime.findRunnable()
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:2874 +0xb10 fp=0x16d02b1a0 sp=0x16d02b0b0 pc=0x102e103b0
runtime.schedule()
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:3214 +0xa8 fp=0x16d02b1e0 sp=0x16d02b1a0 pc=0x102e113f8
runtime.park_m(0x1fa0?)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:3363 +0x138 fp=0x16d02b210 sp=0x16d02b1e0 pc=0x102e11938
runtime.mcall()
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/asm_arm64.s:192 +0x54 fp=0x16d02b220 sp=0x16d02b210 pc=0x102e346b4

goroutine 1 [select]:
runtime.gopark(0x140000d14c0?, 0x2?, 0x18?, 0x13?, 0x140000d1434?)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:363 +0xe4 fp=0x140000d12c0 sp=0x140000d12a0 pc=0x102e0a9b4
runtime.selectgo(0x140000d14c0, 0x140000d1430, 0x140000d1448?, 0x0, 0x140000d1448?, 0x1)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/select.go:328 +0x688 fp=0x140000d13e0 sp=0x140000d12c0 pc=0x102e1a738
github.com/microsoft/go-mssqldb.tokenProcessor.nextToken({0x14000076180, {0x10312b7d8, 0x14000074100}, 0x140003a4000, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, ...})
	/Users/chrislee/go/pkg/mod/github.com/microsoft/[email protected]/token.go:920 +0x118 fp=0x140000d1590 sp=0x140000d13e0 pc=0x102f96578
github.com/microsoft/go-mssqldb.(*Stmt).processQueryResponse(0x140004820f0, {0x10312b810?, 0x140000a0000?})
	/Users/chrislee/go/pkg/mod/github.com/microsoft/[email protected]/mssql.go:698 +0x1e4 fp=0x140000d1850 sp=0x140000d1590 pc=0x102f87fe4
github.com/microsoft/go-mssqldb.(*Stmt).queryContext(0x140004820f0, {0x10312b810, 0x140000a0000}, {0x1032ac3e0?, 0x1030f6000?, 0x140000021a0?})
	/Users/chrislee/go/pkg/mod/github.com/microsoft/[email protected]/mssql.go:682 +0x94 fp=0x140000d1890 sp=0x140000d1850 pc=0x102f87d94
github.com/microsoft/go-mssqldb.(*Stmt).QueryContext(0x140004820f0, {0x10312b810, 0x140000a0000}, {0x1032ac3e0, 0x0, 0x10312b810?})
	/Users/chrislee/go/pkg/mod/github.com/microsoft/[email protected]/mssql.go:1100 +0x110 fp=0x140000d1950 sp=0x140000d1890 pc=0x102f89890
database/sql.ctxDriverStmtQuery({0x10312b810, 0x140000a0000}, {0x10312b998, 0x140004820f0}, {0x1032ac3e0?, 0x0, 0x0})
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/database/sql/ctxutil.go:82 +0x98 fp=0x140000d19d0 sp=0x140000d1950 pc=0x102e8a898
database/sql.rowsiFromStatement({0x10312b810, 0x140000a0000}, {0x10312b2f8, 0x140003ae200}, 0x140000740c0, {0x0, 0x0, 0x0})
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/database/sql/sql.go:2835 +0x118 fp=0x140000d1a60 sp=0x140000d19d0 pc=0x102e90de8
database/sql.(*DB).queryDC(0x14000012080?, {0x10312b810, 0x140000a0000}, {0x10312b7d8, 0x14000074040}, 0x14000428000, 0x1400005e0d0, {0x102fba961, 0x46c}, {0x0, ...})
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/database/sql/sql.go:1787 +0x294 fp=0x140000d1c00 sp=0x140000d1a60 pc=0x102e8f714
database/sql.(*Tx).QueryContext(0x14000012080, {0x10312b810, 0x140000a0000}, {0x102fba961, 0x46c}, {0x0, 0x0, 0x0})
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/database/sql/sql.go:2510 +0x94 fp=0x140000d1c70 sp=0x140000d1c00 pc=0x102e90c74
database/sql.(*Tx).Query(...)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/database/sql/sql.go:2518
main.main()
	/Users/chrislee/IdeaProjects/repro/go-mssql-repro-hanging-result-set/main.go:73 +0x680 fp=0x140000d1f70 sp=0x140000d1c70 pc=0x102fa8470
runtime.main()
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:250 +0x24c fp=0x140000d1fd0 sp=0x140000d1f70 pc=0x102e0a5ec
runtime.goexit()
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/asm_arm64.s:1172 +0x4 fp=0x140000d1fd0 sp=0x140000d1fd0 pc=0x102e36d54

goroutine 2 [force gc (idle)]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:363 +0xe4 fp=0x14000048fa0 sp=0x14000048f80 pc=0x102e0a9b4
runtime.goparkunlock(...)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:369
runtime.forcegchelper()
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:302 +0xac fp=0x14000048fd0 sp=0x14000048fa0 pc=0x102e0a84c
runtime.goexit()
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/asm_arm64.s:1172 +0x4 fp=0x14000048fd0 sp=0x14000048fd0 pc=0x102e36d54
created by runtime.init.6
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:290 +0x24

goroutine 3 [GC sweep wait]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:363 +0xe4 fp=0x14000049770 sp=0x14000049750 pc=0x102e0a9b4
runtime.goparkunlock(...)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:369
runtime.bgsweep(0x0?)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/mgcsweep.go:278 +0xa4 fp=0x140000497b0 sp=0x14000049770 pc=0x102df7ed4
runtime.gcenable.func1()
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/mgc.go:178 +0x28 fp=0x140000497d0 sp=0x140000497b0 pc=0x102dec718
runtime.goexit()
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/asm_arm64.s:1172 +0x4 fp=0x140000497d0 sp=0x140000497d0 pc=0x102e36d54
created by runtime.gcenable
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/mgc.go:178 +0x74

goroutine 4 [GC scavenge wait]:
runtime.gopark(0x14000072000?, 0x103017bc8?, 0x1?, 0x0?, 0x0?)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:363 +0xe4 fp=0x14000049f50 sp=0x14000049f30 pc=0x102e0a9b4
runtime.goparkunlock(...)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:369
runtime.(*scavengerState).park(0x10327cc80)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/mgcscavenge.go:389 +0x5c fp=0x14000049f80 sp=0x14000049f50 pc=0x102df5e8c
runtime.bgscavenge(0x0?)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/mgcscavenge.go:617 +0x44 fp=0x14000049fb0 sp=0x14000049f80 pc=0x102df6434
runtime.gcenable.func2()
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/mgc.go:179 +0x28 fp=0x14000049fd0 sp=0x14000049fb0 pc=0x102dec6b8
runtime.goexit()
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/asm_arm64.s:1172 +0x4 fp=0x14000049fd0 sp=0x14000049fd0 pc=0x102e36d54
created by runtime.gcenable
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/mgc.go:179 +0xb8

goroutine 18 [finalizer wait]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:363 +0xe4 fp=0x14000044580 sp=0x14000044560 pc=0x102e0a9b4
runtime.goparkunlock(...)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:369
runtime.runfinq()
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/mfinal.go:180 +0x120 fp=0x140000447d0 sp=0x14000044580 pc=0x102deb940
runtime.goexit()
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/asm_arm64.s:1172 +0x4 fp=0x140000447d0 sp=0x140000447d0 pc=0x102e36d54
created by runtime.createfing
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/mfinal.go:157 +0x84

goroutine 19 [select]:
runtime.gopark(0x14000044f78?, 0x2?, 0x68?, 0x4e?, 0x14000044f74?)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:363 +0xe4 fp=0x14000044e10 sp=0x14000044df0 pc=0x102e0a9b4
runtime.selectgo(0x14000044f78, 0x14000044f70, 0x0?, 0x0, 0x0?, 0x1)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/select.go:328 +0x688 fp=0x14000044f30 sp=0x14000044e10 pc=0x102e1a738
database/sql.(*DB).connectionOpener(0x1400009ed00, {0x10312b7d8, 0x140000e22c0})
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/database/sql/sql.go:1224 +0x80 fp=0x14000044fa0 sp=0x14000044f30 pc=0x102e8d770
database/sql.OpenDB.func1()
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/database/sql/sql.go:792 +0x30 fp=0x14000044fd0 sp=0x14000044fa0 pc=0x102e8c690
runtime.goexit()
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/asm_arm64.s:1172 +0x4 fp=0x14000044fd0 sp=0x14000044fd0 pc=0x102e36d54
created by database/sql.OpenDB
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/database/sql/sql.go:792 +0x180

goroutine 5 [chan receive]:
runtime.gopark(0x140000486f8?, 0x102e5238c?, 0x60?, 0xca?, 0x102e132f8?)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:363 +0xe4 fp=0x140000486d0 sp=0x140000486b0 pc=0x102e0a9b4
runtime.chanrecv(0x14000024120, 0x0, 0x1)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/chan.go:583 +0x454 fp=0x14000048760 sp=0x140000486d0 pc=0x102ddaa14
runtime.chanrecv1(0x14000074040?, 0x0?)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/chan.go:442 +0x14 fp=0x14000048790 sp=0x14000048760 pc=0x102dda584
database/sql.(*Tx).awaitDone(0x14000012080)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/database/sql/sql.go:2187 +0x38 fp=0x140000487b0 sp=0x14000048790 pc=0x102e90238
database/sql.(*DB).beginDC.func2()
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/database/sql/sql.go:1901 +0x28 fp=0x140000487d0 sp=0x140000487b0 pc=0x102e90088
runtime.goexit()
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/asm_arm64.s:1172 +0x4 fp=0x140000487d0 sp=0x140000487d0 pc=0x102e36d54
created by database/sql.(*DB).beginDC
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/database/sql/sql.go:1901 +0x1dc

goroutine 6 [IO wait]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:363 +0xe4 fp=0x140000bb350 sp=0x140000bb330 pc=0x102e0a9b4
runtime.netpollblock(0x12a892830?, 0x72?, 0x0?)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/netpoll.go:526 +0x158 fp=0x140000bb390 sp=0x140000bb350 pc=0x102e04148
internal/poll.runtime_pollWait(0x12a892830, 0x72)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/netpoll.go:305 +0xa0 fp=0x140000bb3c0 sp=0x140000bb390 pc=0x102e31980
internal/poll.(*pollDesc).wait(0x140000b2280?, 0x1400039a000?, 0x0)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/internal/poll/fd_poll_runtime.go:84 +0x28 fp=0x140000bb3f0 sp=0x140000bb3c0 pc=0x102e79708
internal/poll.(*pollDesc).waitRead(...)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0x140000b2280, {0x1400039a000, 0x8, 0x8000})
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/internal/poll/fd_unix.go:167 +0x1e0 fp=0x140000bb470 sp=0x140000bb3f0 pc=0x102e79fd0
net.(*netFD).Read(0x140000b2280, {0x1400039a000?, 0x12a753fff?, 0x0?})
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/net/fd_posix.go:55 +0x28 fp=0x140000bb4c0 sp=0x140000bb470 pc=0x102f0c2f8
net.(*conn).Read(0x14000386000, {0x1400039a000?, 0x40?, 0x3f?})
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/net/net.go:183 +0x34 fp=0x140000bb510 sp=0x140000bb4c0 pc=0x102f164e4
github.com/microsoft/go-mssqldb.(*timeoutConn).Read(0x12a8cb060?, {0x1400039a000?, 0x14000057588?, 0x102f822e0?})
	/Users/chrislee/go/pkg/mod/github.com/microsoft/[email protected]/net.go:28 +0xa0 fp=0x140000bb540 sp=0x140000bb510 pc=0x102f8b660
io.ReadAtLeast({0x12a8cbfc8, 0x1400038e000}, {0x1400039a000, 0x8, 0x8000}, 0x8)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/io/io.go:332 +0xa0 fp=0x140000bb590 sp=0x140000bb540 pc=0x102e772c0
io.ReadFull(...)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/io/io.go:351
github.com/microsoft/go-mssqldb.(*tdsBuffer).readNextPacket(0x140003a2000)
	/Users/chrislee/go/pkg/mod/github.com/microsoft/[email protected]/buf.go:160 +0x78 fp=0x140000bb620 sp=0x140000bb590 pc=0x102f822f8
github.com/microsoft/go-mssqldb.(*tdsBuffer).Read(0x140003a2000, {0x140003b24a5, 0xf, 0x102f8f0e8?})
	/Users/chrislee/go/pkg/mod/github.com/microsoft/[email protected]/buf.go:307 +0x44 fp=0x140000bb660 sp=0x140000bb620 pc=0x102f82bc4
io.ReadAtLeast({0x103129ea0, 0x140003a2000}, {0x140003b2480, 0x34, 0x34}, 0x34)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/io/io.go:332 +0xa0 fp=0x140000bb6b0 sp=0x140000bb660 pc=0x102e772c0
io.ReadFull(...)
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/io/io.go:351
github.com/microsoft/go-mssqldb.readUcs2({0x103129ea0, 0x140003a2000}, 0x8?)
	/Users/chrislee/go/pkg/mod/github.com/microsoft/[email protected]/tds.go:654 +0x5c fp=0x140000bb700 sp=0x140000bb6b0 pc=0x102f8f10c
github.com/microsoft/go-mssqldb.readBVarChar({0x103129ea0, 0x140003a2000})
	/Users/chrislee/go/pkg/mod/github.com/microsoft/[email protected]/tds.go:693 +0x94 fp=0x140000bb740 sp=0x140000bb700 pc=0x102f8f3c4
github.com/microsoft/go-mssqldb.readBVarCharOrPanic({0x103129ea0?, 0x140003a2000?})
	/Users/chrislee/go/pkg/mod/github.com/microsoft/[email protected]/buf.go:281 +0x24 fp=0x140000bb770 sp=0x140000bb740 pc=0x102f82a84
github.com/microsoft/go-mssqldb.(*tdsBuffer).BVarChar(...)
	/Users/chrislee/go/pkg/mod/github.com/microsoft/[email protected]/buf.go:277
github.com/microsoft/go-mssqldb.parseColMetadata72(0x140003a2000?)
	/Users/chrislee/go/pkg/mod/github.com/microsoft/[email protected]/token.go:585 +0x13c fp=0x140000bb930 sp=0x140000bb770 pc=0x102f93f0c
github.com/microsoft/go-mssqldb.processSingleResponse({0x10312b7d8, 0x14000074100}, 0x140003a4000, 0x14000076180, {0x0?, 0x0?, 0x0?})
	/Users/chrislee/go/pkg/mod/github.com/microsoft/[email protected]/token.go:784 +0x39c fp=0x140000bbf80 sp=0x140000bb930 pc=0x102f9491c
github.com/microsoft/go-mssqldb.startReading.func1()
	/Users/chrislee/go/pkg/mod/github.com/microsoft/[email protected]/token.go:856 +0x40 fp=0x140000bbfd0 sp=0x140000bbf80 pc=0x102f95f20
runtime.goexit()
	/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/asm_arm64.s:1172 +0x4 fp=0x140000bbfd0 sp=0x140000bbfd0 pc=0x102e36d54
created by github.com/microsoft/go-mssqldb.startReading
	/Users/chrislee/go/pkg/mod/github.com/microsoft/[email protected]/token.go:856 +0x108

r0      0x104
r1      0x0
r2      0x400
r3      0x0
r4      0x0
r5      0xa0
r6      0x0
r7      0x0
r8      0x16d02aec8
r9      0x0
r10     0x10327d558
r11     0x2
r12     0x0
r13     0x0
r14     0x0
r15     0x0
r16     0x131
r17     0x1e45647a8
r18     0x0
r19     0x10327d540
r20     0x10327d580
r21     0x1df723b60
r22     0x0
r23     0x0
r24     0x400
r25     0x401
r26     0x500
r27     0xffffffffffffffa0
r28     0x10327cea0
r29     0x16d02af40
lr      0x1843bd638
sp      0x16d02aeb0
pc      0x184381564
fault   0x184381564

Connect to database using a workload identity

I'm currently in the process of setting up and configuring an AKS cluster with workload identities enabled.
I followed this example code to get access tokens using the MSAL library for Go.
Is there a similar strategy or way to use this access token with go-mssqldb? From what I've seen in the source code, there is no case for handling federated identities. Sorry if this is a redundant question, I'm just getting started with federated identities.

Unable to connect to a default SQLExpress Instance using Shared Memory

Summary
I am trying to connect to a default SQL Express installation in SQL Auth mode with only the Shared Memory interface available. To do this I am trying to follow the readme instructions:

To force a specific protocol for the connection there two several options:

  • Prepend the server name in a DSN with the protocol and a colon, like np:host or lpc:host or tcp:host
  • Set the protocol parameter to the protocol name

Following these instruction in the msdn.Config and manually results in the following connections string:

"sqlserver://sa:password@lpc:DESKTOP-XXXXXXX/MSSQLSERVER01?protocol=lpc"

When fed into mssql.NewConnector I get the following error:

parse "sqlserver://sa:password@lpc:DESKTOP-XXXXXXX/MSSQLSERVER01?protocol=lpc": invalid port ":DESKTOP-XXXXXXX" after host

version
get command: go get github.com/microsoft/go-mssqldb@main
go mod entry: github.com/microsoft/go-mssqldb v0.20.1-0.20230228011515-c375dfd444dd // indirect

Expected behavior
Connect via shared memory to SQL instance

Further technical details

SQL Server version: SQL Express 2022
Operating system: Windows 11

All int types scan to int64

in types.go:

// makes go/sql type instance as described below
// It should return
// the value type that can be used to scan types into. For example, the database
// column type "bigint" this should return "reflect.TypeOf(int64(0))".
func makeGoLangScanType(ti typeInfo) reflect.Type {
	switch ti.TypeId {
	case typeInt1:
		return reflect.TypeOf(int64(0))
	case typeInt2:
		return reflect.TypeOf(int64(0))
	case typeInt4:
		return reflect.TypeOf(int64(0))
	case typeInt8:
		return reflect.TypeOf(int64(0))

Perhaps it's not important for server performance like #16, but should the driver use the corresponding go types like int16, int32, int8 instead of always using int64?

Or should it rely on the caller of Scan to pass a reference to an object the specific type if they want to be right-sized?

All integer named parameters mapped to bigint

Describe the bug
Query like:

res, err := db.Query(`SELECT COUNT(*) FROM test WHERE partition_id=@partitionId`,
	sql.Named("partitionId", int(1)))

executed as:

exec sp_executesql N'SELECT * FROM test WHERE partition_id=@partitionId',N'@partitionId bigint',@partitionId=1

partitionId parameter defined as N'@partitionId bigint' even in code we are using int not int64

It's important, because plan will have implicit cast and as a result may be suboptimal plan especially when using partitions.

For example, if table was defined with partitions schema (notice that partition column is int):

DROP TABLE IF EXISTS test;
			IF EXISTS(SELECT * FROM sys.partition_schemes WHERE name='ps')
			BEGIN
				DROP PARTITION SCHEME ps;
			END
			IF EXISTS(SELECT * FROM sys.partition_functions WHERE name='pf')
			BEGIN			
				DROP PARTITION FUNCTION pf;
			END
			CREATE PARTITION FUNCTION pf (int) AS RANGE LEFT FOR VALUES (0,1,2);
			CREATE PARTITION SCHEME ps AS PARTITION pf ALL TO('PRIMARY');
			CREATE TABLE test(partition_id int, id bigint) ON ps(partition_id);
			CREATE UNIQUE CLUSTERED INDEX pk on test(partition_id, id)
			INSERT INTO test values(1,1);

Plan with ,N'@partitionId bigint' will look like (notice greater/less then comparison):
image

Plan with ,N'@partitionId int' will look like (equality comparison, expected):
imageNote: problem with partitions is only one case of problems caused by implicit cast.

To Reproduce
Run SQL profile to see what is executed:

package main

import (
	"context"
	"database/sql"
	"fmt"

	"github.com/jmoiron/sqlx"
	_ "github.com/microsoft/go-mssqldb"
)

const CONNECTION_STR = "sqlserver://sa:<yourpassword here>@127.0.0.1:1433?database=D1"

func main() {

			IF EXISTS(SELECT * FROM sys.partition_schemes WHERE name='ps')
			BEGIN
				DROP PARTITION SCHEME ps;
			END
			IF EXISTS(SELECT * FROM sys.partition_functions WHERE name='pf')
			BEGIN			
				DROP PARTITION FUNCTION pf;
			END
			CREATE PARTITION FUNCTION pf (int) AS RANGE LEFT FOR VALUES (0,1,2);
			CREATE PARTITION SCHEME ps AS PARTITION pf ALL TO('PRIMARY');
			CREATE TABLE test(partition_id int, id bigint) ON ps(partition_id);
			CREATE UNIQUE CLUSTERED INDEX pk on test(partition_id, id)
			INSERT INTO test values(1,1);
			`); err != nil {
		fmt.Println("Error", err)
		return
	}

	res, err := db.Query(`SELECT COUNT(*) FROM test WHERE partition_id=@partitionId`,
		sql.Named("partitionId", int(1)))

	if err != nil {
		fmt.Println("Error", err)
		return
	}

	res.Next()
	cnt := 0
	err = res.Scan(&cnt)
	if err != nil {
		fmt.Println("Error", err)
		return
	}

	fmt.Println(cnt)

}

In SQL profiler you could see what was executed. Run query in SSMS to see the plan

Expected behavior
It should be executed as

exec sp_executesql N'SELECT * FROM test WHERE partition_id=@partitionId',N'@partitionId int',@partitionId=1

Parameter passed as ,N'@partitionId int'

Calling ping() will block

Describe the bug
In arm v7 (arm32), calling the ping() function will cause blocking.Through log tracing, it is found that blocking occurs in Ping()->ExecContext()->s.exec(ctx, list)->s.processExec(ctx)->reader.iterateResponse()->t.nextToken()

Exception message:
Stack trace:

To Reproduce

func main() {
	var sqlDB *sql.DB
	dsn := fmt.Sprintf("sqlserver://%s:%s@%s:%d?database=%s&encrypt=disable", "sa", "xxxx", "192.168.100.245", 1433, "test")
	db, err := gorm.Open(sqlserver.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println(err)
	} else {
		sqlDB, _ = db.DB()
		// SetMaxIdleConns sets the maximum number of connections in the idle connection pool.
		sqlDB.SetMaxIdleConns(10)
		// SetMaxOpenConns sets the maximum number of open connections to the database.
		sqlDB.SetMaxOpenConns(20)
	}
	for {
		err = sqlDB.Ping()
		fmt.Println("ping status ===== ", err)
		if err != nil {
			sqlDB.Close()
			fmt.Println(err)
			return
		}
		time.Sleep(time.Second * 2)
	}
}

Expected behavior
The database will be shut down normally without blocking. Appears only when the network cable between client and server is unplugged.

Further technical details

SQL Server version: SQL Server 2008
Operating system: arm v7
go version go1.18.2 windows/amd64
build command: GOOS=linux GOARCH=arm go build

Go version 1.18 TLS 1.0 and 1.1 disabled by default client-side (sql server old TLS versions not work)

Is your feature request related to a problem? Please describe.
Yes, for the full discussion, see

denisenkom#726

In short, there are a lot of smelly, shitty, shoddy MICROSOFT Sql Servers out there, that are stuck in TLS 1.0, which is deprecated by Go.

This results in the error:

* TLS Handshake failed: tls: server selected unsupported protocol version 301

Describe the solution you'd like
The fix is to allow users of this library to specify the minimum accepted TLS version as described in

denisenkom#726 (comment)

Describe alternatives you've considered
None, the above solution is the solution, as there's no way to convince morons running MS SQLServer to upgrade their database.

Additional context
[Add any other context or screenshots about the feature request here.]

denisenkom#726 was reported in March, and yet we are here in June with no progress.
Microsoft product managers, please, clean up this mess, fast.

func (*DB) Prepare does not report malformed queries

Describe the bug
Preparing a statement accepts malformed SQL. The error surfaces only when the prepared statement is used the first time. Reading the code, it seems that the SQL never gets sent to the server when it's prepared. (I'm not a good Go programmer, so I might be wrong.)

This behaviour leads to a wrong sense of security. An application can't prepare all statements at the start to make sure they will actually run. Instead, broken SQL statements will only surface after an indeterminate time, when they are executed the first time.

To Reproduce

db, _ := sql.Open("sqlserver", "sqlserver://user:password@localhost:1433?database=database")
_, err := db.Prepare("Garbage")
if err != nil {
    fmt.Println("Never prints")
}

Expected behavior
An invalid SQL statement leads to an immediate error.

Further technical details

Using this query and playing around seems to support my theory that Prepare never actually sends the statement to the server for preparation.

Kerberos on MacOS

Hi

After installing newest version of go-mssqldb repository (from 2022-10-26 - with 3 latest kerberos update) I tried to connect to sql server via kerberos (kbr5 provider) on Mac OS Monterey.
I tried all kind of connect string, and the best I got is error provider krb5 not found.
My Azure Data Studio connection operating correctly (via /etc/krb5.conf), but golang application is unable to connect.

What I tried so far.

authenticator=krb5;server=db.address.local;database=DWH;user id=myusername;password=mypassword;realm=address.local;krb5conffile=/etc/krb5.conf;

authenticator=krb5;server=db.address.local;database=DWH;user id=myusername;realm=domain.com;krb5conffile=/etc/krb5.conf;keytabfile=~/MyUserName.keytab

authenticator=krb5;server=db.address.local;port=1433;krb5conffile=/etc/krb5.conf;krbcache=/tmp/krb5cc_1000

If I try to remove authenticator then I got user message user=. not found, etc...

What I'm missing?

Always Encrypted - get it working

  • What is the correct connection string for Always Encrypt?
  • How to integrate with Key Vault?
  • Any example to Insert or Update the data?

I have an issue with updating an encrypted field.
For example:
My table members has an column dob which is encrypted

I can exec it in Azure studio, but cannot implement in code.

DECLARE @dob varchar(10) = '2022-06-01';
Update members SET dob  = @dob where id = '123';

I always get The encryption scheme for the columns/variables is (encryption_type = 'PLAINTEXT') and the expression near line '2' expects it to be DETERMINISTIC, or PLAINTEXT.

Thanks

readPrelogin panic for invalid packet

Bug

readPrelogin methods reads the data from transport and does not check if the packet data is received as expected. Currently it only checks for empty response. If driver received invalid packets, the driver will run in to panic with out of index error.

The driver can get invalid packets if transport is broken/disconnected.

To fix this issue, a new check to be added if expected packet length is received

    struct_buf, err := ioutil.ReadAll(r)
if err != nil {
	return nil, err
}
...
if len(struct_buf) == 0 {
	return nil, errors.New("invalid empty PRELOGIN response, it must contain at least one byte")
}
offset := 0
results := map[uint8][]byte{}
for {
	rec_type := struct_buf[offset]
	if rec_type == preloginTERMINATOR {
		break
	}

	rec_offset := binary.BigEndian.Uint16(struct_buf[offset+1:]) // will panic if packet is invalid
	rec_len := binary.BigEndian.Uint16(struct_buf[offset+3:]) // will panic if packet is invalid
	value := struct_buf[rec_offset : rec_offset+rec_len] // will panic if packet is invalid
	results[rec_type] = value
	offset += 5
}

rec_type := struct_buf[offset]

AAD authentication by providing access token

Is your feature request related to a problem? Please describe.
The current AAD authentication does not allow the client to submit an auth token directly to connect to a database. Instead it provides ways to generate the token and then connect to the DB.

Describe the solution you'd like
Supply the auth token in the connection string to connect to the DB.

Describe alternatives you've considered
The existing mechanism is the alternative, however, it does not allow one to directly submit an auth token.

Additional context
None.


Issue cloned from: denisenkom#752

TestLoginTimeout is not reliable on low latency connections

AppVeyor test runs fail intermittently because TestLoginTimeout gets the wrong error type

--- FAIL: TestLoginTimeout (0.00s)
    queries_test.go:2047: Dialing host localhost with timeout 1ms
    queries_test.go:2097: Got error type *errors.errorString: unable to open tcp connection with host 'localhost:1433': dial tcp 127.0.0.1:1433: i/o timeout 
    queries_test.go:2103: wrong kind of error for login or query timeout: unable to open tcp connection with host 'localhost:1433': dial tcp 127.0.0.1:1433: i/o timeout
2022/08/19 14:02:55 [{Hello} {World} {TVP}]
FAIL
coverage: 79.2% of statements
exit status 1
FAIL	github.com/microsoft/go-mssqldb	25.273s
Command exited with code 1

We can make the test more reliable by looking for timeout in the error message.

can use `USE` switch db

Describe the bug
use USE mydb can not switch mydb,
but use sqlcmd can switch

To Reproduce
Include a complete code listing that we can run to reproduce the issue.

db.ExecContext(context.Background(), "USE mydb")
db.ExecContext(context.Background(), "SELECT db_name()")

but get dbname not is mydb,
i set

sqlDb.SetMaxOpenConns(1)
sqlDb.SetMaxIdleConns(1)

Expected behavior
A clear and concise description of what you expected to happen.

Further technical details

SQL Server version: SQL Server 2019
Operating system: Docker container
Table schema

Additional context
Add any other context about the problem here.

Please publish a stable interface (1.0) and consistent release notes

Is your feature request related to a problem? Please describe.
denisenkom/go-mssqldb has been around for a few years and does not have a 1.0 version. This fork is the successor of that library, and has had several breaking changes. Releases are being tagged (e.g. https://github.com/microsoft/go-mssqldb/tags) which means new versions are rolling out to Go users, but the Releases doesn't have any release notes for these changes.

Relying on and updating to this library is painful - more breaking changes could occur at any time, there's not a good digest of those changes, meaning that it's hard to use this library in it's

Describe the solution you'd like
Two things:

  1. Adopt a consistent release schedule and publish release notes. There's automation that can help such as ReleasePlease that can help make this easier and more streamlined.

  2. Release a 1.0 and stop pushing breaking changes without a major version bump. Add some proper e2e tests of the public API that will alert with the public API has been broken. Signal to users that they can rely on this library and don't need to worry about the API breaking suddenly.

Describe alternatives you've considered
N/A

Additional context
Add any other context or screenshots about the feature request here.

Please add custom type for BIT type

Something like that seem to work

type BitBool bool

func (b *BitBool) Value() (driver.Value, error) {
	if b == nil {
		return nil, nil
	}
	return bool(*b), nil
}

func (b *BitBool) Scan(src interface{}) error {
	if src == nil {
		return nil
	}
	v, ok := src.(bool)
	if !ok {
		return fmt.Errorf("bad bool type assertion")
	}
	*b = BitBool(v)
	return nil
}

nested result sets fail when executed in a transaction

Exception message: Invalid TDS stream: EOF or unknown token type returned

The problem is that when two or more nested result sets are used within an active transaction, the commented error occurs. If we do the same but without being in a transaction, the problem does not appear

Further technical details

SQL Server version: SQL Server 2016
Operating system: Windows Server 2019

can not build c-shared in andriod arm

Describe the bug
A clear and concise description of what the bug is.

If you are seeing an exception, include the full exceptions details (message and stack trace).

CGO_ENABLED=1 GOOS=android GOARCH=arm CC=/Users/xxx/sdk/andriod/ndk/25.1.8937393/toolchains/llvm/prebuilt/darwin-x86_64/bin/armv7a-linux-androideabi21-clang go build -buildmode=c-shared -o build/andriod/armeabi-v7a/aaa.so main.go
# github.com/microsoft/go-mssqldb
../../../go/pkg/mod/github.com/microsoft/[email protected]/ucs22str.go:23:18: constant 4294967288 overflows int

To Reproduce
Include a complete code listing that we can run to reproduce the issue.

Partial code listings, or multiple fragments of code, will slow down our response or cause us to push the issue back to you to provide code to reproduce the issue.

Expected behavior
A clear and concise description of what you expected to happen.

Further technical details

SQL Server version: (e.g. SQL Server 2017)
Operating system: (e.g. Windows 2019, Ubuntu 18.04, macOS 10.13, Docker container)
Table schema

Additional context
Add any other context about the problem here.

ColumnType methods panic when unsupported

Describe the bug

When using this driver with the go sql package, ColumnType.DatabaseTypeName, ScanType, Length, and DecimalSize panics when a column type is unsupported.

Could this return the nil values as appropriate when it's not supported, to match what is described here: "If an empty string is returned, then the driver type name is not supported."?

Current behavior means calling ColumnTypes() or related methods in the database/sql lib panics if I'm doing something like select * on a table with an unsupported column (or trying to use the driver for generic queries). Matching the spec would let clients handle the cases.

To Reproduce

func TestMakeGoLangUnsupportedType(t *testing.T) {
	makeGoLangTypeName(typeInfo{TypeId: 123})

}
// --- FAIL: TestMakeGoLangTypeName1 (0.00s)
// panic: not implemented makeGoLangTypeName for type 123 [recovered]
// 	panic: not implemented makeGoLangTypeName for type 123

Expected behavior
Instead of a panic, it should return nil values as appropriate for callers to handle.

Further technical details

The panic happens in makeGoLangTypeName, which is used in ColumnTypeScanType and ColumnTypeScanType.
The other methods can be found in types.go too.

IPV6 address support

Describe the bug
For some reason my VPN wouldn't resolve an IPv4 address for my server, just IPv6, and the test for TestConnectViaIp fails.

To Reproduce
Run the TestConnectViaIp with an ipv6-only network.

--- FAIL: TestConnectViaIp (0.00s)
    tds_test.go:341: Scan failed: parse "sqlserver://:@2001:4898:e0:bd:c525:913b:52f3:9a8a?disableRetry=false&log=127": invalid port ":9a8a" after host
2022/07/26 09:05:29 [{Hello} {World} {TVP}]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.