Contents
pg_sift 3.3.0: everything since 3.2: the aggregate scan takes many more query shapes (top-N with any sort key, joins to small tables, semi and anti joins, two-level arms), sift_col indexes keep their pre-aggregates and answer from them after churn without a setting, point lookups by value, PostgreSQL 19 as a supported major, and the first release that tells you after the update what is left to do. Update the files, restart, then ALTER EXTENSION pg_sift UPDATE and run the commands it prints (a REINDEX of every sift text index is required).
Highlights
- Maintenance on by default.
sift.col_auto_merge(3.2: off) is on: a background worker vacuums the table when a thousandth of its rows changed and rewrites asift_colindex after theVACUUM, withinsift.col_auto_merge_wal_pct(5) of the WAL the server writes, so the units stay answerable from their pre-aggregates after churn (+0.9% to +1.9% WAL with full-page images in the churn tests; the CHANGELOG.md line has the units answerable).sift.table_auto_defrag(3.2: off) cleans asift_tablein the background past 5% dead rows (sift.table_auto_defrag_dead_pct, was 20), withsift.table_auto_defrag_wal_rateandsift.table_auto_defrag_max_activeto bound it. Both needsift.async_maintenance(on).sift.col_maintenance_status(index)andsift.col_index_statussay what the worker waits for. - Pre-aggregates by default.
cube_minmax,superunit = 16andcalendar_columns(every date and timestamp column) are on for a newsift_colindex;key_columns = autokeeps the keys of foreign key and*_idcolumns in each unit. A whole-tablecount/sumgoes from 0.26-0.37 ms to 0.05-0.08 ms and aGROUP BYwithmin/maxof an integer from 5.5 to 0.34 ms (200,000 rows), for an index 1.5% larger. - Point lookups.
point_columns(a directory of the rows of a unit by value) answers an equality or a shortINlist from the units that hold the value: 0.015 to 0.022 ms at 1,000,000 rows.point_tid_columns(column format minor 16, opt-in) lists heap addresses instead: 0.011 to 0.013 ms. - Top-N and ordering.
ORDER BY .. LIMIT nis a scan of the column index for text, uuid, bytea, inet, bool, time and money keys, for expressions (coalesce,lower,upper, arithmetic, casts,CASE), forOFFSETand for a page withcount(*) OVER (), with parallel workers;order_columnskeeps sorted runs for it (opt-in). - More shapes in the scan. A group table that outgrows
work_memspills to partitions,count(DISTINCT)of a column of a few values keeps a few words per group, semi and anti joins to a tree of small tables,LEFT JOIN LATERALover the elements of a JSON list, two-level arms over a spine, 16UNION ALLarms.sift.explain_refusalsandsift.explain_why(query)say why a query kept its plan;sift.plan_scaleplans as if the tables were larger. sift_table. A row over the write buffer is accepted (sift.table_wide_rows, up to 1 GB through tail pages);COPY,CREATE TABLE ASandREFRESH MATERIALIZED VIEWwrite column groups (sift.table_bulk_groups); a backend reads the tombstone log of a table 2.2 times faster; the row fetch of a text from a dictionary stored in blocks no longer loads the whole dictionary (1.4 ms where it took 28.6 ms).- Row-level security.
sift.rls_scores(superuser setting, off by default) gives a role under a policy the ranked scan andsift.score(); the scores are those of the whole index, so they depend on rows the role cannot see (the caveat is in docs/USAGE.md, “Row-level security”). - PostgreSQL 19 is a supported major (17, 18 and 19), no longer experimental. AArch64 has a
neonlevel ofsift.simd(Linux arm64 and Apple silicon). - Upgrades.
ALTER EXTENSION pg_sift UPDATEprints what is left to do,sift.upgrade_commands()lists it,scripts/upgrade-check.shis a preflight (also--toan older release), anddocs/UPGRADING.mdhas the downgrade sequence;docs/LIMITS.mdlists every fixed limit with the setting that changes it.
What a 3.2 user must know
- A
REINDEXof everysifttext index is required (exceptanalyzer = keyword). In 3.3 a word over 255 bytes is stored as its start and a hash; an older index holds it cut at 255 bytes, so queries miss rows until it is rebuilt. The update prints the commands. - A database written with the 3.3 defaults cannot be read by a 3.2 library. 3.3 writes at
sift.col_format_minor = 15by default (asift_colindex created or rewritten after the update, asift_tablewith a date or timestamp column); a 3.2 library refuses what it cannot read, with aREINDEXhint. To keep 3.2 able to read new writes, setsift.col_format_minor = 6(4 without JSON paths) for the server (ALTER SYSTEM) before the update: a setting of one session or of one role does not reach the background worker ofsift.col_auto_merge, which would write the default 15 into an index. Or create the indexesWITH (cube_minmax = false, superunit = 0, calendar_columns = 'off'). The way back, step by step (the preflight with--to 3.2, one command per blocker, then install the 3.2 files), is in docs/UPGRADING.md, “Going back to 3.2”. A standby that stays on 3.2 needs the same. - The default minor is 15; minor 16 is opt-in.
point_tid_columnswrites minor 16 only for the segments of an index created with it, and a 3.2 library refuses minor 16 like any column format it does not know. To roll the option back,sift.col_format_minor = 14and aREINDEXof that index (the point directory then lists rows again). - Background work is on. The auto-merge worker also runs
VACUUMon the tables ofsift_colindexes and writes WAL bounded bysift.col_auto_merge_wal_pct(5% of the WAL the server writes); the CREATE INDEX and REINDEX NOTICE says how to stop it.sift.col_auto_merge = offandsift.table_auto_defrag = offrestore the 3.2 behavior; asift_tablepass adds about 60% more WAL on an update, insert and delete mix at 200,000 rows (the line in CHANGELOG.md has the numbers). - Other defaults that change bytes or plans:
sift.agg_lookup_shared_kbshares the rows of a joined table between backends (-1: the smaller ofsift.agg_lookup_cache_kband 5% ofshared_buffers; 0 turns it off);sift.table_wide_rowsandsift.table_bulk_groupsare on; the planner compares the shared and the inline form of a CTE with a margin of 1.6. - The first statement of a fresh backend with bound parameters (a driver on the extended protocol,
EXECUTEwith arguments) is planned again through the planner hook in 3.3.0, as a plainSELECTalready was in 3.2.3: 3.2.3 still plans it without sift once per backend (220 to 260 ms against 3 to 5 ms for the next statement).session_preload_libraries = 'pg_sift'avoids that cost in both. - A backend can hold up to 64 MB for
sift_colscans. 3.3 keeps the checked heads and the zone arrays of the segments a backend reads, besides the parsed dictionaries that 3.2.3 already keeps, in one least-recently-used cache per backend capped at 64 MB (dict_cache_kb,col/dictcache.c; a compile-time constant in 3.3.0, no setting changes it). A long-lived backend (a pooled connection, a parallel worker) grows toward the cap and stays there; the entries of segments that a merge or a promotion replaced stay until the cache pushes them out. Measured under a write-heavy soak that creates segments all the time (6 backends, 120 s, PostgreSQL 18): 13 to 36 MB per backend, about 2.1 kB per cached segment; 3.2.3 does not grow that way. Budget up to 64 MB of private memory per backend forsift_colscans; to bound it, limit the number of long-lived backends (a pooler with a server lifetime) or rebuild with a lowerCDICT_CACHE_MAX_KB. A setting is planned for 3.4.
Fixes for defects present in 3.2.3 and earlier
There is no 3.2.4: these fixes ship with 3.3.0.
sift_table: a bitmap-scanUPDATEorDELETEcould stop early without an error. When aVACUUMpromoted the write buffer of the table while the statement ran, the statement skipped the rows of the promoted blocks and reported success (1530 of 3000 rows updated in the reproduction). Present in 3.2.3 and, by the code path, since 3.0.0. Affected:sift_tabletables updated or deleted through a bitmap scan while aVACUUMor the background defrag runs on them. Until the update,SET enable_bitmapscan = offfor such statements, or do not run them concurrently withVACUUMor the defrag; a bulk statement that reports fewer rows than the table holds for itsWHEREclause is the sign.sift_table: anUPDATEorDELETEcould fail withfailed to fetch tuple being updated. It happened when aVACUUMor the background defrag replaced the tombstone log while the statement scanned the table (sequential or bitmap scan). Present in 3.2.3. The statement rolls back and no rows are lost; run it again, or avoid running it while aVACUUMis in progress.GROUP BYan expression of adatecolumn that holdsinfinityand-infinityfailed when the aggregate scan was chosen (3.2.3:a column range of 4294967296 values is too wide for the aggregate scan). An error only, no wrong rows. Workaround:SET sift.enable_agg = offfor the statement.GROUP BYon adatecolumn whose selected rows are allinfinity(or all-infinity) stopped the backend. The range table of the group keys stepped over the end of the 32-bit range and wrote far outside its allocation; a serial scan was enough, no parallel worker needed. Present in 3.2.3 and earlier. Workaround:SET sift.enable_agg = offfor the statement.
Compatibility
- PostgreSQL 17, 18 and 19; the packages below are for PostgreSQL 18 (other majors build from source with
make USE_PGXS=1; PostgreSQL 19 was tested on its release candidate). Extension SQL 3.3;ALTER EXTENSION pg_sift UPDATEreaches it from any older version in one command. Column format minor 15 by default (16 opt-in); the BM25 index needs theREINDEXabove. docs/UPGRADING.md has the preflight and the steps.
Packages
x86-64 packages for PostgreSQL 18 are attached below (SHA256SUMS covers every asset); other versions build from source with make USE_PGXS=1. The packages depend on liblz4 and libzstd.
- Debian 13, Ubuntu 24.04 (PGDG
postgresql-18):sudo apt install ./postgresql-18-pg-sift_3.3.0-1_amd64.deb - RHEL, Rocky, Alma 9 and 10 (PGDG
postgresql18-server):sudo dnf install ./pg_sift_18-3.3.0-1.el9.x86_64.rpm - Arch Linux: AUR
pg_sift.
Every change: CHANGELOG.md.
SQLite extension
pg_sift for SQLite 3.3.0 (sift, a loadable extension; a separate component from the PostgreSQL extension above). Rebuilt with the PostgreSQL 3.3.0 release: no change to the SQL interface of the extension.
- No change: the 3.3 work is in the PostgreSQL column index, the aggregate scan and
sift_table, which are not in this extension. No speed-up or slow-down is claimed. - Needs SQLite 3.38 or newer; Linux x86-64, glibc 2.34 or newer. Built on Debian 12, loaded into the SQLite of Debian 12, Ubuntu 24.04 and AlmaLinux 10 before release.
- Storage format 2: tables of 3.2.3 open as they are, nothing to rebuild.
Download sift-sqlite-3.3.0-linux-x86_64.tar.gz (below, covered by SHA256SUMS with the packages), unpack it and .load ./sift from the sqlite3
shell (sha256sum --ignore-missing -c SHA256SUMS). Documentation: sqlite/README.md.
Every change: sqlite/CHANGELOG.md.
MariaDB extension
pg_sift for MariaDB 3.3.0 (sift, a UDF library and two SQL files; a separate component from the extensions above). Rebuilt with the PostgreSQL 3.3.0 release: no change to the SQL files.
- No change: the 3.3 work is in the PostgreSQL column index, the aggregate scan and
sift_table, which are not in this extension. No speed-up or slow-down is claimed. - One library per MariaDB series, because it calls the server’s own symbols and refuses to load into another series: 10.11, 11.4, 11.8, 12.1 and 13.0; Linux x86-64. Each was built in the stock
mariadb:<series>image against that server’s headers and loaded into a server of the same image before release.BUILD-INFOin the tarball lists the server version and the glibc each library needs; a server of another distribution or of a series not listed is built from source (make -C mariadb). - Index format 2: an index of 3.2.3 opens as it is, nothing to rebuild.
Download sift-mariadb-3.3.0-linux-x86_64.tar.gz (below, covered by SHA256SUMS with the packages), unpack it, copy lib/<series>/libsift_udf.so of your server’s series
(SELECT VERSION()) into its plugin_dir, then SOURCE sql/functions.sql once per server and sql/sift.sql once per database (sha256sum --ignore-missing -c SHA256SUMS).
Documentation: mariadb/README.md.
Every change: mariadb/CHANGELOG.md.