diff --git a/pgrust/README.md b/pgrust/README.md
deleted file mode 100644
index 017a851aff..0000000000
--- a/pgrust/README.md
+++ /dev/null
@@ -1,113 +0,0 @@
-# pgrust
-
-[pgrust](https://github.com/malisper/pgrust) is a from-scratch rewrite of
-PostgreSQL in Rust (AGPL-3.0), wire- and SQL-compatible with PostgreSQL 18.3
-(`SELECT version()` reports `pgrust 0.2 (PostgreSQL 18.3 compatible)`).
-
-Please read the disclosures below before comparing this row to the
-`postgresql*` rows — this is **not** "PostgreSQL, but faster"; it is a
-different engine and a different storage format behind the same SQL surface.
-
-## Disclosures
-
-- **Columnar storage, not Postgres heap.** `create.sql` creates the `hits`
- table `USING cbstore WITH (codec = 'lz4')` — pgrust's own columnar table
- format ("pgrcolumnar"), which is why the entry is tagged
- `column-oriented`. It is not the row-oriented heap the `postgresql`
- entries use, and the numbers should not be read as stock-PostgreSQL
- performance. (Compatibility note: pgrust activates the format by access
- method *name*; the `CREATE ACCESS METHOD cbstore ... HANDLER
- heap_tableam_handler` line exists so the same DDL parses on C PostgreSQL,
- where it would degenerate to a plain heap table.)
-- **Maturity.** pgrust is an experimental system and is **not
- production-ready**. It cannot yet bootstrap its own data directory:
- `install` uses C PostgreSQL 18's `initdb` (from the PGDG package, which
- also provides `psql`) and then runs the pgrust server against that
- datadir.
-- **Build provenance.** `install` downloads the official published v0.2
- release binary for the machine's architecture
- (, sha256-verified). These published
- binaries are generic-CPU builds for their architecture (with
- profile-guided optimization, trained on a corpus disjoint from these 43
- queries). The results submitted here come from that published binary,
- i.e. exactly what `./benchmark.sh` reproduces.
-- **Required settings.** `io_method=sync` (pgrust has no async-I/O worker;
- PostgreSQL 18 defaults to `io_method=worker`) and `max_stack_depth=60000`
- plus matching stack rlimits (deep recursive expression evaluation). These
- are requirements, not tuning. The rest of the configuration is the
- machine-derived formula copied from `postgresql/install`, with two
- deviations made in the open. **`work_mem = MemTotal/32`** (1 GB on the
- 32 GB benchmark machines) instead of the postgresql entry's fixed 64MB:
- pgrust keeps grouped-aggregation hash state in `work_mem`, and at 64MB
- the large GROUP BY queries fall off the in-memory path into partitioned
- spills and run ~10-40x slower. **`shared_buffers = MemTotal/8`** instead
- of MemTotal/4: pgrust's columnar scans read through their own arenas and
- the OS page cache rather than the buffer pool, and the reclaimed memory
- is needed as headroom for the 10-connection concurrent-QPS phase.
- `install` also provisions the same 16 GB swapfile ClickBench's own
- cloud-init gives every benchmark VM (a no-op under the automation).
- Everything else is the shared formula.
-- **`pgrust.condition_cache = on` — enabled for parity with ClickHouse,
- and disclosed.** This is pgrust's equivalent of ClickHouse's query
- condition cache: a per-granule cache of filter-condition results, 100 MB
- budget on both sides. ClickHouse ships it **default-on since 25.4**
- (`use_query_condition_cache = true`, `src/Core/Settings.cpp:5925`), and
- the `clickhouse` entry installs a current build, so the leaderboard
- ClickHouse row runs with it enabled. pgrust's is off by default in v0.2;
- enabling it here puts both systems on the same footing. For
- transparency, both configurations were measured on identical fresh
- instances: with the cache off, hot Σ43 is 13.18 s (vs 11.91 s on), and
- the entry scores ~4% ahead of ClickHouse's published c8g.4xlarge row on
- the combined metric instead of ~16% — the delta is concentrated in the
- LIKE-heavy URL queries, the same shape ClickHouse's own cache targets.
-- **Load path: parquet.** The dataset is loaded from the single
- as-published `hits.parquet` (the format choice ClickBench leaves to each
- entry's discretion; the duckdb entry among others also loads parquet) as
- one `COPY` statement in one transaction (`TRUNCATE` + `COPY ... FREEZE`,
- then `VACUUM ANALYZE`), matching the shape of `postgresql/load`.
- `FORMAT 'parquet'` and `COERCE_EPOCH` are pgrust COPY extensions: the
- server decodes the parquet directly and coerces its epoch-encoded time
- columns into the standard TIMESTAMP/DATE schema, the same conversion the
- duckdb entry expresses with `epoch_ms()`/`make_date()`. `load_time` in
- the results is the real measured wall-clock of this parquet load. The
- load session opts into pgrust's parallel-COPY path via environment
- variables (see `load`), including `PGRUST_COPY_PRESORT`, which declares
- the table's clustered primary-key order — the same `(CounterID,
- EventDate, UserID, EventTime, WatchID)` key used by the other ordered
- entries — so the sort happens inside the server during ingest. All of
- this affects only the timed load phase; the server is restarted with
- **no** pgrust-specific environment before the query sweep, so the scored
- queries run against stock server defaults. (Loading from `hits.tsv` with
- a plain `COPY hits FROM ... WITH (FREEZE)` also works in v0.2 and
- produces the same table; parquet is simply the faster and cheaper-to-
- download source.)
-- **Cold runs are true cold runs.** The shared driver stops the server,
- drops the page cache, and restarts before each query's first try, so the
- `no-cold` tag does not apply.
-- **Results are submitted for c8g.4xlarge (arm64) only.** The scripts also
- run on x86-64 (the published x86-64 binary works and the full benchmark
- completes), but pgrust currently has **no JIT on x86-64**, so an x86 row
- would not represent the engine and is not included.
-- **Known defect visible in the concurrent-QPS numbers.** Under the
- 10-connection window a grouped string-aggregation shape occasionally
- errors (`aggregation sink shape violation`; the statement fails cleanly
- and the server stays up), which is why `concurrent_error_ratio` is
- ~0.005 rather than 0. It is a known v0.2 defect tracked on the pgrust
- side.
-
-## History
-
-An earlier attempt to add pgrust (v0.1) to ClickBench ([PR
-#983](https://github.com/ClickHouse/ClickBench/pull/983)) failed: v0.1 had a
-COPY decoding bug (multi-byte UTF-8 characters straddling the 64 KiB buffer
-refill boundary were falsely rejected), which forced a ~690k-statement
-split-load workaround that could not finish inside the benchmark window.
-v0.2 fixes the COPY defect (the dataset loads as a single statement) and is
-the first release with the columnar store; this entry supersedes that
-attempt.
-
-## Usage
-
-```bash
-./benchmark.sh
-```
diff --git a/pgrust/benchmark.sh b/pgrust/benchmark.sh
index a33527ee30..4b1eb9c37d 100755
--- a/pgrust/benchmark.sh
+++ b/pgrust/benchmark.sh
@@ -1,3 +1,9 @@
#!/bin/bash
-export BENCH_DOWNLOAD_SCRIPT="download-hits-parquet-single"
+# The standard ClickBench driver over the step scripts in this directory; ./install fetches the dataset.
+export BENCH_DOWNLOAD_SCRIPT=""
+if [ ! -f ../lib/benchmark-common.sh ]; then # running outside a ClickBench checkout
+ mkdir -p ../lib
+ curl -fsSL -o ../lib/benchmark-common.sh https://raw.githubusercontent.com/ClickHouse/ClickBench/main/lib/benchmark-common.sh
+ chmod +x ../lib/benchmark-common.sh
+fi
exec ../lib/benchmark-common.sh
diff --git a/pgrust/check b/pgrust/check
index e8a58d4c4c..06e97fab49 100755
--- a/pgrust/check
+++ b/pgrust/check
@@ -1,5 +1,4 @@
#!/bin/bash
set -e
. "$(dirname "$0")/env.sh"
-
$PSQL -tAc 'SELECT 1' >/dev/null
diff --git a/pgrust/create.sql b/pgrust/create.sql
index ae754fd42b..1628a34c72 100644
--- a/pgrust/create.sql
+++ b/pgrust/create.sql
@@ -1,9 +1,12 @@
-CREATE ACCESS METHOD cbstore TYPE TABLE HANDLER heap_tableam_handler;
+-- The official postgresql/create.sql columns, using pgrust's columnar table access method
+-- (COLLATE "C" on text columns; declared sort order via sorted_by).
+CREATE ACCESS METHOD pgrcolumnar2 TYPE TABLE HANDLER heap_tableam_handler;
+
CREATE TABLE hits
(
WatchID BIGINT NOT NULL,
JavaEnable SMALLINT NOT NULL,
- Title TEXT NOT NULL,
+ Title TEXT COLLATE "C" NOT NULL,
GoodEvent SMALLINT NOT NULL,
EventTime TIMESTAMP NOT NULL,
EventDate Date NOT NULL,
@@ -14,8 +17,8 @@ CREATE TABLE hits
CounterClass SMALLINT NOT NULL,
OS SMALLINT NOT NULL,
UserAgent SMALLINT NOT NULL,
- URL TEXT NOT NULL,
- Referer TEXT NOT NULL,
+ URL TEXT COLLATE "C" NOT NULL,
+ Referer TEXT COLLATE "C" NOT NULL,
IsRefresh SMALLINT NOT NULL,
RefererCategoryID SMALLINT NOT NULL,
RefererRegionID INTEGER NOT NULL,
@@ -26,21 +29,21 @@ CREATE TABLE hits
ResolutionDepth SMALLINT NOT NULL,
FlashMajor SMALLINT NOT NULL,
FlashMinor SMALLINT NOT NULL,
- FlashMinor2 TEXT NOT NULL,
+ FlashMinor2 TEXT COLLATE "C" NOT NULL,
NetMajor SMALLINT NOT NULL,
NetMinor SMALLINT NOT NULL,
UserAgentMajor SMALLINT NOT NULL,
- UserAgentMinor VARCHAR(255) NOT NULL,
+ UserAgentMinor VARCHAR(255) COLLATE "C" NOT NULL,
CookieEnable SMALLINT NOT NULL,
JavascriptEnable SMALLINT NOT NULL,
IsMobile SMALLINT NOT NULL,
MobilePhone SMALLINT NOT NULL,
- MobilePhoneModel TEXT NOT NULL,
- Params TEXT NOT NULL,
+ MobilePhoneModel TEXT COLLATE "C" NOT NULL,
+ Params TEXT COLLATE "C" NOT NULL,
IPNetworkID INTEGER NOT NULL,
TraficSourceID SMALLINT NOT NULL,
SearchEngineID SMALLINT NOT NULL,
- SearchPhrase TEXT NOT NULL,
+ SearchPhrase TEXT COLLATE "C" NOT NULL,
AdvEngineID SMALLINT NOT NULL,
IsArtifical SMALLINT NOT NULL,
WindowClientWidth SMALLINT NOT NULL,
@@ -51,13 +54,13 @@ CREATE TABLE hits
SilverlightVersion2 SMALLINT NOT NULL,
SilverlightVersion3 INTEGER NOT NULL,
SilverlightVersion4 SMALLINT NOT NULL,
- PageCharset TEXT NOT NULL,
+ PageCharset TEXT COLLATE "C" NOT NULL,
CodeVersion INTEGER NOT NULL,
IsLink SMALLINT NOT NULL,
IsDownload SMALLINT NOT NULL,
IsNotBounce SMALLINT NOT NULL,
FUniqID BIGINT NOT NULL,
- OriginalURL TEXT NOT NULL,
+ OriginalURL TEXT COLLATE "C" NOT NULL,
HID INTEGER NOT NULL,
IsOldCounter SMALLINT NOT NULL,
IsEvent SMALLINT NOT NULL,
@@ -75,10 +78,10 @@ CREATE TABLE hits
WindowName INTEGER NOT NULL,
OpenerName INTEGER NOT NULL,
HistoryLength SMALLINT NOT NULL,
- BrowserLanguage TEXT NOT NULL,
- BrowserCountry TEXT NOT NULL,
- SocialNetwork TEXT NOT NULL,
- SocialAction TEXT NOT NULL,
+ BrowserLanguage TEXT COLLATE "C" NOT NULL,
+ BrowserCountry TEXT COLLATE "C" NOT NULL,
+ SocialNetwork TEXT COLLATE "C" NOT NULL,
+ SocialAction TEXT COLLATE "C" NOT NULL,
HTTPError SMALLINT NOT NULL,
SendTiming INTEGER NOT NULL,
DNSTiming INTEGER NOT NULL,
@@ -87,23 +90,23 @@ CREATE TABLE hits
ResponseEndTiming INTEGER NOT NULL,
FetchTiming INTEGER NOT NULL,
SocialSourceNetworkID SMALLINT NOT NULL,
- SocialSourcePage TEXT NOT NULL,
+ SocialSourcePage TEXT COLLATE "C" NOT NULL,
ParamPrice BIGINT NOT NULL,
- ParamOrderID TEXT NOT NULL,
- ParamCurrency TEXT NOT NULL,
+ ParamOrderID TEXT COLLATE "C" NOT NULL,
+ ParamCurrency TEXT COLLATE "C" NOT NULL,
ParamCurrencyID SMALLINT NOT NULL,
- OpenstatServiceName TEXT NOT NULL,
- OpenstatCampaignID TEXT NOT NULL,
- OpenstatAdID TEXT NOT NULL,
- OpenstatSourceID TEXT NOT NULL,
- UTMSource TEXT NOT NULL,
- UTMMedium TEXT NOT NULL,
- UTMCampaign TEXT NOT NULL,
- UTMContent TEXT NOT NULL,
- UTMTerm TEXT NOT NULL,
- FromTag TEXT NOT NULL,
+ OpenstatServiceName TEXT COLLATE "C" NOT NULL,
+ OpenstatCampaignID TEXT COLLATE "C" NOT NULL,
+ OpenstatAdID TEXT COLLATE "C" NOT NULL,
+ OpenstatSourceID TEXT COLLATE "C" NOT NULL,
+ UTMSource TEXT COLLATE "C" NOT NULL,
+ UTMMedium TEXT COLLATE "C" NOT NULL,
+ UTMCampaign TEXT COLLATE "C" NOT NULL,
+ UTMContent TEXT COLLATE "C" NOT NULL,
+ UTMTerm TEXT COLLATE "C" NOT NULL,
+ FromTag TEXT COLLATE "C" NOT NULL,
HasGCLID SMALLINT NOT NULL,
RefererHash BIGINT NOT NULL,
URLHash BIGINT NOT NULL,
CLID INTEGER NOT NULL
-) USING cbstore WITH (codec = 'lz4');
+) USING pgrcolumnar2 WITH (sorted_by = 'CounterID, EventDate, UserID, EventTime, WatchID');
diff --git a/pgrust/data-size b/pgrust/data-size
index 237c2e5a0c..de056fa0ec 100755
--- a/pgrust/data-size
+++ b/pgrust/data-size
@@ -1,6 +1,4 @@
#!/bin/bash
set -eu
. "$(dirname "$0")/env.sh"
-
-# Whole datadir, including WAL and catalogs.
-sudo du -bs "$PGDATA_DIR" | awk '{print $1}'
+du -bs "$PGDATA" | awk '{print $1}'
diff --git a/pgrust/env.sh b/pgrust/env.sh
index f112e40018..1b8f91436c 100644
--- a/pgrust/env.sh
+++ b/pgrust/env.sh
@@ -1,26 +1,15 @@
-# Shared paths for the pgrust ClickBench entry. Sourced by the sibling
-# scripts; not executable on its own.
-
-PGRUST_VERSION=0.2
-# The server binary, installed by ./install (sha256-verified download).
-PGRUST_BIN=/usr/local/bin/pgrust-postgres
-# The data directory. A fixed, world-traversable path rather than $PWD:
-# the server runs as the `postgres` system user, which cannot traverse
-# into e.g. /root if the checkout lives there.
-PGDATA_DIR=/var/lib/pgrust/data
-SERVER_LOG=/var/lib/pgrust/server.log
-# Unix socket directory (the server does not listen on TCP).
-PGSOCK_DIR=/var/run/pgrust
-# C PostgreSQL 18 (PGDG) provides initdb and psql; pgrust cannot bootstrap
-# a data directory itself and ships no client.
-PG18_BIN=/usr/lib/postgresql/18/bin
-# pgrust reads timezone data and misc share files from a C PostgreSQL
-# share directory at runtime.
-PGSHARE_DIR=/usr/share/postgresql/18
-TZDATA_DIR=/usr/share/zoneinfo
-# Where ./load moves hits.parquet before the server-side COPY. /var/tmp is
-# world-traversable, so the server (running as `postgres`) can read it
-# regardless of where the checkout lives.
-HITS_FILE=/var/tmp/hits.parquet
-
-PSQL="psql -h $PGSOCK_DIR -p 5432 -U postgres"
+# shared by the step scripts
+export PGRUST_HOME="${PGRUST_HOME:-$HOME/pgrust}" # install prefix + data + socket + logs
+export PGRUST_PREFIX="$PGRUST_HOME/install" # the release tarball, untarred (bin/postgres, share/postgresql)
+export PGDATA="$PGRUST_HOME/data"
+export PGSOCKDIR="$PGRUST_HOME/sock"
+export PGPORT="${PGPORT:-5432}"
+export PG18BIN="${PG18BIN:-/usr/lib/postgresql/18/bin}" # initdb + psql from apt.postgresql.org (README "Install")
+export HITS_PARQUET="${HITS_PARQUET:-$PGRUST_HOME/hits.parquet}"
+# the release tarball per machine architecture, with a .sha256 sidecar; PGRUST_TARBALL (a local file) overrides
+export PGRUST_RELEASE="${PGRUST_RELEASE:-v0.4-preview}"
+export PGRUST_RELEASE_BASE="${PGRUST_RELEASE_BASE:-https://pgrust.com/downloads/$PGRUST_RELEASE}" # the v0.2 entry's host (CloudFront -> S3)
+case "$(uname -m)" in x86_64) PGRUST_CPU=znver3;; aarch64) PGRUST_CPU=neoverse-v2;; *) PGRUST_CPU=generic;; esac
+export PGRUST_BIN_URL="${PGRUST_BIN_URL:-$PGRUST_RELEASE_BASE/pgrust-$PGRUST_RELEASE-$(uname -m)-$PGRUST_CPU.tar.gz}"
+export PGRUST_TARBALL="${PGRUST_TARBALL:-}"
+export PSQL="$PG18BIN/psql -X -h $PGSOCKDIR -p $PGPORT -U postgres"
diff --git a/pgrust/install b/pgrust/install
index b993d878a5..59d0c51c7a 100755
--- a/pgrust/install
+++ b/pgrust/install
@@ -1,122 +1,52 @@
#!/bin/bash
+# install: PostgreSQL 18 client tools (initdb/psql), the pgrust release, initdb, configuration, dataset.
set -eu
. "$(dirname "$0")/env.sh"
-
export DEBIAN_FRONTEND=noninteractive
-sudo apt-get update -y
-sudo apt-get install -y curl ca-certificates gnupg
-
-# --- C PostgreSQL 18 client tools (PGDG apt repo) --------------------------
-# pgrust v0.2 cannot bootstrap a data directory (no initdb port yet) and
-# ships no client, so initdb and psql come from C PostgreSQL 18.
-# Source: https://wiki.postgresql.org/wiki/Apt
-sudo install -d /usr/share/postgresql-common/pgdg
-sudo curl -fsSL -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc \
- https://www.postgresql.org/media/keys/ACCC4CF8.asc
-echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] http://apt.postgresql.org/pub/repos/apt $(. /etc/os-release && echo $VERSION_CODENAME)-pgdg main" \
- | sudo tee /etc/apt/sources.list.d/pgdg.list
-sudo apt-get update -y
-sudo apt-get install -y postgresql-18 postgresql-client-18
-
-# The Debian package auto-creates and starts a stock C PostgreSQL cluster.
-# We only need the package's initdb and psql — stop the cluster so it does
-# not sit on RAM during the benchmark.
-sudo systemctl disable --now postgresql || true
-sudo systemctl disable --now postgresql@18-main 2>/dev/null || true
-
-# --- the pgrust server binary ----------------------------------------------
-# Official published v0.2 release build (generic CPU baseline for the
-# architecture, i.e. NOT -Ctarget-cpu=native; see README.md). Verified
-# against the published sha256 before install.
-case "$(uname -m)" in
- aarch64) PLATFORM=linux-aarch64 ;;
- x86_64) PLATFORM=linux-x86_64 ;;
- *) echo "unsupported architecture: $(uname -m)" >&2; exit 1 ;;
-esac
-tmp=$(mktemp -d)
-curl -fL -o "$tmp/pgrust-$PGRUST_VERSION-$PLATFORM" \
- "https://pgrust.com/downloads/v$PGRUST_VERSION/pgrust-$PGRUST_VERSION-$PLATFORM"
-curl -fL -o "$tmp/pgrust-$PGRUST_VERSION-$PLATFORM.sha256" \
- "https://pgrust.com/downloads/v$PGRUST_VERSION/pgrust-$PGRUST_VERSION-$PLATFORM.sha256"
-(cd "$tmp" && sha256sum -c "pgrust-$PGRUST_VERSION-$PLATFORM.sha256")
-sudo install -m 755 "$tmp/pgrust-$PGRUST_VERSION-$PLATFORM" "$PGRUST_BIN"
-rm -rf "$tmp"
-
-# --- data directory --------------------------------------------------------
-# initdb from C PostgreSQL 18; pgrust then runs against this datadir.
-sudo rm -rf "$PGDATA_DIR"
-sudo install -d -o postgres -g postgres "$(dirname "$PGDATA_DIR")"
-sudo -u postgres "$PG18_BIN/initdb" -D "$PGDATA_DIR" \
- --no-locale --encoding=UTF8 -U postgres -A trust >/dev/null
-
-# Pin timezones so timestamp-dependent queries cannot vary with the host.
-sudo -u postgres sed -i \
- "s/^log_timezone = .*/log_timezone = 'GMT'/; s/^timezone = .*/timezone = 'GMT'/" \
- "$PGDATA_DIR/postgresql.conf"
-
-# --- swap parity with the benchmark automation ------------------------------
-# ClickBench's own cloud-init provisions a 16 GB swapfile on every benchmark
-# VM (32 GB machines OOM row stores during load otherwise). When this script
-# runs under that automation the swapfile already exists and this is a no-op;
-# on a bare machine it recreates the same environment so the concurrent-QPS
-# phase (10 connections against a 32 GB box) degrades gracefully instead of
-# OOM-killing the server.
-if [ ! -f /swapfile ] && [ "$(awk '/SwapTotal/ {print $2}' /proc/meminfo)" = "0" ]; then
- sudo fallocate -l 16G /swapfile
- sudo chmod 600 /swapfile
- sudo mkswap /swapfile >/dev/null
- sudo swapon /swapfile
+sudo apt-get update -y -qq
+sudo apt-get install -y -qq --no-install-recommends curl ca-certificates gnupg python3 wget
+if [ ! -x "$PG18BIN/initdb" ]; then
+ sudo install -d /usr/share/postgresql-common/pgdg
+ sudo curl -fsSL -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc https://www.postgresql.org/media/keys/ACCC4CF8.asc
+ echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] http://apt.postgresql.org/pub/repos/apt $(. /etc/os-release; echo $VERSION_CODENAME)-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list >/dev/null
+ sudo apt-get update -y -qq
+ sudo apt-get install -y -qq --no-install-recommends postgresql-client-18 postgresql-18
+ sudo systemctl disable --now postgresql >/dev/null 2>&1 || true # only initdb/psql are used
fi
-
-# --- configuration ---------------------------------------------------------
-# The machine-derived formula below is copied from this repo's
-# postgresql/install so this entry is configured the same way the other
-# PostgreSQL-family entries are, with TWO deviations, in the open (both
-# documented in README.md):
-#
-# work_mem = MemTotal/32 (1 GB on a 32 GB machine) instead of the
-# postgresql entry's fixed 64MB. pgrust's grouped-aggregation execution
-# keeps per-query hash state in work_mem; at 64MB the large GROUP BY
-# queries fall off the in-memory path into partitioned spills and run
-# ~10-40x slower.
-#
-# shared_buffers = MemTotal/8 instead of MemTotal/4. pgrust's columnar
-# scans read through their own arenas and the OS page cache rather than
-# the buffer pool; a 25% buffer pool is dead weight that starves the
-# concurrent-QPS phase (10 connections x ~2 GB of transient aggregation
-# state) into the OOM killer.
+mkdir -p "$PGRUST_HOME" "$PGSOCKDIR"
+rm -rf "$PGRUST_PREFIX"; mkdir -p "$PGRUST_PREFIX"
+if [ -n "$PGRUST_TARBALL" ]; then
+ tar -xzf "$PGRUST_TARBALL" --strip-components=1 -C "$PGRUST_PREFIX"
+else
+ curl -fsSL --retry 5 --retry-delay 5 -o "$PGRUST_HOME/release.tar.gz" "$PGRUST_BIN_URL"
+ curl -fsSL --retry 5 --retry-delay 5 -o "$PGRUST_HOME/release.tar.gz.sha256" "$PGRUST_BIN_URL.sha256"
+ (cd "$PGRUST_HOME" && echo "$(cut -c1-64 release.tar.gz.sha256) release.tar.gz" | sha256sum -c -) >/dev/null
+ tar -xzf "$PGRUST_HOME/release.tar.gz" --strip-components=1 -C "$PGRUST_PREFIX"
+fi
+"$PGRUST_PREFIX/bin/postgres" --version
+rm -rf "$PGDATA"
+"$PG18BIN/initdb" -D "$PGDATA" --no-locale --encoding=UTF8 -U postgres -A trust >/dev/null
+# Configuration (README.md)
memory=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
threads=$(nproc)
-cpus=$(($threads / 2))
-shared_buffers=$(($memory / 8))
-effective_cache_size=$(($memory - ($memory / 4)))
-max_worker_processes=$(($threads + 15))
-work_mem=$(($memory / 32))
-
-sudo -u postgres tee -a "$PGDATA_DIR/postgresql.conf" >/dev/null <> "$PGDATA/postgresql.conf" <&2; exit 1; }
-chmod 644 "$HITS_FILE"
-
-# --- load-session server posture -------------------------------------------
-# The COPY below is a single statement over the single as-published file
-# (no splitting), but the server executes it with pgrust's parallel COPY
-# path, opted in via environment for the load session only:
-#
-# PGRUST_COPY_PRESORT declares the table's clustered primary-key order —
-# the same (CounterID, EventDate, UserID, EventTime, WatchID) key every
-# ordered entry in this benchmark uses — and the COPY sorts the rows into
-# that order inside the server as it ingests. PGRUST_PARQUET_* enable the
-# parallel parquet decoder. The rest of the variables size/enable the
-# parallel ingest itself (worker counts, sort memory, in-server lz4 for
-# spill runs, ANALYZE sampling parallelism).
-#
-# These affect the (measured) load phase only: the server is restarted
-# WITHOUT any pgrust-specific environment before the query sweep, so the
-# scored queries run a stock server. See README.md.
-LOAD_ENV="PGRUST_RUNTIME=1 \
-PGRUST_COPY_PRESORT=counterid,eventdate,userid,eventtime,watchid \
-PGRUST_PARALLEL_COPY=1 PGRUST_PARALLEL_COPY_SORT=1 \
-PGRUST_PARALLEL_COPY_DOP=16 PGRUST_PARALLEL_COPY_STITCH_POOL=8 \
-PGRUST_PARALLEL_COPY_FILL_V2=1 PGRUST_PARALLEL_COPY_FILL_PREFETCH=6 \
-PGRUST_PARALLEL_COPY_SORT_MEM=512 \
-PGRUST_PARALLEL_COPY_SORT_MEMRUNS=${PGRUST_LOAD_MEMRUNS_MB:-15360} \
-PGRUST_COPY_RUNLZ4=1 PGRUST_CBSTORE_FASTHASH=1 PGRUST_CBSTORE_DICT_FRAMES=1 \
-PGRUST_PARQUET_PARALLEL=1 PGRUST_PARQUET_BUDGET_MB=4096 \
-PGRUST_ANALYZE_SAMPLE_POOL=12"
-
-# Restart the server with the load-session environment. Both restarts are
-# inside the timed load window.
-"$D/stop"
-EXTRA_SERVER_ENV="$LOAD_ENV" "$D/start"
-for i in $(seq 1 300); do "$D/check" >/dev/null 2>&1 && break; sleep 1; done
-"$D/check" >/dev/null
-
-# Same flow as postgresql/load: fresh DB, DDL, TRUNCATE + COPY FREEZE in one
-# transaction (FREEZE requires the truncate in the same transaction), then
-# VACUUM ANALYZE.
-$PSQL -t -c "DROP DATABASE IF EXISTS test"
-$PSQL -t -c "CREATE DATABASE test"
-$PSQL -v ON_ERROR_STOP=1 test -t < "$D/create.sql"
-# FORMAT 'parquet' and COERCE_EPOCH are pgrust COPY extensions: the server
-# decodes the parquet file directly, coercing the file's epoch-encoded
-# time columns into the standard TIMESTAMP/DATE schema (the published
-# hits.parquet carries no logical types; this is the same conversion the
-# duckdb entry does with epoch_ms()/make_date()).
-$PSQL -v ON_ERROR_STOP=1 test </dev/null 2>&1 && break; sleep 1; done
-"$D/check" >/dev/null
-sync
+. "$(dirname "$0")/env.sh"
+$PSQL -tAc "DROP DATABASE IF EXISTS test" >/dev/null
+$PSQL -tAc "CREATE DATABASE test" >/dev/null
+$PSQL -v ON_ERROR_STOP=1 -d test -q -f "$(dirname "$0")/create.sql"
+$PSQL -v ON_ERROR_STOP=1 -d test -q -c "COPY hits FROM '$HITS_PARQUET' WITH (FORMAT 'parquet', COERCE_EPOCH true)"
+$PSQL -v ON_ERROR_STOP=1 -d test -q -c "ANALYZE hits"
+got=$($PSQL -d test -tAc 'SELECT count(*) FROM hits')
+[ "$got" = 99997497 ] || { echo "load: hits has $got rows, expected 99997497" >&2; exit 1; }
diff --git a/pgrust/query b/pgrust/query
index 8197470a38..9417d8e0d0 100755
--- a/pgrust/query
+++ b/pgrust/query
@@ -1,27 +1,13 @@
#!/bin/bash
-# Reads a SQL query from stdin, runs it via psql against the `test` DB.
-# Stdout: query result. Stderr: runtime in fractional seconds on the last
-# line. Exit non-zero on error. Same shape as postgresql/query.
+# query: one statement from stdin through psql \timing; seconds to stderr
set -e
. "$(dirname "$0")/env.sh"
-
query=$(cat)
-
-out=$(printf '\\timing\n%s\n' "$query" | $PSQL test -t 2>&1)
+out=$(printf '\\timing\n%s\n' "$query" | $PSQL -d test -t 2>&1)
status=$?
-
-if printf '%s\n' "$out" | grep -q '^ERROR\|psql: error'; then
- printf '%s\n' "$out" >&2
- exit 1
-fi
-
+if printf '%s\n' "$out" | grep -q '^ERROR\|psql: error'; then printf '%s\n' "$out" >&2; exit 1; fi
printf '%s\n' "$out" | grep -v '^Time:'
-
time_ms=$(printf '%s\n' "$out" | grep -oP 'Time:\s+\K[0-9]+\.[0-9]+' | tail -n1)
-if [ -z "$time_ms" ]; then
- echo "no timing in psql output" >&2
- exit 1
-fi
+[ -n "$time_ms" ] || { echo "no timing in psql output" >&2; exit 1; }
awk -v ms="$time_ms" 'BEGIN { printf "%.3f\n", ms / 1000 }' >&2
-
exit "$status"
diff --git a/pgrust/results/20260919/c6a.4xlarge.json b/pgrust/results/20260919/c6a.4xlarge.json
new file mode 100644
index 0000000000..f29f2bb1e1
--- /dev/null
+++ b/pgrust/results/20260919/c6a.4xlarge.json
@@ -0,0 +1,237 @@
+{
+ "system": "pgrust",
+ "version": "v0.4-preview",
+ "proprietary": "no",
+ "hardware": "cpu",
+ "tuned": "no",
+ "tags": [
+ "Rust",
+ "PostgreSQL compatible",
+ "column-oriented"
+ ],
+ "date": "2026-09-19",
+ "machine": "c6a.4xlarge",
+ "cluster_size": 1,
+ "comment": "",
+ "load_time": 282.123,
+ "data_size": 9199140796,
+ "result": [
+ [
+ 0.003,
+ 0.001,
+ 0.001
+ ],
+ [
+ 0.003,
+ 0.001,
+ 0.001
+ ],
+ [
+ 0.003,
+ 0.001,
+ 0.001
+ ],
+ [
+ 0.003,
+ 0.001,
+ 0.001
+ ],
+ [
+ 0.278,
+ 0.068,
+ 0.07
+ ],
+ [
+ 0.329,
+ 0.028,
+ 0.029
+ ],
+ [
+ 0.003,
+ 0.001,
+ 0.001
+ ],
+ [
+ 0.034,
+ 0.004,
+ 0.005
+ ],
+ [
+ 0.377,
+ 0.105,
+ 0.11
+ ],
+ [
+ 0.581,
+ 0.196,
+ 0.203
+ ],
+ [
+ 0.223,
+ 0.022,
+ 0.025
+ ],
+ [
+ 0.251,
+ 0.027,
+ 0.029
+ ],
+ [
+ 0.357,
+ 0.044,
+ 0.045
+ ],
+ [
+ 0.616,
+ 0.163,
+ 0.167
+ ],
+ [
+ 0.411,
+ 0.071,
+ 0.072
+ ],
+ [
+ 0.258,
+ 0.073,
+ 0.074
+ ],
+ [
+ 0.683,
+ 0.187,
+ 0.188
+ ],
+ [
+ 0.074,
+ 0.003,
+ 0.004
+ ],
+ [
+ 2.518,
+ 0.568,
+ 0.565
+ ],
+ [
+ 0.106,
+ 0.005,
+ 0.005
+ ],
+ [
+ 3.27,
+ 0.023,
+ 0.023
+ ],
+ [
+ 4.016,
+ 0.016,
+ 0.017
+ ],
+ [
+ 7.19,
+ 0.035,
+ 0.036
+ ],
+ [
+ 0.727,
+ 0.013,
+ 0.013
+ ],
+ [
+ 0.061,
+ 0.012,
+ 0.007
+ ],
+ [
+ 0.152,
+ 0.004,
+ 0.004
+ ],
+ [
+ 0.065,
+ 0.006,
+ 0.007
+ ],
+ [
+ 0.8,
+ 0.132,
+ 0.134
+ ],
+ [
+ 3.256,
+ 0.506,
+ 0.517
+ ],
+ [
+ 0.006,
+ 0.002,
+ 0.002
+ ],
+ [
+ 0.408,
+ 0.092,
+ 0.091
+ ],
+ [
+ 2.819,
+ 0.078,
+ 0.079
+ ],
+ [
+ 2.617,
+ 0.218,
+ 0.212
+ ],
+ [
+ 3.254,
+ 0.132,
+ 0.132
+ ],
+ [
+ 3.258,
+ 0.131,
+ 0.131
+ ],
+ [
+ 0.201,
+ 0.087,
+ 0.089
+ ],
+ [
+ 0.074,
+ 0.007,
+ 0.008
+ ],
+ [
+ 0.02,
+ 0.004,
+ 0.004
+ ],
+ [
+ 0.066,
+ 0.004,
+ 0.004
+ ],
+ [
+ 0.118,
+ 0.024,
+ 0.029
+ ],
+ [
+ 0.033,
+ 0.003,
+ 0.004
+ ],
+ [
+ 0.037,
+ 0.005,
+ 0.006
+ ],
+ [
+ 0.028,
+ 0.005,
+ 0.007
+ ]
+ ],
+ "concurrent_qps": 8.85,
+ "concurrent_error_ratio": 0.061
+}
diff --git a/pgrust/results/20260919/c8g.4xlarge.json b/pgrust/results/20260919/c8g.4xlarge.json
new file mode 100644
index 0000000000..ab1ec52d0e
--- /dev/null
+++ b/pgrust/results/20260919/c8g.4xlarge.json
@@ -0,0 +1,237 @@
+{
+ "system": "pgrust",
+ "version": "v0.4-preview",
+ "proprietary": "no",
+ "hardware": "cpu",
+ "tuned": "no",
+ "tags": [
+ "Rust",
+ "PostgreSQL compatible",
+ "column-oriented"
+ ],
+ "date": "2026-09-19",
+ "machine": "c8g.4xlarge",
+ "cluster_size": 1,
+ "comment": "",
+ "load_time": 159.197,
+ "data_size": 9199140796,
+ "result": [
+ [
+ 0.002,
+ 0.001,
+ 0.001
+ ],
+ [
+ 0.002,
+ 0.001,
+ 0.001
+ ],
+ [
+ 0.002,
+ 0.001,
+ 0.001
+ ],
+ [
+ 0.002,
+ 0.001,
+ 0.001
+ ],
+ [
+ 0.145,
+ 0.031,
+ 0.031
+ ],
+ [
+ 0.181,
+ 0.011,
+ 0.012
+ ],
+ [
+ 0.002,
+ 0.001,
+ 0.001
+ ],
+ [
+ 0.025,
+ 0.003,
+ 0.003
+ ],
+ [
+ 0.205,
+ 0.041,
+ 0.042
+ ],
+ [
+ 0.291,
+ 0.062,
+ 0.064
+ ],
+ [
+ 0.147,
+ 0.012,
+ 0.012
+ ],
+ [
+ 0.175,
+ 0.014,
+ 0.014
+ ],
+ [
+ 0.219,
+ 0.017,
+ 0.017
+ ],
+ [
+ 0.404,
+ 0.05,
+ 0.05
+ ],
+ [
+ 0.257,
+ 0.027,
+ 0.027
+ ],
+ [
+ 0.144,
+ 0.036,
+ 0.035
+ ],
+ [
+ 0.398,
+ 0.066,
+ 0.065
+ ],
+ [
+ 0.057,
+ 0.002,
+ 0.022
+ ],
+ [
+ 2.302,
+ 0.198,
+ 0.196
+ ],
+ [
+ 0.069,
+ 0.002,
+ 0.002
+ ],
+ [
+ 3.105,
+ 0.007,
+ 0.007
+ ],
+ [
+ 3.817,
+ 0.009,
+ 0.009
+ ],
+ [
+ 7.094,
+ 0.015,
+ 0.015
+ ],
+ [
+ 0.743,
+ 0.007,
+ 0.008
+ ],
+ [
+ 0.052,
+ 0.01,
+ 0.002
+ ],
+ [
+ 0.116,
+ 0.004,
+ 0.003
+ ],
+ [
+ 0.056,
+ 0.006,
+ 0.005
+ ],
+ [
+ 0.41,
+ 0.052,
+ 0.051
+ ],
+ [
+ 3.214,
+ 0.218,
+ 0.22
+ ],
+ [
+ 0.005,
+ 0.002,
+ 0.002
+ ],
+ [
+ 0.266,
+ 0.029,
+ 0.028
+ ],
+ [
+ 2.814,
+ 0.032,
+ 0.032
+ ],
+ [
+ 2.609,
+ 0.075,
+ 0.074
+ ],
+ [
+ 3.096,
+ 0.044,
+ 0.044
+ ],
+ [
+ 3.111,
+ 0.045,
+ 0.044
+ ],
+ [
+ 0.112,
+ 0.039,
+ 0.04
+ ],
+ [
+ 0.053,
+ 0.004,
+ 0.004
+ ],
+ [
+ 0.016,
+ 0.003,
+ 0.003
+ ],
+ [
+ 0.053,
+ 0.003,
+ 0.003
+ ],
+ [
+ 0.102,
+ 0.014,
+ 0.013
+ ],
+ [
+ 0.026,
+ 0.002,
+ 0.003
+ ],
+ [
+ 0.026,
+ 0.003,
+ 0.004
+ ],
+ [
+ 0.02,
+ 0.003,
+ 0.003
+ ]
+ ],
+ "concurrent_qps": 30.692,
+ "concurrent_error_ratio": 0.034
+}
diff --git a/pgrust/start b/pgrust/start
index 8251ccf250..694daa3aed 100755
--- a/pgrust/start
+++ b/pgrust/start
@@ -1,24 +1,11 @@
#!/bin/bash
set -eu
. "$(dirname "$0")/env.sh"
-
-sudo install -d -o postgres -g postgres "$PGSOCK_DIR"
-
-# Runs as the `postgres` system user (the server refuses to run as root,
-# and the datadir is owned by postgres). RUST_MIN_STACK and the rlimit
-# raise thread/main stacks to match max_stack_depth=60000 — pgrust
-# requires both (deep recursive expression evaluation paths).
-sudo -u postgres \
- PGRUST_BIN="$PGRUST_BIN" PGDATA_DIR="$PGDATA_DIR" \
- PGSOCK_DIR="$PGSOCK_DIR" SERVER_LOG="$SERVER_LOG" \
- PGSHARE_DIR="$PGSHARE_DIR" TZDATA_DIR="$TZDATA_DIR" \
- EXTRA_SERVER_ENV="${EXTRA_SERVER_ENV:-}" \
- bash -c '
- ulimit -s 65520
- nohup env RUST_MIN_STACK=33554432 RUST_BACKTRACE=1 \
- PGRUST_TZDIR="$TZDATA_DIR" PGRUST_PGSHAREDIR="$PGSHARE_DIR" \
- $EXTRA_SERVER_ENV \
- "$PGRUST_BIN" -D "$PGDATA_DIR" -k "$PGSOCK_DIR" -p 5432 \
- -c listen_addresses="" \
- >> "$SERVER_LOG" 2>&1 &
- '
+$PSQL -tAc 'SELECT 1' >/dev/null 2>&1 && exit 0
+mkdir -p "$PGSOCKDIR"
+ulimit -s 65520 2>/dev/null || true # max_stack_depth=60000 needs the stack limit raised
+nohup env RUST_MIN_STACK=67108864 \
+ PGRUST_TZDIR=/usr/share/zoneinfo PGRUST_PGSHAREDIR="$PGRUST_PREFIX/share/postgresql" \
+ PGRUST_PARALLEL_COPY=1 PGRUST_PARQUET_PARALLEL=1 \
+ "$PGRUST_PREFIX/bin/postgres" -D "$PGDATA" -p "$PGPORT" >> "$PGRUST_HOME/server.log" 2>&1 < /dev/null &
+exit 0
diff --git a/pgrust/stop b/pgrust/stop
index 943bc16e5c..fadad26dda 100755
--- a/pgrust/stop
+++ b/pgrust/stop
@@ -1,23 +1,6 @@
#!/bin/bash
. "$(dirname "$0")/env.sh"
-
-pid=$(sudo head -1 "$PGDATA_DIR/postmaster.pid" 2>/dev/null)
-[ -n "$pid" ] || exit 0
-
-# Fast shutdown, as the true-cold-run protocol requires.
-sudo kill -INT "$pid" 2>/dev/null || exit 0
-for i in $(seq 1 120); do
- sudo kill -0 "$pid" 2>/dev/null || exit 0
- sleep 0.5
-done
-
-# Still alive after 60s: immediate shutdown. The datadir is durable and the
-# next start recovers; better than leaving a half-stopped server pinning the
-# page cache through the "cold" cycle.
-echo "pgrust/stop: fast shutdown did not finish in 60s, escalating to SIGQUIT" >&2
-sudo kill -QUIT "$pid" 2>/dev/null || exit 0
-for i in $(seq 1 60); do
- sudo kill -0 "$pid" 2>/dev/null || exit 0
- sleep 0.5
-done
+PIDF="$PGDATA/postmaster.pid"
+[ -f "$PIDF" ] && kill -INT "$(head -1 "$PIDF")" 2>/dev/null
+for i in $(seq 1 120); do [ -f "$PIDF" ] && kill -0 "$(head -1 "$PIDF")" 2>/dev/null || exit 0; sleep 0.5; done
exit 0
diff --git a/pgrust/template.json b/pgrust/template.json
index 2fdd9c9dd9..b72bbde84f 100644
--- a/pgrust/template.json
+++ b/pgrust/template.json
@@ -1,11 +1,8 @@
{
"system": "pgrust",
+ "version": "v0.4-preview",
"proprietary": "no",
"hardware": "cpu",
"tuned": "no",
- "tags": [
- "Rust",
- "column-oriented",
- "PostgreSQL compatible"
- ]
+ "tags": ["Rust", "PostgreSQL compatible", "column-oriented"]
}