Skip to content

This is the multi-page printable view of this section. .

Return to the regular view of this page.

v0.1 Archive: Welcome to pg_mooncake (Delta Lake)

Historical documentation archive for pg_mooncake v0.1 (USING columnstore + Delta Lake storage engine).
Legacy pg_mooncake v0.1 Archive (Delta Lake Engine)

This page documents pg_mooncake v0.1, which used direct CREATE TABLE ... USING columnstore DDL and Delta Lake storage. Starting in v0.2 and PGSTY’s v0.3.0 release, pg_mooncake uses moonlink real-time columnstore mirrors (CALL mooncake.create_table + USING mooncake) and Apache Iceberg v2. For current documentation, see Introduction and v0.1 vs v0.2 & v0.3.

pg_mooncake v0.1 is the first-generation PostgreSQL extension that added USING columnstore tables backed by Delta Lake and DuckDB (pg_duckdb) for analytical workloads.

pg_mooncake Venn Diagram

Key Features of v0.1

  1. Transactional INSERT, UPDATE, and DELETE directly on USING columnstore tables
  2. Columnar Parquet + Delta Lake metadata stored on local disk or S3 / R2 object storage
  3. Vectorized analytical execution via embedded DuckDB
  4. Seamless joins between regular PostgreSQL heap tables and USING columnstore tables

v0.1 Archive Contents

1 - v0.1 Architecture (Delta Lake & USING columnstore)

Architecture of pg_mooncake v0.1: Table Access Method (USING columnstore), Parquet + Delta Lake metadata, and DuckDB read/write paths.
Legacy pg_mooncake v0.1 Archive (Delta Lake Engine)

This page documents pg_mooncake v0.1, which used direct CREATE TABLE ... USING columnstore DDL and Delta Lake storage. Starting in v0.2 and PGSTY’s v0.3.0 release, pg_mooncake uses moonlink real-time columnstore mirrors (CALL mooncake.create_table + USING mooncake) and Apache Iceberg v2. For current documentation, see Introduction and v0.1 vs v0.2 & v0.3.

In pg_mooncake v0.1, table metadata is stored inside PostgreSQL catalogs for transactional consistency, while columnstore data is written as Parquet files with Delta Lake metadata on local disk or object storage.

Columnstore tables are exposed as a PostgreSQL Table Access Method (USING columnstore). Queries involving columnstore tables are routed from PostgreSQL to DuckDB and streamed back via pg_duckdb.

Read Path

Queries involving USING columnstore tables are routed from PostgreSQL to DuckDB, with minor SQL query rewrites applied to bridge syntax differences.

v0.1 Architecture Diagram

pg_mooncake v0.1 caches Parquet metadata and column statistics inside PostgreSQL (mooncake.data_files) and uses them for segment elimination during table scans.

Write Path

In v0.1, transactional INSERT, UPDATE, and DELETE statements run directly on USING columnstore tables. To support DELETE and UPDATE, an implicit row_id column consisting of (file_id, row_offset) identifies rows during table scans.

Each write transaction creates a new Parquet file. Because of this, pg_mooncake v0.1 is designed for batched writes (100,000–1,000,000 rows) rather than small, frequent OLTP transactions.

Local Disk Caching

On writes, data is cached on local disk (mooncake.enable_local_cache = true) to accelerate queries on recently written data before or alongside cloud object storage uploads.

2 - v0.1 Get Started

Historical v0.1 installation guide using Docker or source builds for PostgreSQL 14–17.
Legacy pg_mooncake v0.1 Archive (Delta Lake Engine)

This page documents pg_mooncake v0.1, which used direct CREATE TABLE ... USING columnstore DDL and Delta Lake storage. Starting in v0.2 and PGSTY’s v0.3.0 release, pg_mooncake uses moonlink real-time columnstore mirrors (CALL mooncake.create_table + USING mooncake) and Apache Iceberg v2. For current documentation, see Introduction and v0.1 vs v0.2 & v0.3.

Historical Docker Image (Obsolete)

docker pull mooncakelabs/pg_mooncake
docker run --name mooncake-demo -e POSTGRES_HOST_AUTH_METHOD=trust -p 5432:5432 -d mooncakelabs/pg_mooncake
docker run -it --rm --link mooncake-demo:postgres mooncakelabs/pg_mooncake psql -h postgres -U postgres

