Contents
Contributing to pg_tre
Thank you for your interest in contributing to pg_tre. This document covers what you need to know to land a patch.
Getting Started
Development Setup
git clone --recurse-submodules https://codeberg.org/gregburd/pg_tre
cd pg_tre
Install PostgreSQL 18 with development headers. The project
develops against the pgrx-managed PG18 install at
~/.pgrx/18.3/pgrx-install/, but any working PG18 install with
pg_config on $PATH works.
PG_CONFIG=$HOME/.pgrx/18.3/pgrx-install/bin/pg_config make
PG_CONFIG=$HOME/.pgrx/18.3/pgrx-install/bin/pg_config make install
Run the tests:
PGHOST=$HOME/.pgrx PGPORT=28818 \
PG_CONFIG=$HOME/.pgrx/18.3/pgrx-install/bin/pg_config \
bash scripts/run-regress.sh
Pre-commit Hooks (Recommended)
pip install pre-commit
pre-commit install
This installs the clang-format and trailing-whitespace hooks
defined in .pre-commit-config.yaml. The same checks run in
the formatting.yml CI workflow on every PR; running them
locally avoids the round-trip.
Code Style
We follow PostgreSQL coding conventions, enforced by
clang-format configured in .clang-format. Key points:
- Line limit: 79 characters.
- Indentation: tabs, 4-space tab width.
- Brace style: Allman (opening braces on new lines).
- Naming: snake_case for functions and variables.
- Comments: 2 spaces before trailing comments.
- Headers: include guards (
#ifndef ... #define ... #endif) with the file’s basename uppercased. - Includes:
postgres.hfirst, then standard library headers (<...>), then project headers ("pg_tre/...").
Format your code before committing:
make format # auto-format src/ and include/
make format-check # check formatting without changes
make format-diff # show what `make format` would change
Source Code Architecture
The src/ directory is organized into layers. Upper layers
depend on lower layers, not the reverse. The directory structure
communicates the dependency flow.
Layer 1 — Postgres interface:
- src/am/ — Index access method (handler, build, scan,
vacuum, cost, options, selectivity).
- src/module.c — Extension init, GUC registration, custom
rmgr registration, legacy UDFs.
Layer 2 — Index coordination:
- src/pages/ — Page-level read/write helpers (meta, upper,
posting, pending, range, buffer).
- src/wal/ — WAL record encode/decode + redo for the custom
resource manager.
Layer 3 — Query processing:
- src/query/ — Regex AST, parser, tokens, trigram
extraction, tile expansion, DNF/CNF resolution, debug helpers.
Layer 4 — Storage primitives:
- src/util/ — Vendored sparsemap, bloom, hash, UTF-8,
pattern cache, type pattern, TRE match wrapper.
Cross-cutting:
- src/util/sparsemap.c is vendored from
https://codeberg.org/gregburd/sparsemap with adjusted
include paths. Do not edit it directly; instead, file an
upstream issue and re-vendor on the next sparsemap release.
Dependency rules: - Layer 1 may depend on Layer 2, 3, 4. - Layer 2 may depend on Layer 3, 4. - Layer 3 may depend on Layer 4. - Layer 4 should not depend on Layer 1, 2, or 3.
These rules are enforced by convention and code review, not mechanically.
Include Path Convention
All project-local #include directives use full paths relative
to include/:
#include "pg_tre/posting.h" /* correct */
#include "pg_tre/sparsemap.h" /* correct */
#include "posting.h" /* wrong: missing prefix */
#include "../posting.h" /* wrong: relative path */
Postgres system includes (<postgres.h>, <access/genam.h>,
etc.) are unaffected.
File Size
Split files based on responsibility, not line count. A 1500-line file with a single clear purpose is fine. A 400-line file doing three unrelated things should be split.
Testing Requirements
Before submitting a pull request:
- Build —
makemust succeed without warnings:sh PG_CONFIG=... make 2>&1 | grep -E "warning:|error:" \ && echo FAIL || echo OK - Regression — full suite must pass three runs in a row
(catches flakes):
sh for i in 1 2 3; do PG_CONFIG=... bash scripts/run-regress.sh \ | grep -E "^FAIL" && echo "FLAKE on run $i" done - Format —
make format-checkmust pass. - Upgrade compatibility — if you touched
pg_tre.control, anysql/pg_tre--*.sql, the metapage layout, the on-disk format constants, or any WAL record format, theupgrade-tests.ymlworkflow will fail in CI unless your PR includes a matching upgrade-script update.
If you modify error messages or NOTICE text, update the
corresponding expected output files in test/expected/.
Adding a Regression Test
Tests live in test/sql/<name>.sql with paired expected output
in test/expected/<name>.out. Register the test in
test/expected/ by running the suite once and snapshotting:
bash scripts/run-regress.sh <new-test-name>
cp test/results/<new-test-name>.out test/expected/<new-test-name>.out
Then add <new-test-name> to the test list in
scripts/run-regress.sh so the full suite picks it up.
Differential Tests
When the index is supposed to return the same answer as a
seq-scan, write the test as a differential check. The
test/sql/multi_leaf.sql test ends with this pattern:
SET enable_seqscan = off;
CREATE TEMP TABLE idx_ids AS SELECT id FROM t WHERE ...;
SET enable_seqscan = on;
SET enable_indexscan = off;
SET enable_bitmapscan = off;
CREATE TEMP TABLE seq_ids AS SELECT id FROM t WHERE ...;
SELECT
(SELECT count(*) FROM idx_ids) = (SELECT count(*) FROM seq_ids)
AS counts_agree,
NOT EXISTS (SELECT 1 FROM idx_ids EXCEPT SELECT 1 FROM seq_ids)
AND NOT EXISTS (SELECT 1 FROM seq_ids EXCEPT SELECT 1 FROM idx_ids)
AS row_sets_agree;
A frozen-broken expected output cannot hide a regression that
makes the index drop rows; this assertion will print f for
either column and fail the test.
Commit Guidelines
- Imperative mood, ≤72 character subject line.
- One logical change per commit.
- Reference issues with
#NNNwhen applicable. - Body explains the why, not the what — the diff already shows what changed.
- Never amend or rebase commits already pushed to shared branches.
- Never force-push.
Pull Request Process
- Fork the repository (or push a branch if you have access).
- Create a feature branch off
main. - Make your changes with appropriate tests.
- Ensure all tests pass locally.
- Submit a pull request to
main.
All pull requests are tested against PostgreSQL 18 in CI. The
upgrade-tests workflow runs against every shipped version in
the matrix when SQL or the control file changed.
PR Description
Include:
- A brief summary of the change.
- The problem it solves (or feature it adds).
- Testing notes — what you ran, what you saw.
- Any breaking changes or migration notes.
- Linked issues.
Reporting Issues
Use the issue templates under .github/ISSUE_TEMPLATE/:
- Bug Report — include PG version, pg_tre version, operating system, steps to reproduce, expected vs. actual.
- Feature Request — describe the problem you’re trying to solve, the proposed solution, and any alternatives considered.
License
By contributing to pg_tre, you agree that your contributions
will be licensed under the MIT License (see LICENSE).