
> Zero-boilerplate SQL для Go. Опиши структуру тегами — и это всё.
Если вы пишете на Go и работаете с SQL-базами, вы знаете эту боль. Каждый CRUD-запрос — ручной SQL-строка, rows.Scan для каждого поля, Begin/Commit/Rollback вокруг записи, и постоянная синхронизация DDL-схемы с кодом. Шаблонный код не заканчивается никогда.
Это рассказ о sqlh — библиотеке, которая убирает всё это, оставаясь в «золотой середине» между raw SQL (слишком много работы) и тяжёлыми ORM (слишком много магии).
## §1. Проблема: Go + SQL = смерть от тысячи rows.Scan
Стандартный database/sql в Go отличен. Он даёт прочный, переносимый фундамент для любой SQL-базы. Но он намеренно оставляет тяжёлую работу за вами.
Вот как выглядит простой CRUD на чистом database/sql:
«`go
// 1. CREATE TABLE — raw DDL-строка
_, err := db.Exec(`CREATE TABLE IF NOT EXISTS user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE,
email TEXT,
age INTEGER
)`)
// 2. INSERT — явные placeholder и аргументы
_, err = db.Exec(
«INSERT INTO user (name, email, age) VALUES (?, ?, ?)»,
«Alice», «alice@example.com«, 30,
)
// 3. GET по ID — QueryRow + ручной Scan
var u User
err = db.QueryRow(«SELECT id, name, email, age FROM user WHERE id = ?», 1).
Scan(&u.ID, &u.Name, &u.Email, &u.Age)
// 4. LIST всех — Query + rows.Next + rows.Scan в цикле
rows, err := db.Query(«SELECT id, name, email, age FROM user ORDER BY name ASC»)
var users []User
for rows.Next() {
var u User
if err := rows.Scan(&u.ID, &u.Name, &u.Email, &u.Age); err != nil {