Aperture

Клиент PostgreSQL и SQLite для macOS: фильтры на чистом SQL, правки одной транзакцией и план запроса с подсказками.

Тёмное окно Aperture: слева дерево схемы public с таблицами и числом строк, справа таблица orders с фильтром status in ('paid', 'shipped') and total > 25 и сортировкой placed_at desc; одна строка помечена на удаление, одна добавлена со значениями DEFAULT

Aperture — клиент PostgreSQL и SQLite для macOS с открытым исходным кодом под MIT.

Таблица — это три строчки SQL

Над каждой таблицей три поля: WHERE, SELECT и ORDER. Нужны оплаченные и отправленные заказы дороже 25? Фильтр так и выглядит:

status in ('paid', 'shipped') and total > 25

Обычный SQL, с подсветкой и автодополнением по схеме.

Таблица orders с фильтром, сортировкой и пагинацией

Сама таблица плотная, и в ней ничего не теряется. Каждая ячейка окрашена по типу — дату с числом не перепутать, NULL с пустой строкой тоже. Ширина колонок запоминается для каждой таблицы отдельно. Клик по заголовку сортирует, клик по следующему добавляет его в сортировку вторым. Правый клик по ячейке копирует её, фильтрует по её значению или ведёт по внешнему ключу в связанную таблицу.

А как вернуться обратно? ⌘[. И снова вперёд — ⌘]. История помнит вкладки, фильтры и сортировки так же, как браузер помнит страницы.

Правки сначала копятся

Двойной клик по ячейке открывает не текстовое поле, а редактор под её тип.

Редактор ячейки timestamptz: дата, время, часовой пояс и календарь

У timestamptz — календарь, часы и часовой пояс. У bool — три кнопки: true, false, NULL. У JSON — редактор с подсветкой. NULL и DEFAULT ставятся в один клик, а там, где схема их запрещает, кнопки неактивны.

Время поменяли. Save.

В базу не ушло ничего.

Правка остаётся в памяти: ячейка подсвечена, на панели сверху загорается счётчик. Удалённая строка, новая строка — туда же, счётчик растёт, а база по-прежнему не знает ни об одном изменении.

Что же в итоге туда уйдёт? Это видно в окне, которое открывается по клику на счётчик.

Окно Pending changes: UPDATE, DELETE и INSERT с полным текстом каждого запроса

Здесь каждый запрос целиком. Не «изменено 3 строки», а сам UPDATE … WHERE ctid = '(1204,7)'::tid: какая строка, какая колонка, какое значение. Apply отправляет всё одной транзакцией, Revert all выбрасывает. Для тех, кто уверен и без просмотра, рядом со счётчиком есть галочка — она делает то же, что Apply.

У этой транзакции есть предохранитель. Строки ищутся по ctid в Postgres и по rowid в SQLite. Если хоть один запрос затронул не ровно одну строку — ноль, потому что её уже удалили, или две, потому что что-то пошло не так, — откатывается вся пачка.

Это обмен: каждая правка стоит лишнего шага. Зато без Apply в базу не попадает ничего.

Каждый запрос в скрипте запускается отдельно

SQL-редактор с двумя запросами и таблицей результатов

Слева от каждого запроса — ▶. ⌘↵ выполняет запрос под курсором, ⌘⇧↵ — весь скрипт по очереди, до первой ошибки. Aperture сам делит скрипт на запросы и знает, где каждый на самом деле кончается: точка с запятой внутри строки, комментария или тела $$ … $$ его не обманет.

А если забыть LIMIT? SELECT без него обрезается на 10 000 строк, и таблица об этом сообщает. Миллион строк в окно не приедет.

Запросы сохраняются сами, к своему подключению, и переживают перезапуск.

План запроса сам говорит, что не так

Запрос тормозит. Почему?

EXPLAIN ANALYZE в виде дерева узлов с временем и предупреждением о Seq Scan

В Postgres кнопка Explain рисует план деревом. У каждого узла время, доля от общего, сколько строк ожидалось и сколько пришло. Самый медленный подсвечен — искать его глазами не нужно.

Поверх дерева работают правила, которые читают план за вас. Последовательное чтение, после которого фильтр выбрасывает девять строк из десяти. Сортировка, вылезшая на диск. Оценка строк, промахнувшаяся на порядок. Каждое правило говорит простыми словами, в чём беда и что делать — например, что помог бы индекс по колонкам фильтра.

А если нужно понять саму таблицу, а не запрос? У каждой таблицы и представления есть своя вкладка: описание человеческим языком и DDL. В Postgres DDL собирается из pg_catalog, в SQLite берётся как есть из sqlite_master.

⌘K ищет сразу везде

Командная палитра: поиск ord находит таблицы, открытую вкладку и команду

Три буквы — ord — и в одном списке таблицы orders и order_items, открытая вкладка с тремя неотправленными правками и команда «вперёд по истории». Палитра ищет по таблицам, вкладкам, недавнему, сохранённым запросам, подключениям и командам сразу.

Паролю от базы не обязательно лежать в приложении

Где Aperture хранит пароль от продакшена?

Может не хранить вовсе. Вместо пароля в подключении указывается команда, которая выполняется при каждом подключении и печатает пароль:

op read "op://Private/atlas-prod/password"

Пароль остаётся в 1Password. Aperture получает его в момент подключения и на диск не пишет.

Всё, что приложение всё-таки помнит — подключения, запросы, историю, настройки, — лежит в одном файле SQLite, зашифрованном SQLCipher 4. Ключ от него — пароль, который задаётся при первом запуске. Его можно отдать на хранение в связку ключей macOS, чтобы не вводить каждый раз.

Обрыв связи не закрывает вкладки

VPN отвалился. Ноутбук уснул. Сервер перезагрузили.

Раз в 30 секунд Aperture проверяет соединение, и когда оно пропадает, не выкидывает на стартовый экран. Сверху появляется плашка с кнопкой Reconnect — и после переподключения всё на месте. Те же вкладки. Те же фильтры. Те же неотправленные правки.

Что стоит знать заранее

Пароль от хранилища не восстанавливается. Забыли — файл придётся стереть вместе со всеми подключениями. Это цена того, что он действительно зашифрован.

Число строк в Postgres — оценка. Без фильтра оно берётся из reltuples, а не из count(*). На таблице, куда только что залили много строк, а статистику ещё не обновили, оценка соврёт, и последние страницы спрячутся.

Ставится одним перетаскиванием

Aperture.dmg перетаскивается в Applications, и на этом установка закончена. Сборка подписана Developer ID и заверена Apple, так что macOS не ругается на неизвестного разработчика. Нужна macOS 12 или новее.

Дальше о версиях можно не думать. Раз в день приложение проверяет, не вышла ли новая, и предлагает обновиться. Обновление ставится, только если сходится его подпись.

Окно Software Update: Aperture 1.40.0 вместо 1.39.0, заметки к релизу и кнопки Skip This Version, Remind Me Later, Install Update

Сайт проекта — aperture.ivashkin.dev, исходники — github.com/jwo1f/aperture.