Quero compartilhar minha experiência desenvolvendo um programa de banco de dados de linguagem Go de demonstração simples usando o SQL Server Express 2008. Acredito que as lições aprendidas a seguir se aplicarão a qualquer versão do SQL Server de 2008 e posterior.
Meu SQL Server Express foi instalado anteriormente com o
default
instância em vez de um named
instância. Ele também foi instalado para usar a autenticação do Windows. Ambas as configurações foram exigidas por outro trabalho de desenvolvimento que faço. O outro trabalho que faço usa o SQL Server Express no mesmo PC que o aplicativo como mecanismo de banco de dados local. Eu esperava poder usar a autenticação do Windows com o SQL Server no meu aplicativo Go. Procurando um driver e um pequeno programa de exemplo para usar com um SQL Server e Go local, essa pergunta surgiu na minha pesquisa. Pensei em adicionar um pouco de informação adicional e um programa de exemplo para ajudar os outros a começar e aprender com meus erros. Também achei este artigo GoLang and MSSQL Databases:An Example útil, especialmente depois de cometer erros suficientes para que eu o entendesse melhor.
A versão final do meu programa de teste é a seguinte:
package main
import (
"fmt"
"log"
"database/sql"
_ "github.com/denisenkom/go-mssqldb" // the underscore indicates the package is used
)
func main() {
fmt.Println("starting app")
// the user needs to be setup in SQL Server as an SQL Server user.
// see create login and the create user SQL commands as well as the
// SQL Server Management Studio documentation to turn on Hybrid Authentication
// which allows both Windows Authentication and SQL Server Authentication.
// also need to grant to the user the proper access permissions.
// also need to enable TCP protocol in SQL Server Configuration Manager.
//
// you could also use Windows Authentication if you specify the fully qualified
// user id which would specify the domain as well as the user id.
// for instance you could specify "user id=domain\\user;password=userpw;".
condb, errdb := sql.Open("mssql", "server=localhost;user id=gouser;password=g0us3r;")
if errdb != nil {
fmt.Println(" Error open db:", errdb.Error())
}
defer condb.Close()
errdb = condb.Ping()
if errdb != nil {
log.Fatal(errdb)
}
// drop the database if it is there so we can recreate it
// next we will recreate the database, put a table into it,
// and add a few rows.
_, errdb = condb.Exec("drop database mydbthing")
if errdb != nil {
fmt.Println(" Error Exec db: drop db - ", errdb.Error())
}
_, errdb = condb.Exec("create database mydbthing")
if errdb != nil {
fmt.Println(" Error Exec db: create db - ", errdb.Error())
}
_, errdb = condb.Exec("use mydbthing")
if errdb != nil {
fmt.Println(" Error Exec db: using db - ", errdb.Error())
}
_, errdb = condb.Exec("create table junky (one int, two int)")
if errdb != nil {
fmt.Println(" Error Exec db: create table - ", errdb.Error())
}
_, errdb = condb.Exec("insert into junky (one, two) values (101, 201)")
if errdb != nil {
fmt.Println(" Error Exec db: insert table 1 - ", errdb.Error())
}
_, errdb = condb.Exec("insert into junky (one, two) values (102, 202)")
if errdb != nil {
fmt.Println(" Error Exec db: insert table 2 - ", errdb.Error())
}
_, errdb = condb.Exec("insert into junky (one, two) values (103, 203)")
if errdb != nil {
fmt.Println(" Error Exec db: insert table 3 - ", errdb.Error())
}
// Now that we have our database lets read some records and print them.
var (
one int
two int
)
// documentation about a simple query and results loop is at URL
// http://go-database-sql.org/retrieving.html
// we use Query() and not Exec() as we expect zero or more rows to
// be returned. only use Query() if rows may be returned.
fmt.Println (" Query our table for the three rows we inserted.")
rows, errdb := condb.Query ("select one, two from junky")
defer rows.Close()
for rows.Next() {
err:= rows.Scan (&one, &two)
if err != nil {
fmt.Println(" Error Query db: select - ", err.Error())
} else {
fmt.Printf(" - one %d and two %d\n", one, two)
}
}
rows.Close()
errdb = rows.Err()
if errdb != nil {
fmt.Println(" Error Query db: processing rows - ", errdb.Error())
}
fmt.Println("ending app")
}
Na primeira vez que o aplicativo acima for executado, após as alterações necessárias nas configurações do SQL Server, ele gerará a seguinte saída. Como o banco de dados não existe na primeira vez que o programa é executado, você verá a mensagem de erro impressa. No entanto, nas próximas vezes em que for executado, o banco de dados existirá e a mensagem de erro quando o banco de dados for descartado não será exibida.
starting app
Error Exec db: drop db - mssql: Cannot drop the database 'mydbthing', because it does not exist or you do not have permission.
Query our table for the three rows we inserted.
- one 101 and two 201
- one 102 and two 202
- one 103 and two 203
ending app
Instalando o pacote de driver do SQL Server
A primeira coisa que tive que fazer foi encontrar um pacote de driver de banco de dados que funcionasse com o SQL Server. Várias postagens de stackoverflow recomendadas
github.com/denisenkom/go-mssqldb
então foi isso que usou. Para usar o
github.com/denisenkom/go-mssqldb
eu tive que primeiro recuperá-lo do repositório do github usando go get github.com/denisenkom/go-mssqldb
da janela do shell de comando criada executando o Git Shell
. Git Shell
é o shell do github que é instalado como parte da instalação do Git. Descobri que precisava executar o go get
comando no Git Shell
para que o go
comando para encontrar o git
aplicativo e acesse o repositório do github. Quando tentei executar o go get
comando de um shell de comando normal, vi uma mensagem de erro indicando que o git
comando não pôde ser encontrado. Depois de instalar o
go-mssqldb
pacote, consegui executar meu aplicativo de exemplo e continuei executando um erro de tempo de execução do Open()
. A saída da minha aplicação foi a seguinte:starting app
Error Exec db: create db - Unable to open tcp connection with host 'localhost:1433': dial tcp 127.0.0.1:1433: connectex: No connection could be made because the target machine actively refused it.
ending app
Habilitando conexões TCP para SQL Server
Após algumas pesquisas, encontrei vários sites diferentes, todos indicando que o erro significava que minha instância do SQL Server não estava configurada para TCP/IP. As várias postagens indicaram que eu precisava usar o
Sql Server Configuration Manager
para habilitar o TCP/IP. O que descobri é que na verdade existem dois lugares onde o TCP/IP precisa ser habilitado. Um deles era
Client Protocols
e isso de fato já estava ativado. No entanto, o outro era Protocols for MSSQLSERVER
e nesse TCP/IP foi desabilitado. Então eu habilitei o TCP/IP no Protocols for MSSQLSERVER
seção e reiniciou o serviço SQL Server usando o utilitário Serviço das Ferramentas Administrativas do Painel de Controle. No entanto, eu ainda estava tendo problemas com qualquer tipo de consulta depois de usar
sql.Open()
. Eu estava vendo a saída do aplicativo que era uma variação do seguinte. A mensagem de erro era a mesma, no entanto, quando as chamadas de função tinham erros, podiam mudar de uma execução para outra. Tentei alterar a string de conexão especificada no sql.Open()
sem resultados além de mensagens de erro diferentes. starting app
Error Exec db: create db - driver: bad connection
Error Exec db: create table - driver: bad connection
ending app
Folheando mais, encontrei esta nota no repositório do github:
Problemas conhecidos
O mecanismo do SQL Server 2008 e 2008 R2 não pode manipular registros de logon quando a criptografia SSL não está desabilitada. Para corrigir o problema do SQL Server 2008 R2, instale o SQL Server 2008 R2 Service Pack 2. Para corrigir o problema do SQL Server 2008, instale o Microsoft SQL Server 2008 Service Pack 3 e o pacote de atualização cumulativa 3 para o SQL Server 2008 SP3. Mais informações:http://support.microsoft.com/kb/2653857
Então eu baixei as atualizações que eu nunca instalei. Enquanto esperava o download, pesquisei mais e encontrei a pasta contendo o executável real do SQL Server junto com o
Log
pasta contendo uma série de arquivos ERRORLOG
, ERRORLOG.1
, etc Os logs do SQL Server indicam que o usuário do SQL Server é necessário
Procurando no
ERRORLOG
encontrei um log de erros do SQL Server com os seguintes logs que forneceram a próxima peça do quebra-cabeça:2016-08-15 22:56:22.41 Server SQL Server is now ready for client connections. This is an informational message; no user action is required.
2016-08-15 23:55:47.51 Logon Error: 18456, Severity: 14, State: 58.
2016-08-15 23:55:47.51 Logon Login failed for user 'rchamber'. Reason: An attempt to login using SQL authentication failed. Server is configured for Windows authentication only. [CLIENT: 127.0.0.1]
2016-08-15 23:55:47.61 Logon Error: 18456, Severity: 14, State: 58.
2016-08-15 23:55:47.61 Logon Login failed for user 'rchamber'. Reason: An attempt to login using SQL authentication failed. Server is configured for Windows authentication only. [CLIENT: ::1]
2016-08-15 23:55:47.62 Logon Error: 18456, Severity: 14, State: 58.
2016-08-15 23:55:47.62 Logon Login failed for user 'rchamber'. Reason: An attempt to login using SQL authentication failed. Server is configured for Windows authentication only. [CLIENT: 127.0.0.1]
Percebi então que o driver Go SQL Server não estava usando a autenticação do Windows, mas sim a autenticação do SQL Server. Eu tentei usar a autenticação do Windows especificando um
user id=
vazio no entanto, isso não parecia funcionar. Então, usando o sqlcmd
utilitário, criei um usuário do SQL Server. 1> create login gouser with password='g0us3r';
2> go
1> create user gouser for login gouser;
2> go
Em seguida, baixei e instalei o Microsoft SQL Server Management Studio. Este é um utilitário diferente do SQL Server Configuration Manager. Usando isso, fiz duas coisas:(1) ativei a autenticação do SQL Server, bem como a autenticação do Windows e (2) forneci as permissões necessárias para meu novo usuário do SQL Server
gouser
. Este utilitário também forneceu uma interface de usuário agradável para navegar no SQL Server e seus vários bancos de dados. Certifique-se de que o usuário SQL que você criou tenha permissões suficientes para que possa ser usado para se conectar ao SQL Server e criar um banco de dados.
Algumas considerações para usar a autenticação do Windows
Após uma pesquisa mais aprofundada, descobri que realmente poderia usar a autenticação do Windows, no entanto, o ID de usuário completamente qualificado e sua senha devem ser fornecidos. Para um ambiente usando o Active Directory com um nome de domínio de "AD", o ID de usuário totalmente qualificado seria "AD\userid" e para o host local seria "\userid". Ainda estou pesquisando para poder usar automaticamente as credenciais do usuário conectado no momento.
Depois de pesquisar ainda mais e encontrar assistência dos desenvolvedores do driver Go, a autenticação do Windows com o atual deve ser possível se o
sql.Open()
não inclui as informações do usuário que significam "id do usuário=;senha=;" não deve ser especificado. No entanto, essa forma de autenticação automática do Windows em relação ao usuário atual só é permitida se a instância do SQL Server estiver usando Kerberos com um nome principal de serviço (SPN) válido. Se você executar uma reinicialização em sua instância do SQL Server e vir o log a seguir em seu arquivo ERRORLOG, o SQL Server não pôde inicializar com Kerberos.
2016-08-23 18:32:16.77 Servidor A biblioteca SQL Server Network Interface não pôde registrar o Service Principal Name (SPN) para o serviço SQL Server. Erro:0x54b, estado:3. Falha ao registrar um SPN pode fazer com que a autenticação integrada retorne para NTLM em vez de Kerberos. Esta é uma mensagem informativa. Ação adicional só é necessária se a autenticação Kerberos for exigida pelas políticas de autenticação.
Consulte também Como certificar-se de que você está usando a autenticação Kerberos ao criar uma conexão remota com uma instância do SQL Server 2005 que fornece algumas informações adicionais usando o
setspn
comando para corrigir o problema. Consulte também A biblioteca do SQL Network Interface não pôde registrar o SPN.
Sobre a autenticação confiável do Windows (Atualizado conforme solicitação de @Richard por @xpt)
A Autenticação do Windows está fazendo logon no SQL Server com credencial do Windows sem especificar uma ID de usuário e senha. Isso é chamado de conexão confiável para
sqlcmd
ou ODBC
; ou chamado Single-Sign-On para go-mssqldb
Vá pacote de driver. De
go-mssqldb
leia-me no github,
"user id" - insira o ID do usuário da Autenticação do SQL Server ou o ID do usuário do WindowsAuthentication no formato DOMAIN\User. No Windows, se o ID do usuário estiver vazio ou ausente, o Single Sign-On será usado.
Então, tentei as duas maneiras a seguir com meu SQL Server 2008 R2 e ambas estão funcionando bem:
condb, errdb := sql.Open("mssql", "server=MyServer;user id=;password=DONTCARE;")
condb, errdb := sql.Open("mssql", "server=MyServer;user id=;password=;")
Observe que o uso de server=localhost falharia, pois é importante ter o nome de host correto, a partir desse nome o driver está construindo o nome principal de serviço (SPN) do SQL Server kerberos e esse nome deve corresponder ao do SQL Server. Eu usei um nome principal de serviço (SPN) adequado com meu teste para que funcione.