Go habla con las bases de datos a través de database/sql, que es la misma para todas, más un controlador para la tuya. Para MariaDB, el de MySQL: github.com/go-sql-driver/mysql.
También te puede interesar
Preparar el proyecto
go mod init ejemplo/maria go get github.com/go-sql-driver/mysql
salidago: added github.com/go-sql-driver/mysql v1.10.1
Conectar, y comprobarlo
import (
"database/sql"
"log"
"os"
_ "github.com/go-sql-driver/mysql"
)
db, err := sql.Open("mysql", os.Getenv("DSN"))
if err != nil {
log.Fatal(err)
}
defer db.Close()
if err := db.Ping(); err != nil {
log.Fatal("no se puede conectar: ", err)
}salidaconectado a MariaDB 11.8.9-MariaDB-ubu2404
El guion bajo del import carga el controlador sin usarlo directamente: se registra solo y database/sql lo encuentra por su nombre, "mysql".
sql.Open no se conecta. Solo comprueba que la cadena de conexión tenga buena forma. Con la contraseña mal o el servidor apagado, devuelve err == nil igualmente, y el fallo aparece después, en la primera consulta. Por eso va Ping() justo detrás: es lo que comprueba de verdad que hay conexión.La cadena tiene esta forma: usuario:clave@tcp(servidor:3306)/base?parseTime=true. En el ejemplo se lee de una variable de entorno, DSN; la versión anterior de esta página la escribía en el código, con usuario y contraseña.
Insertar
res, err := db.Exec("INSERT INTO usuarios (correo, nombre) VALUES (?, ?)", "ana@ejemplo.com", "Ana Pérez")
if err != nil {
log.Fatal(err)
}
id, _ := res.LastInsertId()
fmt.Println("insertado con id", id)salidainsertado con id 1
Leer varias filas
filas, err := db.Query("SELECT id, correo, nombre FROM usuarios ORDER BY id")
if err != nil {
log.Fatal(err)
}
defer filas.Close()
for filas.Next() {
var u Usuario
if err := filas.Scan(&u.ID, &u.Correo, &u.Nombre); err != nil {
log.Fatal(err)
}
fmt.Printf(" %+v\n", u)
}
if err := filas.Err(); err != nil {
log.Fatal(err)
}salida {ID:1 Correo:ana@ejemplo.com Nombre:Ana Pérez}
{ID:2 Correo:luis@ejemplo.com Nombre:Luis Gómez}Tres cosas que no se pueden saltar. defer filas.Close(): sin él, la conexión se queda ocupada y, tras unas cuantas consultas, el programa se bloquea esperando una libre. Scan con punteros, en el mismo orden que las columnas del SELECT. Y filas.Err() al final, que es lo único que dice si el bucle terminó porque no había más filas o porque se cortó la conexión a mitad.
Una sola fila, y cuando no hay
var nombre string
err = db.QueryRow("SELECT nombre FROM usuarios WHERE correo = ?", "nadie@ejemplo.com").Scan(&nombre)
fmt.Println(err, errors.Is(err, sql.ErrNoRows))salidasql: no rows in result set true
QueryRow para cuando esperas una fila. Si no hay ninguna, no es un error de la base de datos: es sql.ErrNoRows, y casi siempre hay que tratarlo aparte, como «no encontrado».
Por qué ? y no Sprintf
malicioso := "x' OR '1'='1"
db.QueryRow("SELECT COUNT(*) FROM usuarios WHERE correo = ?", malicioso).Scan(&n)
fmt.Println("con ?, el ataque devuelve:", n)
db.QueryRow(fmt.Sprintf("SELECT COUNT(*) FROM usuarios WHERE correo = '%s'", malicioso)).Scan(&n)
fmt.Println("con Sprintf, devuelve: ", n)salidacon ?, el ataque devuelve: 0 con Sprintf, devuelve: 2
Sprintf, se convierte en SQL: la condición pasa a ser OR '1'='1', siempre cierta, y devuelve todas las filas. Con un DELETE sería un desastre. Los valores van siempre como argumentos aparte, con ?.Más información en inglés: Connect to MariaDB in Golang.
Todo el código se compiló y se ejecutó con Go 1.27 en un contenedor limpio antes de publicar esta página; las salidas están copiadas de esa ejecución.