This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
postgres-rrule is a PostgreSQL extension for working with recurring dates. It parses RRULE (recurrence rule) specifications from the iCalendar standard and generates occurrences of recurring events.
Key limitation: This extension does not support endlessly repeating events. All RRULEs must include either UNTIL or COUNT parameters.
The project uses a Makefile-based build system that compiles individual SQL files into a single postgres-rrule.sql file.
All main targets now use Docker by default, providing consistent testing across machines.
Quick start:
make all # Build Docker image and run all testsMain Docker targets:
make all # Build image and run all tests (default workflow)
make build # Build Docker image with dependencies
make test # Run tests in container
make start # Start PostgreSQL container (detached)
make stop # Stop and remove container
make shell # Open bash shell in container
make psql # Open psql session in container
make logs # View container logs
make clean # Remove image and prune resources
make rebuild # Clean rebuild from scratchDocker configuration:
- Base image: postgres:16
- Container name: postgres-rrule-test
- Port: 5433 (host) → 5432 (container)
- Workspace: /workspace (mounted from current directory)
Note: Old docker-* prefix commands still work but show deprecation warnings.
Build the SQL file without database:
make compile # Build postgres-rrule.sql from source filesIf you prefer to use a local PostgreSQL installation instead of Docker:
make local-all # Compile and install extension locally
make local-execute # Install extension into local PostgreSQL
make local-test # Run tests on local PostgreSQL
make local-clean # Drop _rrule schema from local database
make local-pgtap # Install pgTAP extension locallyDatabase connection parameters:
- Host: localhost
- Port: 5432
- User: postgres
- Password: unsafe
Override when needed:
make local-test PGHOST=myhost PGPORT=5433 PGUSER=myuserAll types, functions, and operators are created in the _rrule schema. Users must add _rrule to their search path:
SET search_path TO public, _rrule;-
RRULE (table-based composite type in src/types/types.sql:19-36)
- Represents a single recurrence rule with frequency, interval, count, until, and various BY* parameters
- Uses table constraints to enforce RFC 5545 validity rules
- Enum types:
FREQ(YEARLY, MONTHLY, WEEKLY, DAILY) andDAY(MO, TU, WE, etc.)
-
RRULESET (table-based composite type in src/types/types.sql:39-46)
- Combines an RRULE with dtstart, dtend, exrule, rdate, and exdate
- Represents a complete recurrence specification
-
exploded_interval (type in src/types/types.sql:49-53)
- Internal helper type for interval calculations with separate months, days, and seconds
The build system concatenates SQL files in this order:
- src/schema.sql - Creates the
_rruleschema - src/types/ - Type definitions (FREQ, DAY, RRULE, RRULESET)
- src/functions/ - Functions numbered 0001-0270 (processed alphabetically)
- src/operators/ - Operator definitions (@>, <@, =, <>, etc.)
- src/casts/ - Type casting functions (TEXT <-> RRULE, JSONB <-> RRULESET)
Function files are numbered to ensure proper dependency ordering during compilation:
- 0001-0090: Helper functions and utilities
- 0100-0105: Core RRULE and RRULESET parsing functions
- 0200-0214: Query functions (is_finite, occurrences, first, last, before, after, contains_timestamp)
- 0215-0240: JSONB conversion functions
- 0250-0270: RRULESET array operators
Parsing:
rrule(TEXT)(src/functions/0100-rrule.sql) - Parse RRULE string to RRULE typerruleset(TEXT)(src/functions/0105-rruleset.sql) - Parse multiline RRULESET format with DTSTART, RRULE, EXDATE, RDATEjsonb_to_rrule(JSONB),jsonb_to_rruleset(JSONB)- Parse from JSON format
Querying:
occurrences(RRULE, TIMESTAMP, INTEGER)- Generate N occurrences starting from timestampfirst(RRULESET),last(RRULESET)- Get first/last occurrenceis_finite(RRULE)- Check if rule has COUNT or UNTIL
Operators:
RRULESET @> TIMESTAMP- Check if timestamp is contained in the rulesetRRULESET[] @> TIMESTAMP- Check if timestamp is in any ruleset in array- Similar operators work with JSONB representations
RRULE validation happens in _rrule.validate_rrule() (src/functions/0090-validate_rrule.sql), which enforces:
- FREQ is required
- BYWEEKNO only valid with FREQ=YEARLY
- BYYEARDAY only valid with FREQ=YEARLY
- BYMONTHDAY not valid with FREQ=WEEKLY
- BYDAY not valid with FREQ=DAILY
- BYSETPOS requires at least one other BY* parameter
- UNTIL and COUNT are mutually exclusive
- INTERVAL must be positive
Tests in tests/ directory follow pgTAP conventions:
- Each test file begins with
BEGIN;andSELECT plan(N); - Tests use
is(),throws_like(), and other pgTAP assertions - Each test file ends with
SELECT * FROM finish();andROLLBACK; - Tests set
search_path TO _rrule, public;to access extension functions
Docker workflow (recommended):
- Modify source files in src/types/, src/functions/, src/operators/, or src/casts/
- Run
make testto compile, install, and test in Docker - Or use
make allto rebuild image and run tests from scratch
Local PostgreSQL workflow:
- Modify source files in src/types/, src/functions/, src/operators/, or src/casts/
- Run
make compileto rebuild postgres-rrule.sql - Run
make local-executeto install into database (drops and recreates _rrule schema) - Run
make local-testto verify changes
When adding new functions, use appropriate numbering (0001-0270) to maintain dependency order.
The documentation for the project is maintained in the docs/ directory. All documentation files are written in Markdown format. All generated documentation files should be placed in this directory.
When adding new features to the project, please ensure that you write the unit tests for the new features and place them in the tests/ directory and ensure they fail BEFORE writing the implementation code.