denisenkom/go-mssqldb
GitHub: denisenkom/go-mssqldb
该项目是一个纯 Go 实现的 Microsoft SQL Server 驱动,为 Go 应用提供通过标准 database/sql 接口连接和操作 SQL Server 数据库的能力。
Stars: 1883 | Forks: 523
# 一个适用于 Go 的 database/sql 包的纯 Go MSSQL 驱动
[](https://pkg.go.dev/github.com/denisenkom/go-mssqldb)
[](https://ci.appveyor.com/project/denisenkom/go-mssqldb)
[](https://codecov.io/gh/denisenkom/go-mssqldb)
有关更多最新更新,请查看 [Microsoft fork](https://github.com/microsoft/go-mssqldb)。
## 安装
需要 Go 1.8 或更高版本。
使用 `go get github.com/denisenkom/go-mssqldb` 进行安装。
## 连接参数和 DSN
推荐的连接字符串使用 URL 格式:
`sqlserver://username:password@host/instance?param1=value¶m2=value`
下面列出了其他支持的格式。
### 常用参数
* `user id` - 输入 SQL Server 身份验证的用户 ID,或格式为 DOMAIN\User 的 Windows 身份验证用户 ID。在 Windows 上,如果用户 ID 为空或缺失,则使用 Single-Sign-On。用户域对连接字符串中定义的大小写敏感。
* `password`
* `database`
* `connection timeout` - 以秒为单位(默认为 0 表示无超时),设置为 0 表示无超时。建议设置为 0 并使用 context 来管理查询和连接超时。
* `dial timeout` - 以秒为单位(默认为 15),设置为 0 表示无超时
* `encrypt`
* `disable` - 客户端和服务器之间发送的数据不加密。
* `false` - 客户端和服务器之间发送的数据在登录包之外不进行加密。(默认)
* `true` - 客户端和服务器之间发送的数据被加密。
* `app name` - 应用程序名称(默认为 go-mssqldb)
### 用于 ODBC 和 ADO 风格连接字符串的连接参数
* `server` - host 或 host\instance(默认为 localhost)
* `port` - 仅在 server 中没有 instance 时使用(默认为 1433)
### 不常用参数
* `keepAlive` - 以秒为单位;0 为禁用(默认为 30)
* `failoverpartner` - host 或 host\instance(默认为无伙伴)。
* `failoverport` - 仅在 failoverpartner 中没有 instance 时使用(默认为 1433)
* `packet size` - 以字节为单位;512 到 32767(默认为 4096)
* 加密连接的最大数据包大小为 16383 字节
* 有关用法的更多信息:
* `log` - 日志标志(默认为 0/无日志,63 为完整日志)
* 1 记录错误
* 2 记录消息
* 4 记录受影响的行数
* 8 追踪 sql 语句
* 16 记录语句参数
* 32 记录事务开始/结束
* `TrustServerCertificate`
* false - 检查服务器证书。如果指定了 encrypt,则默认为 false。
* true - 不检查服务器证书。如果未指定 encrypt,则默认为 true。如果 trust server certificate 为 true,驱动程序将接受服务器提供的任何证书以及该证书中的任何主机名。在此模式下,TLS 容易受到中间人攻击。这应该仅用于测试。
* `certificate` - 包含签署了 SQL Server 证书的 CA 公钥证书的文件。指定的证书将覆盖 go 平台特定的 CA 证书。
* `hostNameInCertificate` - 指定服务器证书中的通用名称 (CN)。默认值为服务器主机。
* `ServerSPN` - 服务器的 kerberos SPN (Service Principal Name)。默认为 MSSQLSvc/host:port。
* `Workstation ID` - 工作站名称(默认为主机名)
* `ApplicationIntent` - 可以给定值 `ReadOnly` 以发起与可用性组侦听器的只读连接。当在 `Application Intent` 设置为 `ReadOnly` 的情况下进行连接时,必须指定 `database`。
### 连接字符串可以指定为以下三种格式之一
1. URL:使用 `sqlserver` 方案。username 和 password 出现在 host 之前。任何 instance 显示为
path 中的第一段。所有其他选项均为查询参数。示例:
* `sqlserver://username:password@host/instance?param1=value¶m2=value`
* `sqlserver://username:password@host:port?param1=value¶m2=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"
可以使用 `net/url` 包中的 `URL` 类型构造此格式的字符串。
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())
2. ADO:以 `;` 分隔的 `key=value` 键值对。值不能包含 `;`,前导和尾随空格将被忽略。
示例:
* `server=localhost\\SQLExpress;user id=sa;database=master;app name=MyAppName`
* `server=localhost;user id=sa;database=master;app name=MyAppName`
ADO 字符串支持 database、app name、user id 和 server 的同义词
* server <= addr, address, network address, data source
* user id <= user, uid
* database <= initial catalog
* app name <= application name
3. ODBC:以 `odbc` 为前缀,以 `;` 分隔的 `key=value` 键值对。通过将值包裹在
`{}` 中来允许包含 `;`。示例:
* `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}` // 使用 `{}` 标记的值,密码为 "foo;bar"
* `odbc:server=localhost;user id=sa;password={foo{bar}` // 使用 `{}` 标记的值,密码为 "foo{bar"
* `odbc:server=localhost;user id=sa;password={foobar }` // 使用 `{}` 标记的值,密码为 "foobar "
* `odbc:server=localhost;user id=sa;password=foo{bar` // 字面量 `{`,密码为 "foo{bar"
* `odbc:server=localhost;user id=sa;password=foo}bar` // 字面量 `}`,密码为 "foo}bar"
* `odbc:server=localhost;user id=sa;password={foo{bar}` // 字面量 `{`,密码为 "foo{bar"
* `odbc:server=localhost;user id=sa;password={foo}}bar}` // 转义的 `} with`}}`,密码为 "foo}bar"
### Azure Active Directory 身份验证
Azure Active Directory 身份验证使用临时身份验证令牌进行身份验证。
`mssql` 包不提供获取令牌的实现:相反,请导入 `azuread` 包并使用驱动程序名称 `azuresql`。此驱动程序使用 [azidentity](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#section-readme) 通过各种凭据类型来获取令牌。
凭据类型由新的 `fedauth` 连接字符串参数决定。
* `fedauth=ActiveDirectoryServicePrincipal` 或 `fedauth=ActiveDirectoryApplication` - 使用 Azure Active Directory 应用程序客户端 ID 和客户端密钥或证书进行身份验证。通过 [ClientSecretCredential 或 CertificateCredential](https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/azidentity#authenticating-service-principals) 实现
* `clientcertpath=<证书文件路径>;password=<证书密码>` 或
* `password=<客户端密钥>`
* `user id=<应用程序 ID>[@tenantid]` 注意,如果服务器的租户与应用程序的租户相同,则可以省略 `@tenantid` 部分。
* `fedauth=ActiveDirectoryPassword` - 使用用户名和密码进行身份验证。
* `user id=username@domain`
* `password=<密码>`
* `applicationclientid=<应用程序 ID>` - 此 GUID 标识了 AAD 管理员已批准其访问租户中 Azure SQL 数据库资源的 Azure Active Directory 企业应用程序。此驱动程序本身没有关联的应用程序 ID。
* `fedauth=ActiveDirectoryDefault` - 使用链式凭据集进行身份验证。该链由 EnvironmentCredential -> ManagedIdentityCredential->AzureCLICredential 构建。有关设置主机环境以使用它的说明,请参阅 [DefaultAzureCredential 文档](https://github.com/Azure/azure-sdk-for-go/wiki/Set-up-Your-Environment-for-Authentication#configure-defaultazurecredential)。使用此选项可以让您的服务部署与交互式开发机器具有相同的连接字符串。
* `fedauth=ActiveDirectoryManagedIdentity` 或 `fedauth=ActiveDirectoryMSI` - 使用系统分配或用户分配的 Azure 托管标识进行身份验证。
* `user id=<标识 ID>` - 用户分配的托管标识的可选 ID。如果为空,则使用系统分配的托管标识。
* `fedauth=ActiveDirectoryInteractive` - 使用从外部 Web 浏览器获取的凭据进行身份验证。仅适用于人工交互。
* `applicationclientid=<应用程序 ID>` - 此 GUID 标识了 AAD 管理员已批准其访问租户中 Azure SQL 数据库资源的 Azure Active Directory 企业应用程序。此驱动程序本身没有关联的应用程序 ID。
```
import (
"database/sql"
"net/url"
// Import the Azure AD driver module (also imports the regular driver package)
"github.com/denisenkom/go-mssqldb/azuread"
)
func ConnectWithMSI() (*sql.DB, error) {
return sql.Open(azuread.DriverName, "sqlserver://azuresql.database.windows.net?database=yourdb&fedauth=ActiveDirectoryMSI")
}
```
## 执行存储过程
要运行存储过程,请将查询文本设置为该过程的名称:
```
var account = "abc"
_, err := db.ExecContext(ctx, "sp_RunMe",
sql.Named("ID", 123),
sql.Named("Account", sql.Out{Dest: &account}),
)
```
## 从带有结果集的存储过程中读取输出参数
要从带有结果集的存储过程中读取输出参数,请确保在读取输出参数之前读取所有行:
```
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)
```
## 本地临时表的注意事项
由于协议限制,只有在执行参数为零的查询时,才会在连接上分配临时表。由于使用了参数,以下查询将在其自己的会话中执行,并且 `#mytemp` 将被立即取消分配:
```
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
```
要解决此问题,请始终在没有参数的查询中显式创建本地临时表。作为一种特殊情况,驱动程序随后将能够直接在连接作用域的会话上执行该查询。以下示例有效:
```
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)
```
## 返回状态
要获取过程返回状态,请在参数中传入一个 `*mssql.ReturnStatus`。例如:
```
var rs mssql.ReturnStatus
_, err := db.ExecContext(ctx, "theproc", &rs)
log.Printf("status=%d", rs)
```
或
```
var rs mssql.ReturnStatus
_, err := db.QueryContext(ctx, "theproc", &rs)
for rows.Next() {
err = rows.Scan(&val)
}
log.Printf("status=%d", rs)
```
限制:无法使用 `QueryRow` 检索 ReturnStatus。
## 参数
`sqlserver` 驱动程序使用普通的 MS SQL Server 语法,并期望 sql 查询中的参数采用 `@Name` 或 `@p1` 到 `@pN`(顺序位置)的形式。
```
db.QueryContext(ctx, `select * from t where ID = @ID and Name = @p2;`, sql.Named("ID", 6), "Bob")
```
### 参数类型
要将特定类型(例如 `varchar` 或 `date` 类型)传递给查询参数,必须在传入之前将类型转换为该类型。支持以下类型:
* string -> nvarchar
* mssql.VarChar -> varchar
* time.Time -> datetimeoffset 或 datetime(取决于 TDS 版本)
* 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 版本)
## 重要说明
* 由于 TDS 协议的工作方式,[LastInsertId](https://golang.org/pkg/database/sql/#Result.LastInsertId) 不应与此驱动程序(或 SQL Server)一起使用。请使用 [OUTPUT Clause](https://docs.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql) 或在查询末尾添加 `select ID = convert(bigint, SCOPE_IDENTITY());`(参考 [SCOPE_IDENTITY](https://docs.microsoft.com/en-us/sql/t-sql/functions/scope-identity-transact-sql))。
这将确保您获得正确的 ID 并防止网络往返。
* [NewConnector](https://godoc.org/github.com/denisenkom/go-mssqldb#NewConnector) 可以与 [OpenDB](https://golang.org/pkg/database/sql/#OpenDB) 一起使用。
* [Connector.SessionInitSQL](https://godoc.org/github.com/denisenkom/go-mssqldb#Connector.SessionInitSQL) 可用于在会话重置后设置任何特定于驱动程序的会话设置。如果为空,会话仍将被重置,但在 Go1.10+ 中将使用数据库默认值。
## 功能
* 可与 SQL Server 2005 或更高版本一起使用
* 可与 Microsoft Azure SQL Database 一起使用
* 可在 go 支持的平台上使用(例如 Linux、Mac OS X 和 Windows)
* 支持新的日期/时间类型:date、time、datetime2、datetimeoffset
* 支持长度超过 8000 个字符的字符串参数
* 支持使用 SSL/TLS 进行加密
* 支持 SQL Server 和 Windows 身份验证
* 在 Windows 上支持 Single-Sign-On
* 支持连接到 AlwaysOn 可用性组侦听器,包括重定向到只读副本。
* 支持查询通知
## 测试
`go test` 用于测试。需要运行中的 MSSQL server 实例。
环境变量用于传递登录信息。
示例:
```
env SQLSERVER_DSN=sqlserver://user:pass@hostname/instance?database=test1 go test
```
`AZURESERVER_DSN` 环境变量提供用于基于 Azure Active Directory 的身份验证的连接字符串。如果未设置,将跳过 AAD 测试。
## 已弃用
这些功能仍然存在于驱动程序中,但它们已被弃用。
### 查询参数标记替换(驱动程序 "mssql")
如果您使用驱动程序名称 "mssql"(而不是 "sqlserver"),SQL 文本将被松散地解析,并尝试使用以下之一来提取标识符:
* ?
* ?nnn
* :nnn
* $nnn
这不推荐用于 SQL Server。
查询解析中至少存在一个现有的 `won't fix` 问题。
请在 "sqlserver" 驱动程序名称中改用原生的 "@Name" 参数。
## 已知问题
* 当未禁用 SSL 加密时,SQL Server 2008 和 2008 R2 引擎无法处理登录记录。
要修复 SQL Server 2008 R2 问题,请安装 SQL Server 2008 R2 Service Pack 2。
要修复 SQL Server 2008 问题,请安装 Microsoft SQL Server 2008 Service Pack 3 和 Cumulative update package 3 for SQL Server 2008 SP3。
更多信息:
标签:EVTX分析, Go, HTTP, Ruby工具, SQL Server, 底层编程, 开发库, 数据库连接, 数据库驱动, 日志审计