New Features ✨
-
Emit a once-per-query warning when a scan encounters a significantly stale visibility map (at least
paradedb.visibility_map_warning_thresholdnon-all-visible heap blocks across the table and at least 50% of scanned blocks not all-visible), with guidance on runningVACUUMand tuning autovacuum. -
Join Scan now pushes down window aggregates with a
FILTERclause, such asCOUNT(*) FILTER (WHERE r.score > 80) OVER ()orpdb.agg(...) FILTER (WHERE ...) OVER (), over joins. The filter can compare columnar fields from either side of the join and can include search operators like|||. Filters that cannot be evaluated in the scan fall back to PostgreSQL. Join Scan also no longer declinesSELECT DISTINCTqueries with window aggregates over a column, such asSUM(r.score) OVER ()orAVG(r.score) OVER ()(#6501). -
Join Scan no longer declines queries whose
SELECTlist contains a volatile function, such asrandom(). Postgres now computes these expressions from the rows Join Scan returns, once per output row after theLIMIT, as it does in its own plans. AnORDER BYon a volatile expression still declines Join Scan.
Performance Improvements 🚀
-
Aggregate Scan pushes every
ORDER BYkey of aGROUP BY ... LIMITquery into the DataFusion TopK, joins included, and declares the ordering to Postgres so noSortsits above the scan. Only a single key was pushed before. - Join Scan and Aggregate Scan join the relations of an all-inner join in the order the Postgres planner’s row estimates favour, so the smallest joins run first. Outer, semi, anti, and lateral joins keep the order the query wrote.
-
Prune index segments using per-segment min/max field statistics across all columnar fields (not only
partition_bycolumns) before plan-time estimation and scan execution, and reportIncludedandPrunedsegment counts underSegmentsinEXPLAIN ANALYZE.
Stability Improvements 💪
-
Fixed Join Scan queries that project an expression over an indexed
DATE,TIME, orTIMETZcolumn, such asd::textorto_char(d, 'YYYY-MM-DD'). ADATEexpression failed at planning time withcoercion from Timestamp(µs) to the signature Exact(Date32) failed, and aTIMEorTIMETZexpression failed at execution time withUnsupported OID for Utf8 Arrow type(#6577). - Cancelling or terminating a backend that runs an MPP query no longer leaves it waiting on parallel workers that never exit.
-
Range and term queries on integer fields are now correct at the edges of the type (#6673). Fractional values are no longer truncated:
x >= 2.5and term 2.5 used to match 2, andx <= -2.5used to match -2. A bound that is not a number on an integer field is now an error. On int4range and int8range, a fractional bound or value works in terms of integers:range_term2.5 matches nothing. -
Fixed Join Scan declining queries whose
SELECTlist contains an expression over a column that is not columnar, such as'x/' || s.path(#6576). Postgres now computes these expressions from the rows Join Scan returns, after theLIMIT. AnORDER BYorDISTINCTexpression still needs columnar inputs. -
Fixed
pdb.unicode_wordsremoving emojis whenever its first option was anything other thanremove_emojis, for example'remove_emojis=false','columnar=true','pnorms=true'or'lowercase=false'. Only the bareremove_emojisflag andremove_emojis=trueremove emojis now, as documented. Existing indexes created with one of those options were built with emojis removed, so reindex them to make emojis searchable. -
Fixed an issue where large vector index builds could fail with
no unpinned buffers availablebecause of excessive small segments. -
pg_stat_user_indexesnow counts the scans of a ParadeDB index.idx_scanmoves for every executor path that reads the index, so an unused-index check no longer reports a live ParadeDB index as unused. -
Aggregate Scan no longer fails on an
ORDER BY ... LIMITover aLATERAL unnestgroup column, and keeps a sort key with a non byte-ordered collation on the PostgresSort. ALIMITabove aDISTINCTor a window function over aGROUP BYno longer loses rows. -
Fixed Aggregate Scan reading a
NUMERICliteral in an aggregateFILTERorHAVINGclause as the bits of a float, which produced wrong counts such ascount(*) FILTER (WHERE score >= 7.5)(#6792). AFILTERthat compares aNUMERICcolumn now falls back to Postgres, because the scan readsNUMERICfields in their storage encoding. - Fixed missing tokenizer errors when reading or writing range indexes created before 0.26.0, without requiring a reindex.
-
pdb.aggtermsfacets over an array column, combined with other facets on a join, now run correctly under MPP. Previously the query failed with a transport error, hung untilstatement_timeout, or could return under-counted buckets. -
A query with a
LIMITabove 8192 no longer fails withmin > max. min = ..., max = 8192when only part of itsORDER BYcan be pushed down, such asORDER BY pdb.score(id) DESC, id DESCon a table with a text key. This regressed in 0.26.1. -
Fixed Join Scan returning rows in the wrong order for an
ORDER BYon a volatile expression that is not in theSELECTlist, such asORDER BY random(), t1.id. Join Scan dropped the volatile sort key and sorted on the remaining keys. These queries now decline Join Scan. - Fixed logical replication apply worker errors when loading tokenizer configuration during transaction commit without an active snapshot.