Building v0.1 from Source

git submodule update --init --recursive
make release -j$(nproc)
make install

Create the Extension

CREATE EXTENSION pg_mooncake;

3 - v0.1 Hello World (USING columnstore)

Basic operations on v0.1 USING columnstore tables: CREATE TABLE, INSERT, UPDATE, DELETE, transactions, and joins.
Legacy pg_mooncake v0.1 Archive (Delta Lake Engine)

This page documents pg_mooncake v0.1, which used direct CREATE TABLE ... USING columnstore DDL and Delta Lake storage. Starting in v0.2 and PGSTY’s v0.3.0 release, pg_mooncake uses moonlink real-time columnstore mirrors (CALL mooncake.create_table + USING mooncake) and Apache Iceberg v2. For current documentation, see Introduction and v0.1 vs v0.2 & v0.3.

Create the Extension

CREATE EXTENSION pg_mooncake;

Create a USING columnstore Table (v0.1)

CREATE TABLE user_activity (
    user_id BIGINT,
    activity_type TEXT,
    activity_timestamp TIMESTAMP,
    duration INT
) USING columnstore;

Inserts, Updates, and Deletes

INSERT INTO user_activity VALUES
    (1, 'login', '2024-01-01 08:00:00', 120),
    (2, 'page_view', '2024-01-01 08:05:00', 30),
    (3, 'logout', '2024-01-01 08:30:00', 60),
    (4, 'error', '2024-01-01 08:13:00', 60);

UPDATE user_activity SET duration = 180 WHERE user_id = 1;
DELETE FROM user_activity WHERE user_id = 2;

Transactions

BEGIN;
INSERT INTO user_activity VALUES
    (5, 'login', '2024-01-02 10:00:00', 200),
    (6, 'login', '2024-01-02 10:30:00', 90);
ROLLBACK;
SELECT * FROM user_activity;

Joins

-- Between two columnstore tables
SELECT * FROM user_activity ua
JOIN user_activity ua2 ON ua.user_id = ua2.user_id;

-- Between a regular PostgreSQL table and a columnstore table
SELECT * FROM users JOIN user_activity ON users.user_id = user_activity.user_id;

4 - v0.1 Cloud Storage (mooncake.create_secret)

Configuring S3 and Cloudflare R2 buckets in pg_mooncake v0.1 using mooncake.create_secret and mooncake.default_bucket.
Legacy pg_mooncake v0.1 Archive (Delta Lake Engine)

This page documents pg_mooncake v0.1, which used direct CREATE TABLE ... USING columnstore DDL and Delta Lake storage. Starting in v0.2 and PGSTY’s v0.3.0 release, pg_mooncake uses moonlink real-time columnstore mirrors (CALL mooncake.create_table + USING mooncake) and Apache Iceberg v2. For current documentation, see Introduction and v0.1 vs v0.2 & v0.3.

Current v0.3.0 Cloud Storage Setup

In v0.2 and v0.3.0, credentials are inserted into duckdb.secrets rather than mooncake.create_secret. See Iceberg & Object Storage.

S3 Buckets (v0.1)

SELECT mooncake.create_secret(
    '<name>', 'S3', '<key_id>', '<secret>',
    '{"REGION": "<s3-region>", "SCOPE": "s3://<bucket>"}'
);

For S3 Express buckets:

SELECT mooncake.create_secret(
    '<name>', 'S3', '<key_id>', '<secret>',
    '{"ENDPOINT": "s3express-use1-az6.us-east-1.amazonaws.com", "REGION": "us-east-1"}'
);

Cloudflare R2 Buckets (v0.1)

SELECT mooncake.create_secret(
    '<name>', 'S3', '<key_id>', '<secret>',
    '{"ENDPOINT": "<ACCOUNT_ID>.r2.cloudflarestorage.com"}'
);

Set Default Bucket

ALTER DATABASE <database> SET mooncake.default_bucket = 's3://<bucket>';
SHOW mooncake.default_bucket;

S3 and R2 Parameters

