Contributing to ulak

Thank you for your interest in contributing to ulak! This document provides guidelines and instructions for contributing.

Development Environment

Prerequisites

  • Docker and Docker Compose
  • PostgreSQL 14-18 development headers (if building locally)
  • libcurl development headers (required)
  • Optional protocol libraries: librdkafka, libmosquitto, hiredis, librabbitmq
  • lefthook (installed automatically by make hooks-install when Homebrew or Go is available)
  • clang-format, cppcheck (install with make tools-install for local/CI parity)

Quick Start

# Clone the repository
git clone https://github.com/zeybek/ulak.git
cd ulak

# Start development environment (PostgreSQL + all protocol services)
docker compose up -d

# Build and install the extension
docker exec ulak-postgres-1 bash -c \
  "cd /src/ulak && make clean && make ENABLE_KAFKA=1 ENABLE_MQTT=1 ENABLE_REDIS=1 ENABLE_AMQP=1 ENABLE_NATS=1 && make install"

# Restart PostgreSQL to load the extension
docker restart ulak-postgres-1

# Run regression tests
docker exec ulak-postgres-1 bash -c \
  "cd /src/ulak && make installcheck"

Testing Against Different PostgreSQL Versions

The Dockerfile accepts a PG_MAJOR build argument:

docker compose build --build-arg PG_MAJOR=15 postgres
docker compose up -d postgres

Code Style

C Code

  • Follow the PostgreSQL coding conventions.
  • Use clang-format for formatting: make format
  • Use cppcheck for static analysis: make lint
  • Always use palloc/pfree — never malloc/free.
  • Always use strlcpy/snprintf — never strcpy/strcat.
  • Zero sensitive data before pfree.

Git Hooks

This repository uses lefthook to keep local commits aligned with CI.

Install and wire the local hooks with:

make tools-install
make hooks-install

Check your local toolchain with:

make tools-versions

Configured hooks:

  • pre-commit
    • cppcheck
    • clang-format -i on staged src/ and include/ C headers/sources, then auto re-stage
  • commit-msg
    • conventional-commit style validation

Accepted commit header format:

type(scope): subject
type: subject
type(scope)!: subject

Allowed commit types:

feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert

SQL

  • Use lowercase for SQL keywords.
  • Prefix all objects with the ulak schema.
  • Add GRANT EXECUTE for all three RBAC roles on every new function.
  • Include SECURITY DEFINER only when elevated privileges are required.

Pull Requests

Before Submitting

  1. Run make format to ensure code is formatted correctly.
  2. Run make lint to check for static analysis warnings.
  3. Run make hooks-run to exercise the same local pre-commit checks manually when needed.
  4. Run make installcheck to verify the regression and isolation suites pass.
  5. Add or update tests in tests/regress/sql/, tests/regress/expected/, or tests/isolation/ for new functionality.
  6. Update CHANGELOG.md to reflect the changes included in the next release.

PR Guidelines

  • Keep PRs focused on a single change.
  • Write clear commit messages explaining the “why”, not just the “what”.
  • Reference related issues in the PR description.
  • Ensure backward compatibility with PostgreSQL 14-18.
  • Use #if PG_VERSION_NUM guards for version-specific code.

Architecture Guidelines

Adding a New Protocol Dispatcher

See src/dispatchers/dispatcher.h for the dispatcher interface. In summary:

  1. Create src/dispatchers/{protocol}/ directory.
  2. Implement the DispatcherOperations interface.
  3. Register in the factory (src/dispatchers/dispatcher.c).
  4. Add conditional compilation with ENABLE_{PROTOCOL} flag.
  5. Update the Makefile, Dockerfile, and docker-compose.yml.
  6. Add regression tests and documentation.

Adding a New GUC Parameter

  1. Add the DefineCustom*Variable() call in src/config/guc.c.
  2. Add a corresponding config_is_valid_*() validation function.
  3. Keep the validation range in sync with the DefineCustomVariable bounds.
  4. Declare the extern in src/config/guc.h.
  5. Update CHANGELOG.md.

Memory Safety

  • Always wrap SPI calls in PG_TRY/PG_CATCH blocks.
  • Never call SPI_finish() in PG_CATCH — only AbortCurrentTransaction().
  • Never use REPEATABLE READ in workers — it causes serialization failures.
  • Use MemoryContext switches for long-lived allocations.

Reporting Issues

When reporting bugs, please include:

  • PostgreSQL version (SELECT version())
  • ulak version
  • Enabled protocols and build flags
  • Steps to reproduce
  • Relevant log output (SET ulak.log_level = 'DEBUG')

License

By contributing, you agree that your contributions will be licensed under the Apache License 2.0, consistent with the rest of the repository.