Регрессионные тесты выполняются через pg_regress (make installcheck) на работающем сервере:
sql/- входные SQL-файлы, один файл на тестexpected/- ожидаемый вывод psql для каждого теста, сравнивается побайтноresults/- фактический вывод последнего прогона, при расхождении рядом появляетсяregression.diffs
Комментарии из sql/ psql печатает в вывод, поэтому они входят в expected/: правка комментария в тесте
меняет оба файла.
CREATE EXTENSION pg_query_stack, проверка наличия функцийpg_query_stackиpg_self_query- Единственный тест, который создает расширение, поэтому любой набор начинается с него
SELECT * FROM pg_query_stack(0)возвращает одну строку: frame_number = 0 и текст самого запроса
pg_query_stack(1)иpg_query_stack()(умолчание 1) на верхнем уровне возвращают 0 строк
- Большие (1000 и выше) и отрицательные значения обрабатываются без ошибок
- Функция вызывает
pg_query_stack(), виден только внешний вызов функции
- Функция вызывает
pg_query_stack(0), видны обе строки: вызов функции и внутренний SELECT
- После деления на ноль расширение работает как прежде
- EXCEPTION-блоки, вложенные субтранзакции, откаты, серия INFO со снимками стека
- Вложенные функции,
CREATE TEMP TABLE ... AS SELECT * FROM pg_query_stack(...), разные skip_count
- 200 итераций, на каждой три TEMP TABLE и вызов стека, проверяется отсутствие ошибок
- 200, а не больше: каждая TEMP TABLE держит AccessExclusiveLock до конца DO-блока
- Стек внутри AFTER INSERT/UPDATE триггеров, каскадные операции с временными таблицами, REFERENCING
- Рекурсия на 150 уровней при кольце в 100 слотов
- Глубже лимита фреймы не записываются, в кольце остаются 100 внешних, счет остается согласованным
- Литерал в 70 КБ: query_text возвращается целиком, без усечения
- CTE, рекурсивные CTE и вложенные CTE внутри функций видны в стеке корректно
- Ошибка на этапе ExecutorFinish перехвачена внешним EXCEPTION, завершившийся INSERT в стеке не остается
- Цепочка AFTER UPDATE -> INSERT -> AFTER INSERT -> EXCEPTION WHEN OTHERS, после отката субтранзакции стек чист
- Пять функций f1..f5 с EXECUTE между уровнями, все шесть фреймов имеют непустой текст, номера идут без пропусков
- Двойная вложенность EXCEPTION-блоков и CHECK-ошибка в AFTER-триггере, фреймы упавших запросов сняты по снимкам
LOAD 'auto_explain'внутри сессии, его хуки становятся снаружи наших, стек внутри запроса корректен
- BEFORE-триггер с
pg_query_stack(0): прямой INSERT, INSERT из функции, bulk INSERT на 50 строк, UPDATE и DELETE
- Стек внутри запроса к columnar-таблице, при отсутствии
citus_columnarтест пропускается с тем же выводом
- Переходные таблицы,
pg_self_query() ORDER BY frame_number DESC LIMIT 1захватывает top-level UPDATE
- То же для DELETE с переходной таблицей OLD TABLE
- Аудит захватывает сам UPDATE (frame 1), а не вызывающий
SELECT fn()
- Два курсора закрываются в порядке открытия (SQL и plpgsql), стек от порядка CLOSE не зависит
- Функция возвращает открытый курсор, SELECT завершен, аудит видит UPDATE, FETCH показывает запрос курсора как фрейм
- SAVEPOINT / ROLLBACK TO и EXCEPTION-блок: снятый слот курсора не воскресает
- FETCH падает внутри субтранзакции, портал становится PORTAL_FAILED и ExecutorEnd не получает, CLOSE и аудит безопасны
- Открытый курсор без FETCH в стеке не виден, аудит получает UPDATE как frame 0, во время FETCH запрос курсора виден
- Holdable-курсор дочитывается на COMMIT и виден фреймом внутри дочитывания, пять открытых курсоров сбрасываются на COMMIT
- 300 SAVEPOINT через
\gexec, ошибка в EXCEPTION-блоке на глубине 301: стек до конца транзакции пуст - Сбой внутри функции под живым внешним SELECT, затем PREPARE TRANSACTION или COMMIT: следующая транзакция чистая
- Ветка PREPARE выполняется только при
max_prepared_transactions > 0, иначе тест идет через COMMIT с тем же выводом
Тестовый PostgreSQL 16 живет в pg_data_test/ на порту 5433. LC_ALL=C обязателен, иначе PG 16 на macOS
падает при старте с postmaster became multithreaded during startup.
LC_ALL=C /opt/homebrew/opt/postgresql@16/bin/pg_ctl \
-D pg_data_test -l pg_data_test/logfile -o "-p 5433" startЧтобы ветка PREPARE в тесте 032 действительно выполнялась, в postgresql.conf тестового сервера нужен
max_prepared_transactions = 10 (или другое значение больше 0).
make PG_CONFIG=/opt/homebrew/opt/postgresql@16/bin/pg_config
make install PG_CONFIG=/opt/homebrew/opt/postgresql@16/bin/pg_configПосле make install сервер нужно перезапустить: загруженная библиотека остается в памяти процессов.
make clean удаляет каталог results/ вместе с отслеживаемыми снимками, поэтому перед коммитом их
возвращает git checkout -- results/.
LC_ALL=C make installcheck PG_CONFIG=/opt/homebrew/opt/postgresql@16/bin/pg_config PGPORT=5433./run_tests.sh делает то же самое вместе со сборкой и установкой (sudo make install, pg_config из PATH)
и печатает сводку по каждому тесту.
PG_CONFIG=/opt/homebrew/opt/postgresql@16/bin/pg_config PGPORT=5433 LC_ALL=C ./run_single_test.sh 026_cursor_close_orderСкрипт сам добавляет 001_setup перед выбранным тестом: pg_regress пересоздает базу перед прогоном, а расширение
создает только 001_setup. Без скрипта список задается вручную:
LC_ALL=C make installcheck REGRESS="001_setup 026_cursor_close_order" \
PG_CONFIG=/opt/homebrew/opt/postgresql@16/bin/pg_config PGPORT=5433psql -p 5433 -d your_database
\i sql/001_setup.sql
\i sql/002_basic_functionality.sqlТак вывод не сравнивается с expected/, это только для отладки отдельного сценария.
# using postmaster on 127.0.0.1, port 5433
ok 1 - 001_setup 17 ms
ok 2 - 002_basic_functionality 13 ms
...
ok 31 - 032_subxact_snapshot_overflow 44 ms
# All 31 tests passed.
При расхождении pg_regress пишет:
results/<test>.out- фактический вывод тестаregression.diffs- diff междуexpected/<test>.outиresults/<test>.outregression.out- протокол прогона
Падение сервера (Assert в cassert-сборке, SIGSEGV) видно как server closed the connection unexpectedly в
results/<test>.out и как TRAP: или terminated by signal в логе сервера. Следующие тесты в этом прогоне
могут упасть только из-за восстановления сервера, их надо перепрогнать отдельно.
- Создайте
sql/NNN_name.sql. Комментарии в файле попадают в вывод, пишите их в настоящем времени. - Создайте
expected/NNN_name.out. Удобно снять выводpsql -X -a -q < sql/NNN_name.sqlи выправить значения до ожидаемых, так формат таблиц psql совпадет сpg_regress. - Добавьте имя теста в
REGRESSв Makefile, в списокtest_filesвrun_tests.shи в подсказку вrun_single_test.sh. - Опишите тест в этом файле.
Требования к детерминизму:
- Прогон идет с
LC_ALL=C, вывод не должен зависеть от локали и от версии PostgreSQL (16, 17 и 18). - Тексты запросов в выводе лучше сравнивать через
LIKE,left()или длину, а не печатать целиком. - Форматирование и пробелы в
expected/должны совпадать с выводом psql побайтно. - Ветки, зависящие от окружения (например
max_prepared_transactions), оформляются через\ifтак, чтобы вывод обеих веток совпадал.