Parameter Description Required
name Name for your credentials Yes
key_id S3 / R2 access key ID Yes
secret S3 / R2 secret access key Yes
REGION AWS region where your bucket is located Yes
ENDPOINT Custom endpoint for R2 or S3 Express Yes (R2 / S3 Express)
SCOPE Bucket prefix path to scope credentials (e.g. s3://mybucket) No

5 - v0.1 on Neon Postgres

Historical guide for enabling pg_mooncake v0.1 on Neon serverless PostgreSQL.
Legacy pg_mooncake v0.1 Archive (Delta Lake Engine)

This page documents pg_mooncake v0.1, which used direct CREATE TABLE ... USING columnstore DDL and Delta Lake storage. Starting in v0.2 and PGSTY’s v0.3.0 release, pg_mooncake uses moonlink real-time columnstore mirrors (CALL mooncake.create_table + USING mooncake) and Apache Iceberg v2. For current documentation, see Introduction and v0.1 vs v0.2 & v0.3.

pg_mooncake v0.1 was offered as an experimental extension on Neon.

Enable the Extension on Neon (v0.1)

SET neon.allow_unstable_extensions = 'true';
CREATE EXTENSION pg_mooncake;

Cloud Storage Requirement

Because Neon does not allow extensions to persist columnstore files on local compute disk, using pg_mooncake v0.1 on Neon required configuring an external S3 or Cloudflare R2 bucket as described in v0.1 Cloud Storage, and disabling Neon’s scale-to-zero feature to preserve local metadata caches.

6 - v0.1 Reference Overview

Reference index for pg_mooncake v0.1 DDL, Delta Lake metadata catalogs, GUC parameters, data loading, and external file queries.
Legacy pg_mooncake v0.1 Archive (Delta Lake Engine)

This page documents pg_mooncake v0.1, which used direct CREATE TABLE ... USING columnstore DDL and Delta Lake storage. Starting in v0.2 and PGSTY’s v0.3.0 release, pg_mooncake uses moonlink real-time columnstore mirrors (CALL mooncake.create_table + USING mooncake) and Apache Iceberg v2. For current documentation, see Introduction and v0.1 vs v0.2 & v0.3.

7 - v0.1 Columnstore DDL (USING columnstore)

Creating and dropping USING columnstore tables in pg_mooncake v0.1, supported data types, constraints, and default_table_access_method.
Legacy pg_mooncake v0.1 Archive (Delta Lake Engine)

This page documents pg_mooncake v0.1, which used direct CREATE TABLE ... USING columnstore DDL and Delta Lake storage. Starting in v0.2 and PGSTY’s v0.3.0 release, pg_mooncake uses moonlink real-time columnstore mirrors (CALL mooncake.create_table + USING mooncake) and Apache Iceberg v2. For current documentation, see Introduction and v0.1 vs v0.2 & v0.3.

Create Columnstore Table (v0.1)

In v0.1, columnstore tables were created directly with the USING columnstore clause:

CREATE TABLE users_columnstore (
    id INT,
    name TEXT,
    created_at TIMESTAMP
) USING columnstore;

-- Or via CREATE TABLE ... AS SELECT
CREATE TABLE users_columnstore USING columnstore AS
    SELECT * FROM users_rowstore;

Drop Columnstore Table

DROP TABLE users_columnstore;

Supported Features & Limitations in v0.1

  • Supported: INSERT, UPDATE, DELETE, ACID transactions, NOT NULL constraints, default values, and joins with heap tables.
  • Unsupported Data Types in v0.1: ENUM, uhugeint, JSONB (partial), and complex arrays.
  • Unsupported Constraints in v0.1: Primary keys, foreign keys, unique constraints, indexes, and partitioned tables (ALTER TABLE schema changes required recreating the table).

Setting columnstore as Default Table Access Method (v0.1)

SET default_table_access_method = 'columnstore';

8 - v0.1 Querying Columnstore Outside Postgres

Inspecting mooncake.columnstore_tables and mooncake.data_files in pg_mooncake v0.1 and querying Delta Lake tables from Python Polars.
Legacy pg_mooncake v0.1 Archive (Delta Lake Engine)

This page documents pg_mooncake v0.1, which used direct CREATE TABLE ... USING columnstore DDL and Delta Lake storage. Starting in v0.2 and PGSTY’s v0.3.0 release, pg_mooncake uses moonlink real-time columnstore mirrors (CALL mooncake.create_table + USING mooncake) and Apache Iceberg v2. For current documentation, see Introduction and v0.1 vs v0.2 & v0.3.

Current v0.3.0 Metadata Function

In v0.2 and v0.3.0, use SELECT * FROM mooncake.list_tables(); to inspect table LSNs and Apache Iceberg v2 warehouse locations.

Finding Table Locations (v0.1)

In v0.1, mooncake.columnstore_tables listed the Delta Lake directory path for each USING columnstore table:

SELECT * FROM mooncake.columnstore_tables;

Example output:

    table_name     |                                          path
-------------------+----------------------------------------------------------------------------------------
 users_columnstore | /usr/local/pgsql/data/mooncake_local_tables/mooncake_postgres_users_columnstore_16446/
 events            | s3://my-bucket/mooncake_postgres_events_16459/

Viewing Data Files (v0.1)

SELECT * FROM mooncake.data_files;

Accessing v0.1 Delta Lake Tables with Polars

import polars as pl

local_path = "/usr/local/pgsql/data/mooncake_local_tables/mooncake_postgres_users_columnstore_16446/"
df = pl.scan_delta(local_path)
print(df.collect())

9 - v0.1 GUC Configurations

Historical PostgreSQL GUC variables in pg_mooncake v0.1 (mooncake.default_bucket, maximum_memory, maximum_threads, enable_local_cache, enable_memory_metadata_cache).
Legacy pg_mooncake v0.1 Archive (Delta Lake Engine)

This page documents pg_mooncake v0.1, which used direct CREATE TABLE ... USING columnstore DDL and Delta Lake storage. Starting in v0.2 and PGSTY’s v0.3.0 release, pg_mooncake uses moonlink real-time columnstore mirrors (CALL mooncake.create_table + USING mooncake) and Apache Iceberg v2. For current documentation, see Introduction and v0.1 vs v0.2 & v0.3.

v0.1 GUC Parameters

-- Default cloud storage bucket
ALTER DATABASE <database_name> SET mooncake.default_bucket = 's3://my-bucket';

-- Memory allocated to DuckDB (v0.1.1+, default 4GB)
ALTER DATABASE <database_name> SET mooncake.maximum_memory = '6GB';
CALL mooncake.reset_duckdb();

-- Number of threads for DuckDB (v0.1.1+, default -1 = all cores)
ALTER DATABASE <database_name> SET mooncake.maximum_threads = -1;

-- Local write cache (default true)
ALTER DATABASE <database_name> SET mooncake.enable_local_cache = false;

-- Memory cache for Parquet metadata (default true)
ALTER DATABASE <database_name> SET mooncake.enable_memory_metadata_cache = false;

10 - v0.1 Load Data

Loading data into pg_mooncake v0.1 USING columnstore tables from Postgres tables, Parquet/CSV/JSON files, Iceberg/Delta tables, and HuggingFace.
Legacy pg_mooncake v0.1 Archive (Delta Lake Engine)

This page documents pg_mooncake v0.1, which used direct CREATE TABLE ... USING columnstore DDL and Delta Lake storage. Starting in v0.2 and PGSTY’s v0.3.0 release, pg_mooncake uses moonlink real-time columnstore mirrors (CALL mooncake.create_table + USING mooncake) and Apache Iceberg v2. For current documentation, see Introduction and v0.1 vs v0.2 & v0.3.

From PostgreSQL Heap Tables (v0.1)

INSERT INTO users_columnstore
SELECT * FROM users_rowstore;

From Parquet, CSV, or JSON Files (v0.1)

-- Using COPY
COPY users_columnstore FROM 's3://bucket/file.parquet';
COPY users_columnstore FROM 's3://bucket/folder/*.parquet';
COPY users_columnstore FROM 's3://bucket/data.csv' WITH (FORMAT 'csv', HEADER true);

-- Using INSERT with mooncake.read_parquet
INSERT INTO users_columnstore
SELECT * FROM mooncake.read_parquet('s3://bucket/file.parquet') AS (
    id INT,
    name TEXT,
    created_at TIMESTAMP
);

From Iceberg, Delta Lake, or HuggingFace (v0.1)

-- Iceberg
INSERT INTO users_columnstore
SELECT * FROM iceberg_scan('s3://bucket/table') AS (id INT, name TEXT, created_at TIMESTAMP);

-- Delta Lake
INSERT INTO users_columnstore
SELECT * FROM delta_scan('s3://bucket/table') AS (id INT, name TEXT, created_at TIMESTAMP);

-- HuggingFace
COPY users_columnstore FROM 'hf://datasets/OpenLeecher/lmsys_chat_1m_clean/data/train-00000-of-00004.parquet';

11 - v0.1 Query External Files

Querying external Parquet, CSV, JSON, Iceberg, and Delta Lake files in pg_mooncake v0.1 using mooncake.read_parquet, mooncake.read_csv, and mooncake.read_json.
Legacy pg_mooncake v0.1 Archive (Delta Lake Engine)

This page documents pg_mooncake v0.1, which used direct CREATE TABLE ... USING columnstore DDL and Delta Lake storage. Starting in v0.2 and PGSTY’s v0.3.0 release, pg_mooncake uses moonlink real-time columnstore mirrors (CALL mooncake.create_table + USING mooncake) and Apache Iceberg v2. For current documentation, see Introduction and v0.1 vs v0.2 & v0.3.

Current v0.3.0 External File Readers

In v0.2 and v0.3.0, use duckdb.read_parquet, duckdb.read_csv, duckdb.iceberg_scan, and duckdb.delta_scan as documented in Query Your Data Lake in Postgres.

Querying Parquet, CSV, and JSON (v0.1)

-- Parquet
SELECT user_id, event_type, created_at
FROM mooncake.read_parquet('s3://bucket/events.parquet') AS (
    user_id INT,
    event_type TEXT,
    created_at TIMESTAMP
)
WHERE created_at > '2023-01-01';

-- CSV
SELECT r['user_id']::INT, r['event_type'], r['created_at']::TIMESTAMP
FROM mooncake.read_csv('s3://bucket/events.csv', AUTO_DETECT=TRUE) r
WHERE r['created_at']::TIMESTAMP > '2023-01-01';

-- JSON
SELECT
    json_extract_path_text(data, 'user_id')::INT AS user_id,
    json_extract_path_text(data, 'event_type') AS event_type
FROM mooncake.read_json('s3://bucket/events.json') AS (data JSONB);

Querying Iceberg and Delta Lake (v0.1)

SELECT * FROM iceberg_scan('s3://bucket/iceberg_table') AS (
    user_id INT, event_type TEXT, created_at TIMESTAMP
);

SELECT * FROM delta_scan('s3://bucket/delta_table') AS (
    user_id INT, event_type TEXT, created_at TIMESTAMP
);

12 - v0.1 Supported Queries & Query Shapes

Analytical query patterns, unsupported PostgreSQL functions, and DuckDB function placeholders in pg_mooncake v0.1.
Legacy pg_mooncake v0.1 Archive (Delta Lake Engine)

This page documents pg_mooncake v0.1, which used direct CREATE TABLE ... USING columnstore DDL and Delta Lake storage. Starting in v0.2 and PGSTY’s v0.3.0 release, pg_mooncake uses moonlink real-time columnstore mirrors (CALL mooncake.create_table + USING mooncake) and Apache Iceberg v2. For current documentation, see Introduction and v0.1 vs v0.2 & v0.3.

For the updated v0.3.0 guide on query shapes and DuckDB 2.0 execution, see Query Columnstore & Query Shapes.

Analytical Aggregations & Time-Series (v0.1)

SELECT
    region,
    date_trunc('quarter', sale_date) AS quarter,
    SUM(amount) AS total_sales,
    COUNT(*) AS num_transactions,
    AVG(amount) AS avg_sale
FROM sales_columnstore
WHERE sale_date BETWEEN '2023-01-01' AND '2023-12-31'
GROUP BY region, quarter
ORDER BY region, quarter;

Exposing DuckDB Functions in Postgres (v0.1)

CREATE OR REPLACE FUNCTION time_bucket(interval, timestamp)
RETURNS timestamp AS $$
BEGIN
    RAISE EXCEPTION 'this is a placeholder for columnstore';
END;
$$ LANGUAGE plpgsql;

13 - v0.1 Data Archiving

Historical v0.1 pattern for moving old rows from heap tables into a USING columnstore archive table.
Legacy pg_mooncake v0.1 Archive (Delta Lake Engine)

This page documents pg_mooncake v0.1, which used direct CREATE TABLE ... USING columnstore DDL and Delta Lake storage. Starting in v0.2 and PGSTY’s v0.3.0 release, pg_mooncake uses moonlink real-time columnstore mirrors (CALL mooncake.create_table + USING mooncake) and Apache Iceberg v2. For current documentation, see Introduction and v0.1 vs v0.2 & v0.3.

Current v0.3.0 Partition Archiving Pattern

In v0.2 and v0.3.0, you no longer need manual INSERT INTO ... DELETE FROM batch scripts—simply mirror a partitioned table with CALL mooncake.create_table and DROP old partitions. See Archiving Large Postgres Tables.

How Archiving Worked in v0.1

-- Create columnstore archive table
CREATE TABLE orders_archive (
    order_id INT,
    customer_id INT,
    order_date TIMESTAMP,
    amount NUMERIC(10, 2)
) USING columnstore;

-- Move old data to archive
INSERT INTO orders_archive
SELECT * FROM orders
WHERE order_date < CURRENT_DATE - INTERVAL '1 year';

-- Delete from source and reclaim space
DELETE FROM orders
WHERE order_date < CURRENT_DATE - INTERVAL '1 year';
VACUUM FULL orders;

14 - v0.1 Operational Analytics (Trigger + pg_cron Staging)

Historical v0.1 staging-table trigger and pg_cron pattern for near-real-time analytics before Moonlink was introduced.
Legacy pg_mooncake v0.1 Archive (Delta Lake Engine)

This page documents pg_mooncake v0.1, which used direct CREATE TABLE ... USING columnstore DDL and Delta Lake storage. Starting in v0.2 and PGSTY’s v0.3.0 release, pg_mooncake uses moonlink real-time columnstore mirrors (CALL mooncake.create_table + USING mooncake) and Apache Iceberg v2. For current documentation, see Introduction and v0.1 vs v0.2 & v0.3.

Replaced by mooncake.create_table in v0.2 / v0.3.0

In v0.1, syncing a rowstore table to a USING columnstore table required triggers, a staging table, and a pg_cron job. In v0.2 and v0.3.0, all of this is replaced by a single CALL mooncake.create_table('orders_analytics', 'orders');. See Real-Time Operational Analytics.

Historical v0.1 Staging & Trigger Pattern

-- 1. Staging table
CREATE TABLE orders_staging (
    order_id INT PRIMARY KEY,
    customer_id INT,
    quantity INT,
    unit_price DECIMAL(10, 2),
    order_date TIMESTAMP
);

-- 2. Columnstore table
CREATE TABLE orders_analytics (
    order_id INT,
    customer_id INT,
    quantity INT,
    unit_price DECIMAL(10, 2),
    order_date TIMESTAMP,
    last_updated TIMESTAMP
) USING columnstore;

-- 3. Periodic batch sync function scheduled via pg_cron
CREATE OR REPLACE FUNCTION sync_to_columnstore() RETURNS void AS $$
BEGIN
    INSERT INTO orders_analytics
    SELECT s.*, NOW() FROM orders_staging s;
    TRUNCATE orders_staging;
END;
$$ LANGUAGE plpgsql;

15 - v0.1 Data Warehousing & dbt

Historical v0.1 guide for daily aggregation pipelines and dbt integration using default_table_access_method = ‘columnstore’.
Legacy pg_mooncake v0.1 Archive (Delta Lake Engine)

This page documents pg_mooncake v0.1, which used direct CREATE TABLE ... USING columnstore DDL and Delta Lake storage. Starting in v0.2 and PGSTY’s v0.3.0 release, pg_mooncake uses moonlink real-time columnstore mirrors (CALL mooncake.create_table + USING mooncake) and Apache Iceberg v2. For current documentation, see Introduction and v0.1 vs v0.2 & v0.3.

Daily Aggregation Pipeline (v0.1)

CREATE TABLE daily_sales_summary (
    date DATE,
    product_id INT,
    category_id INT,
    region_id INT,
    total_quantity INT,
    total_revenue DECIMAL(18, 2)
) USING columnstore;

Integration with dbt (v0.1)

In v0.1, dbt models could target USING columnstore by setting default_table_access_method in an on-run-start hook:

{% macro on_run_start() %}
  {% set query %}
    SET default_table_access_method = 'columnstore';
  {% endset %}
  {% do run_query(query) %}
{% endmacro %}