Skip to content

Commit 1e252eb

Browse files
committed
Add CLAUDE.md
Signed-off-by: Marco Slot <marco.slot@snowflake.com>
1 parent d6c1057 commit 1e252eb

1 file changed

Lines changed: 311 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
pg_lake integrates Apache Iceberg and data lake files (Parquet, CSV, JSON) into PostgreSQL, enabling PostgreSQL to function as a lakehouse system. The architecture consists of two main components:
8+
- **PostgreSQL with pg_lake extensions**: Handles query planning, transaction boundaries, and orchestration
9+
- **pgduck_server**: A separate multi-threaded process that implements the PostgreSQL wire protocol and delegates computation to DuckDB's columnar execution engine
10+
11+
Users connect only to PostgreSQL. The pg_lake extensions transparently delegate data scanning and computation to pgduck_server (running DuckDB) when appropriate, while maintaining full transactional guarantees.
12+
13+
## Build Commands
14+
15+
### First-time build
16+
```bash
17+
# Install vcpkg dependencies (required once)
18+
export VCPKG_VERSION=2025.12.12
19+
git clone --recurse-submodules https://github.com/Microsoft/vcpkg.git
20+
./vcpkg/bootstrap-vcpkg.sh
21+
./vcpkg/vcpkg install azure-identity-cpp azure-storage-blobs-cpp azure-storage-files-datalake-cpp openssl
22+
export VCPKG_TOOLCHAIN_PATH="$(pwd)/vcpkg/scripts/buildsystems/vcpkg.cmake"
23+
24+
# Build and install all extensions and pgduck_server
25+
make install
26+
```
27+
28+
### Subsequent builds
29+
```bash
30+
# Fast build that skips rebuilding DuckDB if already built
31+
make install-fast
32+
```
33+
34+
### Component-specific builds
35+
```bash
36+
# Build/install individual extensions
37+
make install-pg_lake_iceberg
38+
make install-pg_lake_table
39+
make install-pgduck_server
40+
41+
# Build all extensions (top-level meta extension)
42+
make install-pg_lake
43+
```
44+
45+
## Running pg_lake
46+
47+
### Required setup
48+
```sql
49+
-- In postgresql.conf:
50+
shared_preload_libraries = 'pg_extension_base'
51+
52+
-- Connect to PostgreSQL and create extensions:
53+
CREATE EXTENSION pg_lake CASCADE;
54+
-- This installs: pg_extension_base, pg_map, pg_extension_updater,
55+
-- pg_lake_engine, pg_lake_iceberg, pg_lake_table, pg_lake_copy, pg_lake
56+
57+
-- Set default location for Iceberg tables:
58+
SET pg_lake_iceberg.default_location_prefix TO 's3://your-bucket/pglake';
59+
```
60+
61+
### Starting pgduck_server
62+
```bash
63+
# Start pgduck_server (must be running for pg_lake to work)
64+
pgduck_server
65+
66+
# With options:
67+
pgduck_server --memory_limit '8GB' --cache_dir /tmp/cache --init_file_path /path/to/init.sql
68+
69+
# pgduck_server listens on port 5332 (unix socket /tmp by default)
70+
# You can connect directly to pgduck_server for debugging:
71+
psql -p 5332 -h /tmp
72+
```
73+
74+
## Testing
75+
76+
The project uses **pytest** for all regression testing (not traditional PostgreSQL SQL regression tests).
77+
78+
### Running all tests
79+
```bash
80+
# Install test dependencies (first time only)
81+
pipenv install --dev
82+
83+
# Run all local tests
84+
make check
85+
86+
# Run end-to-end tests (requires S3/cloud access)
87+
make check-e2e
88+
89+
# Run upgrade tests
90+
make check-upgrade
91+
92+
# Run all tests (local + e2e)
93+
make check # includes check-local and check-e2e
94+
```
95+
96+
### Running tests for specific components
97+
```bash
98+
# Test specific extension
99+
make check-pg_lake_table
100+
make check-pg_lake_iceberg
101+
make check-pgduck_server
102+
103+
# Run isolation tests
104+
make check-isolation_pg_lake_table
105+
```
106+
107+
### Running installcheck
108+
```bash
109+
# Start pgduck_server with test configuration
110+
pgduck_server --init_file_path pgduck_server/tests/test_secrets.sql --cache_dir /tmp/cache &
111+
112+
# Run installcheck (tests against installed extensions)
113+
make installcheck
114+
115+
# Run installcheck for specific component
116+
make installcheck-pg_lake_table
117+
```
118+
119+
### Running individual pytest tests
120+
```bash
121+
cd pg_lake_table
122+
PYTHONPATH=../test_common pipenv run pytest -v tests/pytests/test_specific.py
123+
PYTHONPATH=../test_common pipenv run pytest -v tests/pytests/test_specific.py::test_function_name
124+
```
125+
126+
### Testing with PostgreSQL regression suite
127+
```bash
128+
# Run PostgreSQL's own tests with pg_lake extensions loaded
129+
export PG_REGRESS_DIR=/path/to/postgres/src/test/regress
130+
131+
# Run tests
132+
make installcheck-postgres PG_REGRESS_DIR=$PG_REGRESS_DIR
133+
make installcheck-postgres-with_extensions_created PG_REGRESS_DIR=$PG_REGRESS_DIR
134+
```
135+
136+
## Extension Architecture
137+
138+
pg_lake follows a **modular design** with interoperating components. Each extension has a specific responsibility:
139+
140+
### Extension dependency chain
141+
```
142+
pg_lake (meta-extension)
143+
├── pg_lake_table (FDW for querying data lake files)
144+
│ └── pg_lake_iceberg (Iceberg specification implementation)
145+
│ └── pg_lake_engine (common module for pg_lake extensions)
146+
│ ├── pg_extension_base (foundation for all extensions)
147+
│ ├── pg_map (generic map type)
148+
│ └── pg_extension_updater (automatic extension updates)
149+
└── pg_lake_copy (COPY to/from data lake)
150+
└── pg_lake_engine
151+
152+
pg_lake_spatial (optional, depends on PostGIS)
153+
pg_lake_benchmark (optional, for benchmarking)
154+
```
155+
156+
### Core extensions
157+
- **pg_extension_base**: Foundation for all extensions, provides common utilities
158+
- **pg_extension_updater**: Automatically updates extensions on startup
159+
- **pg_map**: Generic map/key-value type generator for semi-structured data
160+
- **pg_lake_engine**: Common module shared by data lake extensions (depends on Apache Avro)
161+
- **pg_lake_iceberg**: Full Iceberg v2 protocol implementation with transactional support
162+
- **pg_lake_table**: Foreign data wrapper to query Parquet/CSV/JSON/Iceberg files
163+
- **pg_lake_copy**: COPY command extensions for importing/exporting to data lakes
164+
- **pg_lake**: Meta-extension that installs all required extensions via CASCADE
165+
166+
### External components
167+
- **pgduck_server**: Standalone server implementing PostgreSQL wire protocol, executes queries via DuckDB
168+
- **duckdb_pglake**: DuckDB extension adding PostgreSQL-compatible functions to DuckDB
169+
- **avro**: Apache Avro library (patched) for Iceberg metadata handling
170+
171+
## Important File Locations
172+
173+
### Build system
174+
- `Makefile`: Top-level build orchestration for all components
175+
- `shared.mk`: Shared Makefile rules for extensions
176+
- Each extension has its own `Makefile` following PGXS conventions
177+
178+
### Tests
179+
- `<extension>/tests/pytests/`: Main pytest test suites
180+
- `<extension>/tests/e2e/`: End-to-end tests requiring cloud storage
181+
- `<extension>/tests/isolation/`: Isolation tester tests for concurrency
182+
- `test_common/`: Shared test utilities and fixtures
183+
- `pytest.ini`: Root pytest configuration
184+
185+
### Documentation
186+
- `docs/building-from-source.md`: Detailed build instructions
187+
- `docs/iceberg-tables.md`: Iceberg table usage
188+
- `docs/query-data-lake-files.md`: Foreign table usage
189+
- `docs/data-lake-import-export.md`: COPY command usage
190+
191+
## Code Conventions
192+
193+
### C code (PostgreSQL extensions and pgduck_server)
194+
- **Indentation**: Use `pgindent` before commits (see `make reindent`)
195+
- **Naming**:
196+
- Variables/functions: `lower_case_with_underscores`
197+
- Macros/constants: `UPPER_CASE_WITH_UNDERSCORES`
198+
- Structs/enums: `CamelCase`
199+
- Global variables: Prefix with module identifier (e.g., `IcebergTableCache`)
200+
- **Comments**: Focus on "why" not "what"; use block comments for complex algorithms
201+
- **Typedefs**: Download from buildfarm via `make typedefs` for pgindent
202+
203+
### Python code
204+
- **Formatting**: Use `black` (see `make reindent` or `pipenv run black`)
205+
- **Style**: Follow pytest conventions for test naming and fixtures
206+
207+
### Before committing
208+
```bash
209+
# Format all code
210+
make reindent
211+
212+
# Check formatting
213+
make check-indent
214+
```
215+
216+
## Local Development with MinIO
217+
218+
For testing without cloud S3 latency, use MinIO locally:
219+
220+
```bash
221+
# Install and start MinIO
222+
brew install minio # or download from min.io
223+
minio server /tmp/data
224+
225+
# Access UI at http://localhost:9000
226+
# Create access key: testkey / testpassword
227+
# Create bucket: localbucket
228+
229+
# Add to ~/.aws/config:
230+
[services testing-minio]
231+
s3 =
232+
endpoint_url = http://localhost:9000
233+
234+
[profile minio]
235+
region = us-east-1
236+
services = testing-minio
237+
aws_access_key_id = testkey
238+
aws_secret_access_key = testpassword
239+
240+
# Configure pgduck_server (connect to port 5332)
241+
psql -p 5332 -h /tmp -c "
242+
CREATE SECRET s3testMinio (
243+
TYPE S3,
244+
KEY_ID 'testkey',
245+
SECRET 'testpassword',
246+
ENDPOINT 'localhost:9000',
247+
SCOPE 's3://localbucket',
248+
URL_STYLE 'path',
249+
USE_SSL false
250+
);"
251+
252+
# Use in PostgreSQL
253+
psql -c "SET pg_lake_iceberg.default_location_prefix TO 's3://localbucket'"
254+
```
255+
256+
## Common Development Workflows
257+
258+
### Adding new functionality to an extension
259+
1. Read existing code in `<extension>/src/` to understand patterns
260+
2. Modify C source files in `src/`
261+
3. If adding SQL functions, update `<extension>--<version>.sql`
262+
4. Add pytest tests in `tests/pytests/`
263+
5. Build and test:
264+
```bash
265+
make install-<extension>
266+
make check-<extension>
267+
```
268+
269+
### Creating a version upgrade script
270+
```bash
271+
# Bump all extension versions at once
272+
python tools/bump_extension_versions.py 3.1
273+
274+
# This creates upgrade stubs like: pg_lake_engine--3.0--3.1.sql
275+
```
276+
277+
### Debugging query execution
278+
```bash
279+
# Connect to PostgreSQL and check pg_lake query plans
280+
psql -c "EXPLAIN (VERBOSE) SELECT * FROM iceberg_table"
281+
282+
# Connect directly to pgduck_server to see DuckDB execution
283+
psql -p 5332 -h /tmp
284+
postgres=> SELECT * FROM duckdb_settings();
285+
postgres=> EXPLAIN SELECT ...;
286+
```
287+
288+
### Viewing Iceberg metadata
289+
```sql
290+
-- Show all Iceberg tables and their metadata locations
291+
SELECT table_name, metadata_location FROM iceberg_tables;
292+
293+
-- View Iceberg snapshots
294+
SELECT * FROM iceberg_snapshots('<table_name>');
295+
```
296+
297+
## Key PostgreSQL GUCs (Settings)
298+
299+
```sql
300+
-- Required: preload base extension in postgresql.conf
301+
shared_preload_libraries = 'pg_extension_base'
302+
303+
-- Iceberg settings
304+
pg_lake_iceberg.default_location_prefix = 's3://bucket/prefix'
305+
```
306+
307+
## CI and Testing Notes
308+
309+
- JDBC driver path must be set for Spark verification tests: `JDBC_DRIVER_PATH=/usr/share/java/postgresql.jar`
310+
- Java 21+ required for Polaris REST catalog tests
311+
- Azure tests require `azurite` (install via npm: `npm install -g azurite`)

0 commit comments

Comments
 (0)