Спросите любого разработчика Go, и он скажет вам, насколько болезненно писать программы, которые взаимодействуют с базами данных SQL. Go ,кажется, даже наполовину менее продуктивен по сравнению с такими инструментами, как SQLAlchemy, Diesel, Hibernate или ActiveRecord. Существующие инструменты в экосистеме Go вынуждают разработчиков приложений вручную писать функции сопоставления (map) или засорять свой код небезопасными пустыми интерфейсами.
Введение в sqlc
Я уже много лет чувствую эту боль, поэтому полгода назад я начал разрабатывать решение. Сегодня я рад объявить о результатах этой работы. sqlc - это новый инструмент, который превращает работу с SQL в Go в удовольствие.
Это значительно улучшает опыт разработчиков при работе с реляционными базами данных без ущерба для безопасности типов или производительности во время выполнения. Он не использует структурные теги, написанные вручную функции сопоставления, ненужное отражение и не добавляет какие-либо новые зависимости в ваш код. Фактически, он даже обеспечивает гарантии правильности и безопасности, с которыми не может сравниться ни один другой набор инструментов SQL в экосистеме Go.
sqlc выполняет все это, используя принципиально иной подход: компиляцию SQL в полностью типобезопасный идиоматический код Go. sqlc может принимать этот SQL:
CREATE TABLE authors (
id BIGSERIAL PRIMARY KEY,
name text NOT NULL,
bio text
);
-- name: GetAuthor :one
SELECT * FROM authors
WHERE id = $1 LIMIT 1;
и автоматически сгенерирует следующий код Go.
package db
import (
"context"
"database/sql"
)
type Author struct {
ID int64
Name string
Bio sql.NullString
}
const getAuthor = `-- name: GetAuthor :one
SELECT id, name, bio FROM authors
WHERE id = $1 LIMIT 1
`
type Queries struct {
db *sql.DB
}
func (q *Queries) GetAuthor(ctx context.Context, id int64) (Author, error) {
row := q.db.QueryRowContext(ctx, getAuthor, id)
var i Author
err := row.Scan(&i.ID, &i.Name, &i.Bio)
return i, err
}
В течение многих лет инженеры-программисты генерировали SQL-запросы из аннотированных объектов на языках программирования. SQL уже является структурированным типизированным языком; мы должны генерировать правильный типобезопасный код на каждом языке программирования из источника истины: самого SQL.
Проблемы с использованием SQL от Go сегодня
Работать с реляционными базами данных на любом языке программирования сложно. В Go особенно больно. Даже с существующими пакетами и инструментами написание и поддержка запросов в приложении Go - это рутинная работа.
Стандартная библиотека
Стандартная библиотека Go предлагает пакет database/sql для взаимодействия с реляционными базами данных, но большинство приложений перерастают пакет database/sql и достигают полноценной ORM или абстракции библиотеки более высокого уровня. Зачем?
Использовать пакет database/sql просто. Напишите запрос, передайте необходимые аргументы и отсканируйте результаты обратно в поля. Программисты несут ответственность за явное указание соответствия между полем SQL и его значением в программе как для входов, так и для выходов.
Если в приложении имеется несколько запросов, поддержание этих сопоставлений становится обременительным и серьезно влияет на производительность программиста. Хуже того, легко совершать ошибки, которые не обнаруживаются до выполнения. Если вы измените порядок параметров в запросе, необходимо обновить отображение параметров. Если в таблицу добавлен столбец, все запросы должны быть обновлены, чтобы вернуть это значение. Если тип в SQL не соответствует типу в вашем коде, сбои не возникнут до времени выполнения запроса.
Высокоуровневые библиотеки
Сообщество Go разработало библиотеки более высокого уровня (github.com/jmoiron/sqlx) и ORM (github.com/jinzhu/gorm) для решения этих проблем. Однако библиотеки более высокого уровня по-прежнему требуют сопоставления вручную с помощью текстовых и структурных тегов запроса, которые, если они неверны, не сработают только во время выполнения.
ORM устраняет большую часть ручного сопоставления, но требует, чтобы вы теперь писали свои запросы в псевдо-sql DSL, который в основном заново изобретает SQL в наборе вызовов функций Go.
Неверный SQL
При любом подходе по-прежнему легко допускать ошибки, которые компилятор не может проверить. Как программист на Go вы когда-нибудь:
- Изменили порядок аргументов при вызове запроса, поэтому они не совпадают с текстом SQL
- Обновили имя столбца в одном запросе, а не в другом
- Ввели неверное имя столбца в запросе
- Изменили количество аргументов в запросе, но забыли передать дополнительные значения
- Изменили тип столбца, но забыли изменить тип в коде?
Все эти ошибки невозможны с sqlc. Как так?
Как использовать sqlc. 3 шага
- Запускаете sqlc для генерации кода Go, который представляет типобезопасные интерфейсы для этих запросов.
- Вы запускаете sqlc для генерации кода Go, который представляет типобезопасные интерфейсы для этих запросов.
- Пишете код приложения, который вызывает методы, сгенерированые sqlc
Серьезно, это так просто. Вам больше не придется писать какой-либо шаблонный код запросов SQL. sqlc генерирует полностью безопасный идиоматический код Go из ваших запросов. sqlc также предотвращает целые классы распространенных ошибок в коде SQL.
Во время генерации кода sqlc анализирует все ваши запросы и операторы DDL (например, CREATE TABLE), чтобы знать имена и типы каждого столбца в ваших таблицах и каждого выражения в ваших запросах. Если какие-либо из них не совпадают, sqlc не сможет скомпилировать ваши запросы, выявляя возможные ошибки времени выполнения до того, как они произойдут.
Точно так же методы, которые sqlc генерирует для вас, имеют строгую арность и правильные определения типа Go, соответствующие вашим столбцам. Поэтому, если вы измените аргументы запроса или тип столбца, но не обновите свой код, он не сможет скомпилироваться.
Оригинальная статья https://conroy.org/introducing-sqlc .
Руководства по установке и дополнительные примеры можно посмотреть там же.