Quiero compartir mi experiencia trabajando en un programa de base de datos de lenguaje Go de demostración simple usando SQL Server Express 2008. Creo que las siguientes lecciones aprendidas se aplicarán a cualquier versión de SQL Server a partir de 2008 y posteriores.
Mi SQL Server Express se instaló previamente con el default
instancia en lugar de un named
instancia. También se instaló para usar la autenticación de Windows. Ambas configuraciones fueron requeridas por otro trabajo de desarrollo que hago. El otro trabajo que hago usa SQL Server Express en la misma PC que la aplicación como motor de base de datos local. Esperaba poder usar la autenticación de Windows con SQL Server en mi aplicación Go.
Buscando un controlador y un pequeño programa de muestra para usar con un servidor SQL local y Go, surgió esta pregunta en mi búsqueda. Pensé en agregar un poco de información adicional y un programa de muestra para ayudar a otros a comenzar y aprender de mis errores. También encontré este artículo GoLang y bases de datos MSSQL:un ejemplo útil, especialmente después de cometer suficientes errores que lo entendí mejor.
La versión final de mi programa de prueba es la siguiente:
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")
}
La primera vez que se ejecuta la aplicación anterior una vez que se realizan los cambios necesarios en la configuración de SQL Server, generará el siguiente resultado. Dado que la base de datos no existe la primera vez que se ejecuta el programa, verá impreso el mensaje de error. Sin embargo, las veces posteriores que se ejecute, la base de datos existirá y no aparecerá el mensaje de error cuando la base de datos se elimine.
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
Instalación del paquete de controladores de SQL Server
Lo primero que tenía que hacer era encontrar un paquete de controlador de base de datos que funcionara con SQL Server. Se recomiendan varias publicaciones de stackoverflow github.com/denisenkom/go-mssqldb
así que eso es lo que usé.
Para usar github.com/denisenkom/go-mssqldb
paquete Primero tuve que recuperarlo del repositorio de github usando go get github.com/denisenkom/go-mssqldb
desde la ventana del shell de comandos creada al ejecutar Git Shell
.
Git Shell
es el shell de github que se instala como parte de la instalación de Git. Descubrí que tenía que ejecutar go get
comando en el Git Shell
para que el go
comando para encontrar el git
aplicación y acceda al repositorio de github. Cuando traté de ejecutar go get
comando desde un shell de comando normal, vi un mensaje de error que indicaba que git
no se pudo encontrar el comando.
Después de instalar go-mssqldb
paquete Pude ejecutar mi aplicación de muestra y seguí encontrando un error de tiempo de ejecución de Open()
. El resultado de mi aplicación fue el siguiente:
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
Habilitación de conexiones TCP para SQL Server
Después de algunas búsquedas, encontré varios sitios diferentes que indicaban que el error significaba que mi instancia de SQL Server no estaba configurada para TCP/IP. Las diversas publicaciones indicaron que necesitaba usar el Sql Server Configuration Manager
para habilitar TCP/IP.
Lo que descubrí es que en realidad hay dos lugares donde se debe habilitar TCP/IP. Uno era Client Protocols
y eso de hecho ya estaba habilitado. Sin embargo, el otro era Protocols for MSSQLSERVER
y en ese TCP/IP estaba deshabilitado. Así que habilité TCP/IP en los Protocols for MSSQLSERVER
y, a continuación, reinicia el servicio de SQL Server utilizando la utilidad Servicio de Herramientas administrativas desde el Panel de control.
Sin embargo, todavía tenía problemas con cualquier tipo de consulta después de usar sql.Open()
. Estaba viendo el resultado de la aplicación que era una variación de lo siguiente. El mensaje de error era el mismo, sin embargo, cuando las llamadas a funciones tenían errores, podían cambiar de una ejecución a la siguiente. Intenté cambiar la cadena de conexión especificada en sql.Open()
sin más resultados que diferentes mensajes de error.
starting app
Error Exec db: create db - driver: bad connection
Error Exec db: create table - driver: bad connection
ending app
Buscando más, encontré esta nota en el repositorio de github:
Problemas conocidos
El motor de SQL Server 2008 y 2008 R2 no puede manejar registros de inicio de sesión cuando el cifrado SSL no está deshabilitado. Para solucionar el problema de SQL Server 2008 R2, instale SQL Server 2008 R2 Service Pack 2. Para solucionar el problema de SQL Server 2008, instale Microsoft SQL Server 2008 Service Pack 3 y el paquete de actualización acumulativa 3 para SQL Server 2008 SP3. Más información:http://support.microsoft.com/kb/2653857
Así que descargué las actualizaciones que en realidad nunca instalé. Mientras esperaba la descarga, busqué más y encontré la carpeta que contenía el ejecutable real de SQL Server junto con el Log
carpeta que contiene una serie de archivos ERRORLOG
, ERRORLOG.1
, etc.
Los registros de SQL Server indican que se requiere un usuario de SQL Server
Buscando en el ERRORLOG
Encontré un registro de errores de SQL Server con los siguientes registros que proporcionaron la siguiente pieza del rompecabezas:
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]
Luego me di cuenta de que el controlador Go SQL Server no estaba usando la autenticación de Windows, sino que estaba usando la autenticación de SQL Server. Intenté usar la autenticación de Windows especificando un user id=
vacío sin embargo, eso no pareció funcionar. Entonces usando el sqlcmd
utilidad, creé un usuario de SQL Server.
1> create login gouser with password='g0us3r';
2> go
1> create user gouser for login gouser;
2> go
Luego descargué e instalé Microsoft SQL Server Management Studio. Esta es una utilidad diferente del Administrador de configuración de SQL Server. Usando esto, hice dos cosas:(1) activé la Autenticación de SQL Server así como la Autenticación de Windows y (2) brindé los permisos necesarios para mi nuevo usuario de SQL Server gouser
. Esta utilidad también proporcionó una buena interfaz de usuario para explorar SQL Server y sus diversas bases de datos.
Asegúrese de que el usuario de SQL que cree tenga permisos suficientes para que pueda usarse para conectarse a SQL Server y crear una base de datos.
Algunas consideraciones para usar la autenticación de Windows
Después de más investigaciones, descubrí que en realidad podía usar la autenticación de Windows, sin embargo, se debe proporcionar la identificación de usuario completamente calificada y su contraseña. Para un entorno que usa Active Directory con un nombre de dominio de "AD", la identificación de usuario completa sería "AD\userid" y para el host local sería "\userid". Todavía estoy investigando para poder usar automáticamente las credenciales del usuario que ha iniciado sesión actualmente.
Después de investigar más y encontrar ayuda de los desarrolladores del controlador Go, la autenticación de Windows con la versión actual debería ser posible si sql.Open()
no incluye la información del usuario que significa "id de usuario =; contraseña =;" no debe especificarse.
Sin embargo, esta forma de autenticación automática de Windows contra el usuario actual solo se permite si la instancia de SQL Server usa Kerberos con un nombre principal de servicio (SPN) válido. Si reinicia su instancia de SQL Server y ve el siguiente registro en su archivo ERRORLOG, SQL Server no pudo inicializarse con Kerberos.
2016-08-23 18:32:16.77 Servidor La biblioteca de interfaz de red de SQL Server no pudo registrar el nombre principal del servicio (SPN) para el servicio de SQL Server. Error:0x54b, estado:3. Si no se registra un SPN, es posible que la autenticación integrada recurra a NTLM en lugar de a Kerberos. Este es un mensaje informativo. Solo se requieren acciones adicionales si las políticas de autenticación requieren la autenticación Kerberos.
Consulte también Cómo asegurarse de que está utilizando la autenticación Kerberos cuando crea una conexión remota a una instancia de SQL Server 2005, que también proporciona información adicional utilizando setspn
comando para corregir el problema.
Consulte también La biblioteca de interfaz de red de SQL no pudo registrar el SPN.
Acerca de la autenticación de Windows de confianza (Actualizado según solicitud de @Richard por @xpt)
La autenticación de Windows está iniciando sesión en SQL Server con la credencial de Windows sin especificar una identificación de usuario y una contraseña. Esto se llama conexión de confianza para sqlcmd
o ODBC
; o llamado Single-Sign-On para go-mssqldb
Ir al paquete de controladores.
Desde go-mssqldb
's Léame en github,
"identificación de usuario":ingrese la identificación de usuario de autenticación de SQL Server o la identificación de usuario de autenticación de Windows en el formato DOMINIO\Usuario. En Windows, si el ID de usuario está vacío o falta, se utiliza el inicio de sesión único.
Así que probé las siguientes dos formas con mi SQL Server 2008 R2 y ambas funcionan bien:
condb, errdb := sql.Open("mssql", "server=MyServer;user id=;password=DONTCARE;")
condb, errdb := sql.Open("mssql", "server=MyServer;user id=;password=;")
Tenga en cuenta que el uso de server =localhost fallaría, ya que es importante tener el nombre de host correcto, a partir de ese nombre, el controlador está creando el nombre principal del servicio (SPN) de kerberos de SQL Server y ese nombre debe coincidir con el de SQL Server. Utilicé un nombre principal de servicio (SPN) adecuado con mi prueba para que funcione.