# Введение

> Что делает эта библиотека, чего она принципиально не делает и почему разница важна.

Source: https://ormgo.vercel.app/ru/docs/introduction/
Symbols: https://ormgo.vercel.app/api/orm.txt — the generated list of every exported name.

---
## Тезис

> Структуры — ваши. Схема — PostgreSQL. Генератор доказывает, что они совпадают.

Большинство Go-мапперов выбирают сторону. Либо структуры — источник истины, и схема порождается из них, либо наоборот. Оба направления дают код, который кто-то руками держит в согласии, как только реальность расходится.

Здесь ничего не порождается из другого. Структуры пишете вы. Схемой владеют миграции. Команда `orm` читает и то и другое, показывает каждое расхождение и генерирует типизированные метаданные **только из отображения, которое смогла доказать**.

Следствие и есть смысл: если запрос компилируется, база сможет на него ответить.

## Что это даёт

Сгенерированные дескрипторы несут параметры типов, поэтому компилятор требует того, что доказала сверка:

```go
// Predicate[User] не дотянется до запроса по Post.
db.Orders.Query().Where(Users.Email.Eq("a@example.com")) // не компилируется

// У текстовой колонки есть Like. У целочисленной нет.
Users.Email.ILike("%@example.com") // нормально
Users.Age.ILike("%")               // не компилируется

// У NOT NULL колонки нет IsNull.
Users.Bio.IsNull()   // нормально: bio nullable
Users.Email.IsNull() // не компилируется
```

Это не соглашение об именовании и не правило линтера. Это следует из типа дескриптора, который пришёл из каталога.

## Чего она не делает

Инструмент определяется и тем, чего он делать не станет:

- **Нет ленивой загрузки.** Связь загружается потому, что вы её попросили. Цикл по срезу не превратится незаметно в запрос на строку.
- **Нет отслеживания изменений, identity map и `Save`.** `Insert`, `Update` и `Delete` выражают намерение. Ничего не додумывается.
- **Нет неявной обработки нулевых значений.** Структура с `Active: false` сохранит `false`, потому что библиотека не отличит это от поля, которого никто не касался. Попросить значение по умолчанию — это [`orm.Default`](/ru/docs/writing/), и это отдельное явное действие.
- **Нет сборки SQL из строк.** Любое значение становится параметром привязки. `Expr` и `Raw` принимают текст SQL намеренно; ни один не принимает значения, вставленные в него.
- **Нет случайных `UPDATE` без `WHERE`.** Обновление или удаление без условий отвергается с `ErrMissingWhere`, если только `All` не сказал, что имелись в виду все строки.

## Откуда берутся гарантии

Три слоя, у каждого своя работа.

| Слой | Владеет | Доказывает |
| --- | --- | --- |
| Миграции | Схемой | Что базу можно собрать с нуля |
| Сверка | Отображением | Что у каждого поля есть колонка совместимого типа |
| Сгенерированный код | Дескрипторами | Что неверный запрос не компилируется |

Если сверка не может доказать поле, дескриптор для него не появится — будет ошибка с именем поля, колонки и способом починки. Отката к `any` нет.

## Поддерживаемые версии PostgreSQL

14, 15, 16, 17 и 18. Не «должно работать»: набор тестов совместимости отказывается запускаться меньше чем на всех пяти, потому что заявленной, но ни разу не проверенной версии верить нельзя.

## Куда дальше

- [Установка](/ru/docs/installation/) — модуль и CLI.
- [Быстрый старт](/ru/docs/quickstart/) — схема, структура и запрос за несколько минут.
- [Ключевые идеи](/ru/docs/concepts/) — словарь, которым пользуется остальная документация.

## Разобранные примеры

Пример того, что делает за вас компилятор, — в четырёх несвязанных схемах.

```go
// Магазин. У текста есть Like, и компилятор это знает, потому что так сказал каталог.
db.Products.Query().Where(Products.Name.ILike("%lamp%"))

// Бухгалтерия. Проверка баланса в WHERE, поэтому овердрафт — это обновление,
// не нашедшее строк, а не гонка.
db.Accounts.Update().
    Set(Accounts.Balance.SetExpr(Accounts.Balance.Sub(amount))).
    Where(Accounts.ID.Eq(id)).
    Where(Accounts.Balance.Gte(amount)).
    Exec(ctx)

// Календарь. Пересечение — один предикат, а не четыре сравнения, которые надо не перепутать.
db.Bookings.Query().Where(Bookings.During.Overlaps(orm.ClosedOpen(from, to)))

// Автопарк. Три уровня за три запроса, сколько бы ни было строк.
db.Depots.Query().With(Depots.Vehicles.With(Vehicles.Services)).All(ctx)
```

И четыре вещи, которые не компилируются, — то же утверждение с другой стороны:

```go
Products.PriceCents.ILike("%")          // у целого числа нет ILike
Products.Name.IsNull()                  // name объявлена NOT NULL
db.Accounts.Query().Where(Products.Name.Eq("x"))  // не та сущность
orm.UnionAll[Row](byEmail, byAge)       // ветви расходятся по форме
```
