0 / 63 (0%)

psql на каждый день

В Brew прилетает баг: «в приложении пропал колд брю из меню». Прежде чем лезть в код, хочется на тридцать секунд заглянуть в саму базу — есть ли там вообще этот напиток, какой у него stock, не сломалась ли структура таблицы. Можно открыть Adminer и кликать мышкой, но это медленно и не ложится в терминальный workflow. Рабочий инструмент для такого — psql: официальный консольный клиент Postgres, который ставится вместе с сервером (brew install libpq на macOS).

Цель юнита узкая: не выучить весь psql, а собрать «аптечку» из горстки команд, которой хватает на 90% случаев «надо быстро посмотреть в БД». Это escape-hatch-юнит — здесь нет Go и нет sqlc, потому что урок про сам клиент.

Мета-команды: то, что отличает psql от «просто SQL»

В psql два вида ввода. Обычный SQL (SELECT ...;) уходит на сервер и исполняется им. А команды, начинающиеся с обратного слэша (\dt, \d, \x) — это мета-команды: их обрабатывает сам psql на клиенте, до и вместо отправки на сервер. Они не часть SQL и не работают из драйвера в твоём приложении — это инструмент интерактивной работы руками.

plaintext
   ты набираешь…                     где это обрабатывается
   ─────────────────────────────────────────────────────────────────────
   \dt   \d   \x   \timing    ──▶  psql (КЛИЕНТ) исполняет сам, на сервер не идёт
   SELECT … ;   INSERT … ;    ──▶  psql пересылает ──▶ postgres (СЕРВЕР) исполняет

Именно поэтому урок — escape-hatch: мета-команду нельзя «написать в query.sql и сгенерировать через sqlc». Зато за один символ они дают то, на что в чистом SQL ушёл бы громоздкий запрос к системным каталогам (information_schema, pg_catalog).

Подключиться к песочнице:

sh
psql 'postgres://brew:brew@localhost:5432/brew?sslmode=disable'

Внутри — приглашение brew=#. Дальше всё интерактивно.

Осмотреться: \l, \dt, \d

Три команды отвечают на вопрос «что вообще здесь есть» на трёх уровнях вложенности.

\l (list) — список баз на сервере. Покажет brew плюс системные postgres, template0, template1. Полезно, когда не уверен, к той ли базе подключился.

\dt (describe tables) — таблицы текущей базы. В нашей brew это 9 таблиц схемы Brew. Рядом живут \dv (вьюхи), \di (индексы), \ds (последовательности), \dn (схемы), \df (функции) — буква после \d сужает тип.

\d <имя> — структура одного объекта: колонки, типы, nullable, дефолты, индексы и — что особенно ценно — внешние ключи в обе стороны (на кого ссылается таблица и кто ссылается на неё). По \d drinks сразу видно, что base_price — это bigint (цена в центах, не float), а на drinks ссылаются order_items и inventory. Это карта связей без единого JOIN'а к pg_catalog.

Сделать вывод читаемым: \x и \timing

\x (expanded) переключает таблицу «в столбик»: вместо широкой строки, которая не влезает в терминал и переносится кашей, каждое поле печатается на своей строке имя | значение. Незаменимо для таблиц с десятком колонок или с длинным body/description. \x auto — умный режим: широкие результаты в столбик, узкие обычной таблицей.

\timing включает замер времени каждого запроса (Time: 2.255 ms). Это первый, грубый сигнал «быстро/медленно» — не профайлинг (для него есть EXPLAIN ANALYZE в модуле 06), но достаточно, чтобы заметить, что запрос внезапно стал занимать секунды. Вывод \timing зависит от машины и нагрузки, поэтому в демо ниже его нет — попробуй сам в make db-shell.

Не держать всё в голове: \i, \?, \h

\i <файл> исполняет SQL-файл — ровно так наш make db-reset накатывает schema/brew.sql и seed.sql. Удобно для повторяемых скриптов: не копировать запрос в приглашение, а держать в файле под версионным контролем.

\? — справка по всем мета-командам (их десятки). \h <команда> — справка по синтаксису SQL: \h INSERT напомнит грамматику INSERT со всеми опциями, не отрываясь от терминала. Две команды, которые делают остальные необязательными для запоминания.

Вся аптечка одной таблицей:

КомандаЧто делаетГде исполняется
\lсписок баз на сервереpsql (клиент)
\dtтаблицы текущей базы (\dv/\di/\ds/\dn/\df — по типу объекта)psql (клиент)
\d <имя>структура объекта: колонки, типы, PK, индексы, FK в обе стороныpsql (клиент)
\xвывод «в столбик» (\x auto — по ширине результата)psql (клиент)
\timingзамер времени каждого запроса (грубый сигнал, не профайлинг)psql (клиент)
\i <файл>выполнить SQL из файлаpsql (клиент)
\? / \hсправка: по мета-командам / по синтаксису SQLpsql (клиент)
SELECT … ;обычный SQL — уходит на сервер и исполняется имpostgres (сервер)

Последняя строка — для контраста: всё с \ остаётся на клиенте, обычный SQL уходит на сервер (та же граница, что на диаграмме выше).

Что показывает наш код

«Код» этого юнита — psql-скрипт demo.sql: маленькая экскурсия, которая прогоняет три ключевые мета-команды по схеме Brew, ничего не меняя в данных.

sql
\dt                 -- какие таблицы есть в базе
\d drinks           -- структура одной таблицы: колонки, типы, PK, FK
\x on               -- расширенный вывод (строка столбиком)
SELECT id, sku, name, category, base_price, stock FROM drinks WHERE sku = 'CLD-01';
\x off

make run запускает его через psql -f demo.sql — то же самое, что набрать эти команды руками в make db-shell, только разом и воспроизводимо. Это и есть «аптечка»: осмотреться (\dt), разобрать одну таблицу (\d), прочитать строку удобно (\x).

Запуск

Подними песочницу (из корня репозитория) и накати схему Brew:

sh
docker compose up -d
make lecture L=00-getting-connected/00-03-psql-survival-kit T=db-reset

Запусти экскурсию:

sh
make lecture L=00-getting-connected/00-03-psql-survival-kit

(T=run — значение по умолчанию. Изнутри каталога юнита это просто make db-reset и make run.)

Вывод:

plaintext
== \dt — какие таблицы есть в базе brew (схема Brew: 9 таблиц) ==========
                List of tables
 Schema |         Name         | Type  | Owner 
--------+----------------------+-------+-------
 public | articles             | table | brew
 public | customers            | table | brew
 public | drinks               | table | brew
 public | inventory            | table | brew
 public | order_items          | table | brew
 public | orders               | table | brew
 public | outbox               | table | brew
 public | processed_outbox_ids | table | brew
 public | shops                | table | brew
(9 rows)
 
 
== \d drinks — структура: колонки, типы, PK и кто на таблицу ссылается ==
                          Table "public.drinks"
   Column    |           Type           | Collation | Nullable | Default 
-------------+--------------------------+-----------+----------+---------
 id          | bigint                   |           | not null | 
 sku         | text                     |           | not null | 
 name        | text                     |           | not null | 
 description | text                     |           | not null | 
 category    | text                     |           | not null | 
 base_price  | bigint                   |           | not null | 
 stock       | integer                  |           | not null | 0
 created_at  | timestamp with time zone |           | not null | now()
 updated_at  | timestamp with time zone |           | not null | now()
Indexes:
    "drinks_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "inventory" CONSTRAINT "inventory_drink_id_fkey" FOREIGN KEY (drink_id) REFERENCES drinks(id) ON DELETE CASCADE
    TABLE "order_items" CONSTRAINT "order_items_drink_id_fkey" FOREIGN KEY (drink_id) REFERENCES drinks(id)
 
 
== \x + SELECT — расширенный вывод: одна строка столбиком (для широких) ==
-[ RECORD 1 ]--------
id         | 4
sku        | CLD-01
name       | Колд брю
category   | cold
base_price | 520
stock      | 40

Колд брю на месте (stock = 40) — значит, баг не в данных, а в коде приложения. На это ушло три команды и десять секунд.

Заборчик

  • psql — инструмент для разведки и отладки руками: посмотреть, прикинуть, проверить гипотезу. В продакшене так не работают с данными: приложение не запускает psql и не парсит текстовый вывод — оно ходит в базу через драйвер типизированным запросом (к этому конвейеру курс придёт в 00-04 и 00-05).
  • Мета-команды (\dt, \d) вообще существуют только в psql: из pgx в Go их не вызвать, это не SQL.
  • \d-вывод и pg_catalog — это удобная карта, но не контракт. Структуру, на которую опирается приложение, фиксируют миграции (модуль 02), а не «я посмотрел через \d и вроде совпадает».

Что забрать с собой

  • В psql два вида ввода: SQL (исполняет сервер) и мета-команды с \ (обрабатывает клиент). Мета-команды — не SQL и из приложения недоступны.
  • Аптечка на каждый день: \dt/\d — осмотреться и разобрать таблицу; \x — читаемый вывод; \timing — грубый замер; \i — выполнить файл; \? и \h — справка, чтобы не держать всё в голове.
  • \d <таблица> показывает внешние ключи в обе стороны — это карта связей без запросов к системным каталогам.

Дальше — юнит 00-04 «подключение из Go»: уходим из интерактивного psql в код приложения. Откроем pgxpool, выполним первый запрос с параметром через $1 — и на анти-демо увидим, почему склейка SQL строками открывает дорогу инъекции, а биндинг параметров её закрывает.

·Модуль 01

Этот урок ещё впереди

Курс изучается по порядку — чтобы открыть этот шаг, сначала завершите предыдущие. Так контекст накапливается без пропусков.

/ вы пытались открыть
Подключение и ориентация / psql на каждый день