<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Ruohang Feng on Vchord</title>
    <link>https://vchord.pgsty.com/authors/vonng/</link>
    <description>Recent content in Ruohang Feng on Vchord</description>
    <generator>Hugo</generator>
    <language>en-US</language>
    
    
    
      <lastBuildDate>Thu, 05 Nov 2026 00:32:10 +0800</lastBuildDate>
    
    
      <atom:link href="https://vchord.pgsty.com/authors/vonng/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
        <title>vchord 1.2.0: First Release Under PGSTY</title>
        <link>https://vchord.pgsty.com/blog/release/vchord-1.2.0/</link>
        <pubDate>Wed, 04 Nov 2026 00:00:00 +0000</pubDate>
        
        <guid>https://vchord.pgsty.com/blog/release/vchord-1.2.0/</guid>
        <description>&lt;p&gt;&lt;strong&gt;vchord 1.2.0&lt;/strong&gt; is the first release maintained by PGSTY in &lt;a href=&#34;https://github.com/pgsty/vchord&#34;&gt;&lt;code&gt;pgsty/vchord&lt;/code&gt;&lt;/a&gt; under the AGPL-3.0 license. It supports direct in-place upgrades from &lt;code&gt;vchord 0.2.0&lt;/code&gt; through &lt;code&gt;1.1.1&lt;/code&gt; (&lt;code&gt;ALTER EXTENSION vchord UPDATE TO &#39;1.2.0&#39;&lt;/code&gt; without reindexing), fixing query-correctness bugs in &lt;code&gt;vchordrq&lt;/code&gt; and &lt;code&gt;vchordg&lt;/code&gt;, &lt;code&gt;VACUUM&lt;/code&gt; vertex visibility and catalog statistics, internal and external index-build checks, quantization edge cases, and privilege checks for prewarming and query sampling.&lt;/p&gt;&#xA;&lt;div class=&#34;td-callout td-callout--note&#34; role=&#34;note&#34;&gt;&#xA;  &lt;div class=&#34;td-callout__title&#34;&gt;&lt;i class=&#34;td-callout__icon fa-solid fa-circle-info&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;&lt;span class=&#34;td-callout__label&#34;&gt;Extension &amp;amp; Release Links&lt;/span&gt;&lt;/div&gt;&#xA;  &lt;div class=&#34;td-callout__body&#34;&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;GitHub Release&lt;/strong&gt;: &lt;a href=&#34;https://github.com/pgsty/vchord/releases/tag/1.2.0&#34;&gt;&lt;code&gt;pgsty/vchord 1.2.0&lt;/code&gt;&lt;/a&gt; · &lt;a href=&#34;https://github.com/pgsty/vchord&#34;&gt;Source Repository&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Extension Catalog&lt;/strong&gt;: &lt;a href=&#34;https://pgext.cloud/e/vchord&#34;&gt;pgext.cloud (&lt;code&gt;vchord&lt;/code&gt;)&lt;/a&gt; · &lt;a href=&#34;https://pigsty.io/ext/e/vchord/&#34;&gt;Pigsty Extension Page (&lt;code&gt;pigsty.io&lt;/code&gt;)&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Binary Packages&lt;/strong&gt;: &lt;a href=&#34;https://vchord.pgsty.com/download/&#34;&gt;RPM / DEB Repository Setup&lt;/a&gt; · &lt;a href=&#34;https://vchord.pgsty.com/docs/about/sbom/&#34;&gt;SBOM &amp;amp; License Inventory&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;&lt;h2 id=&#34;changes&#34;&gt;Changes in 1.2.0&#xA;&lt;/h2&gt;&#xA;&lt;div class=&#34;td-table-scroll td-table-scroll--static&#34;&gt;&#xA;&lt;table class=&#34;wide expand td-table--wide&#34;&gt;&#xA;  &lt;thead&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;th scope=&#34;col&#34; style=&#34;text-align: left&#34;&gt;Area&lt;/th&gt;&#xA;      &lt;th scope=&#34;col&#34; style=&#34;text-align: left&#34;&gt;Change&lt;/th&gt;&#xA;    &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Multi-vector MaxSim (&lt;code&gt;@#&lt;/code&gt;)&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Under partial &lt;code&gt;vchordrq.probes&lt;/code&gt; with &lt;code&gt;maxsim_refine = 0&lt;/code&gt; and &lt;code&gt;maxsim_threshold = 0&lt;/code&gt;, documents absent from a query token&amp;rsquo;s candidate set now receive the worst distance in that candidate set instead of &lt;code&gt;-Infinity&lt;/code&gt;, preventing missing candidates from being falsely ranked first.&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Sphere filters &amp;amp; constructors&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;vchordg&lt;/code&gt; sphere operators (&lt;code&gt;&amp;lt;&amp;lt;-&amp;gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;lt;=&amp;gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;lt;#&amp;gt;&amp;gt;&lt;/code&gt;) compare radii in the operator&amp;rsquo;s distance units rather than squared distance and set &lt;code&gt;xs_recheck = true&lt;/code&gt;. All six &lt;code&gt;sphere(center, real)&lt;/code&gt; constructors bind return types at extension creation time rather than via caller &lt;code&gt;search_path&lt;/code&gt;.&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;read_stream&lt;/code&gt; reranking&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;On PostgreSQL 17/18, &lt;code&gt;vchordrq.io_rerank = &#39;read_stream&#39;&lt;/code&gt; keeps heap buffer guards aligned with candidate index items when concurrent &lt;code&gt;VACUUM&lt;/code&gt; shortens a page&amp;rsquo;s visible item batch.&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;VACUUM&lt;/code&gt; &amp;amp; catalog stats&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;vchordg&lt;/code&gt; excludes deleted vertices after &lt;code&gt;VACUUM&lt;/code&gt; while keeping a deleted entry point traversable; &lt;code&gt;CREATE INDEX&lt;/code&gt;, &lt;code&gt;REINDEX&lt;/code&gt;, and &lt;code&gt;VACUUM&lt;/code&gt; on &lt;code&gt;vchordrq&lt;/code&gt; and &lt;code&gt;vchordg&lt;/code&gt; report actual tuple and page counts instead of resetting &lt;code&gt;reltuples&lt;/code&gt; to &lt;code&gt;0&lt;/code&gt;; &lt;code&gt;ANALYZE&lt;/code&gt; alone no longer triggers index cleanup.&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Planner &amp;amp; parallel builds&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;vchordg&lt;/code&gt; honors partial index &lt;code&gt;WHERE&lt;/code&gt; predicates and adds &lt;code&gt;disable_cost&lt;/code&gt; for non-vector scans (such as &lt;code&gt;SELECT COUNT(*)&lt;/code&gt;); &lt;code&gt;vchordrq&lt;/code&gt; cost estimation accounts for &lt;code&gt;LIMIT&lt;/code&gt;, filter selectivity, and &lt;code&gt;probes&lt;/code&gt; depth; table options like &lt;code&gt;parallel_workers&lt;/code&gt; no longer block parallel index builds.&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Internal &amp;amp; external builds&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Internal k-means sampling reads only MVCC-visible rows and checks &lt;code&gt;maintenance_work_mem&lt;/code&gt; when &lt;code&gt;lists &amp;gt; samples&lt;/code&gt;; external centroid builds validate tree depth (&lt;code&gt;2..=9&lt;/code&gt;), reject cycles and unreachable nodes, and synthesize a root when &lt;code&gt;&amp;gt;= 2&lt;/code&gt; top-level centroids have &lt;code&gt;parent IS NULL&lt;/code&gt;.&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Quantization types&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;rabitq4&lt;/code&gt; and &lt;code&gt;rabitq8&lt;/code&gt; enforce &lt;code&gt;1..=65535&lt;/code&gt; dimensions and code ranges (&lt;code&gt;0..=15&lt;/code&gt; / &lt;code&gt;0..=255&lt;/code&gt;); &lt;code&gt;quantize_to_rabitq*&lt;/code&gt; rejects non-finite overflow while input functions accept legacy &lt;code&gt;NaN&lt;/code&gt;/&lt;code&gt;Inf&lt;/code&gt; metadata for &lt;code&gt;pg_restore&lt;/code&gt;; &lt;code&gt;halfvec&lt;/code&gt; distances accumulate in &lt;code&gt;f32&lt;/code&gt;; quantized L2 clamps negative rounding residuals to &lt;code&gt;0&lt;/code&gt; before &lt;code&gt;sqrt&lt;/code&gt;.&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Recall &amp;amp; privileges&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;vchordrq_evaluate_query_recall&lt;/code&gt; requires &lt;code&gt;exact_search =&amp;gt; true&lt;/code&gt; for &lt;code&gt;@#&lt;/code&gt;, derives multi-level default &lt;code&gt;accu_probes&lt;/code&gt;, and restores &lt;code&gt;vchordrq.*&lt;/code&gt; GUCs; &lt;code&gt;vchordrq_prewarm&lt;/code&gt;, &lt;code&gt;vchordg_prewarm&lt;/code&gt;, and query sampling enforce table ownership or &lt;code&gt;SELECT&lt;/code&gt; privileges.&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Build &amp;amp; platform support&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Upgraded to &lt;code&gt;pgrx 0.19.3&lt;/code&gt; for PostgreSQL 14–18 on Linux x86_64 and aarch64, with full in-place upgrade scripts from &lt;code&gt;0.2.0&lt;/code&gt; through &lt;code&gt;1.1.1&lt;/code&gt;.&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;h2 id=&#34;query-correctness&#34;&gt;Multi-vector MaxSim (&lt;code&gt;@#&lt;/code&gt;), sphere filters, and &lt;code&gt;read_stream&lt;/code&gt; reranking&#xA;&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;MaxSim (&lt;code&gt;@#&lt;/code&gt;) missing-value estimates under partial probes&lt;/strong&gt;: VectorChord&amp;rsquo;s &lt;code&gt;@#&lt;/code&gt; operator negates MaxSim similarity so smaller (more negative) values rank first in ascending &lt;code&gt;ORDER BY&lt;/code&gt;. Under partial probes, a candidate document can appear in the probed lists for some query vectors while being absent from others. Previously, when both &lt;code&gt;vchordrq.maxsim_refine = 0&lt;/code&gt; and &lt;code&gt;vchordrq.maxsim_threshold = 0&lt;/code&gt;, missing query-token hits defaulted to &lt;code&gt;-Infinity&lt;/code&gt; (&lt;code&gt;f32::NEG_INFINITY&lt;/code&gt;), which falsely ranked unprobed candidates first. In &lt;code&gt;1.2.0&lt;/code&gt;, when neither refinement nor a threshold supplies an estimate, missing hits use the worst distance observed in that query token&amp;rsquo;s candidate set.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;vchordg&lt;/code&gt; sphere similarity filters (&lt;code&gt;&amp;lt;&amp;lt;-&amp;gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;lt;=&amp;gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;lt;#&amp;gt;&amp;gt;&lt;/code&gt;)&lt;/strong&gt;: Internally, &lt;code&gt;vchordg&lt;/code&gt; tracks squared L2 and cosine distances during graph traversal. Previously, sphere range scans compared the user-supplied radius directly against the internal squared distance and returned index tuples without requesting a heap recheck. &lt;code&gt;vchordg&lt;/code&gt; now converts the radius into the operator&amp;rsquo;s distance units before pruning and sets &lt;code&gt;xs_recheck = true&lt;/code&gt; on returned items so PostgreSQL rechecks the exact sphere predicate on the heap tuple.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;search_path&lt;/code&gt;-safe &lt;code&gt;sphere(...)&lt;/code&gt; constructors&lt;/strong&gt;: The six &lt;code&gt;sphere(vector, real)&lt;/code&gt;, &lt;code&gt;sphere(halfvec, real)&lt;/code&gt;, &lt;code&gt;sphere(rabitq8, real)&lt;/code&gt;, and multi-vector array constructors are now defined as SQL-language &lt;code&gt;ROW($1, $2)::sphere_*&lt;/code&gt; expressions whose composite return types are bound at &lt;code&gt;CREATE EXTENSION&lt;/code&gt; / &lt;code&gt;ALTER EXTENSION&lt;/code&gt; time. Calling &lt;code&gt;sphere(...)&lt;/code&gt; works regardless of the caller&amp;rsquo;s &lt;code&gt;search_path&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;PostgreSQL 17/18 &lt;code&gt;read_stream&lt;/code&gt; reranking&lt;/strong&gt;: When &lt;code&gt;vchordrq.io_rerank = &#39;read_stream&#39;&lt;/code&gt; is active on PostgreSQL 17 or 18 and concurrent &lt;code&gt;VACUUM&lt;/code&gt; removes heap tuples between index scan and heap fetch, the &lt;code&gt;read_stream&lt;/code&gt; batch consumer keeps its buffer pin guards aligned with the remaining candidate tuples rather than advancing out of sync.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;maintenance-and-planner&#34;&gt;Index maintenance, &lt;code&gt;VACUUM&lt;/code&gt;, and planner cost estimation&#xA;&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;vchordg&lt;/code&gt; &lt;code&gt;VACUUM&lt;/code&gt; visibility and entry-point reachability&lt;/strong&gt;: After &lt;code&gt;VACUUM&lt;/code&gt; marks deleted heap TIDs in a &lt;code&gt;vchordg&lt;/code&gt; index, graph searches exclude deleted vertices from returned results while still traversing through the graph&amp;rsquo;s entry point if that entry point was deleted, keeping surviving vertices reachable without a &lt;code&gt;REINDEX&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;vchordg&lt;/code&gt; partial indexes and &lt;code&gt;COUNT(*)&lt;/code&gt; protection&lt;/strong&gt;: &lt;code&gt;vchordg&lt;/code&gt; now supports partial index predicates (&lt;code&gt;CREATE INDEX ... USING vchordg (...) WHERE ...&lt;/code&gt;) and adds PostgreSQL&amp;rsquo;s &lt;code&gt;disable_cost&lt;/code&gt; penalty whenever a scan has no vector ordering or filter clauses, preventing the planner from choosing a graph index for plain &lt;code&gt;SELECT COUNT(*)&lt;/code&gt; queries.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Accurate &lt;code&gt;reltuples&lt;/code&gt; / &lt;code&gt;relpages&lt;/code&gt; statistics and &lt;code&gt;ANALYZE&lt;/code&gt; no-op&lt;/strong&gt;: Previously, &lt;code&gt;ambuild&lt;/code&gt; and &lt;code&gt;amvacuumcleanup&lt;/code&gt; returned zeroed &lt;code&gt;IndexBuildResult&lt;/code&gt; / &lt;code&gt;IndexVacuumInfo&lt;/code&gt; counters, which caused &lt;code&gt;CREATE INDEX&lt;/code&gt;, &lt;code&gt;REINDEX&lt;/code&gt;, and &lt;code&gt;VACUUM&lt;/code&gt; to overwrite &lt;code&gt;pg_class.reltuples&lt;/code&gt; with &lt;code&gt;0&lt;/code&gt;. Both &lt;code&gt;vchordrq&lt;/code&gt; and &lt;code&gt;vchordg&lt;/code&gt; now report actual indexed tuple and page counts. When &lt;code&gt;ANALYZE&lt;/code&gt; runs (&lt;code&gt;analyze_only = true&lt;/code&gt;), &lt;code&gt;amvacuumcleanup&lt;/code&gt; returns immediately without running index compaction or page cleanup.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Parallel index build planning&lt;/strong&gt;: Setting table storage options such as &lt;code&gt;ALTER TABLE items SET (parallel_workers = 4)&lt;/code&gt; previously caused &lt;code&gt;amoptions&lt;/code&gt; validation to reject the table&amp;rsquo;s reloptions when planning parallel index builds. Both &lt;code&gt;vchordrq&lt;/code&gt; and &lt;code&gt;vchordg&lt;/code&gt; now validate only index-level reloptions so parallel worker planning proceeds normally.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;vchordrq&lt;/code&gt; cost model&lt;/strong&gt;: &lt;code&gt;amcostestimate&lt;/code&gt; for &lt;code&gt;vchordrq&lt;/code&gt; now incorporates the query&amp;rsquo;s &lt;code&gt;LIMIT&lt;/code&gt;, filter selectivity, and the effective &lt;code&gt;probes&lt;/code&gt; budget when estimating filtered vector scans, and adds &lt;code&gt;disable_cost&lt;/code&gt; when a multi-level &lt;code&gt;vchordrq.probes&lt;/code&gt; setting does not match the number of levels in the index&amp;rsquo;s &lt;code&gt;lists&lt;/code&gt; hierarchy.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;index-builds&#34;&gt;Internal and external index build validation&#xA;&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;MVCC-visible k-means sampling and memory checks&lt;/strong&gt;: Internal k-means clustering checks &lt;code&gt;HeapTupleSatisfiesVisibility&lt;/code&gt; before sampling a heap tuple so dead or uncommitted tuples do not skew centroids. The initial sampling buffer is sized from the estimated live row count (capped at &lt;code&gt;max(lists) * sampling_factor&lt;/code&gt;), and centroid buffer allocations are checked against &lt;code&gt;maintenance_work_mem&lt;/code&gt; even when &lt;code&gt;lists&lt;/code&gt; exceeds the number of sampled rows.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;External centroid tree validation&lt;/strong&gt;: When building a &lt;code&gt;vchordrq&lt;/code&gt; index from an external centroid table (&lt;code&gt;[build.external]&lt;/code&gt;), &lt;code&gt;vchord&lt;/code&gt; validates the &lt;code&gt;id&lt;/code&gt; / &lt;code&gt;parent&lt;/code&gt; hierarchy before partitioning vectors:&#xA;&lt;ol&gt;&#xA;&lt;li&gt;The tree depth must be between &lt;code&gt;2&lt;/code&gt; and &lt;code&gt;9&lt;/code&gt; levels (including the root), matching the &lt;code&gt;1..=8&lt;/code&gt; &lt;code&gt;lists&lt;/code&gt; levels supported by &lt;code&gt;vchordrq&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Cyclic &lt;code&gt;parent&lt;/code&gt; chains and disconnected nodes are rejected with a descriptive error.&lt;/li&gt;&#xA;&lt;li&gt;If two or more top-level centroids have &lt;code&gt;parent IS NULL&lt;/code&gt;, &lt;code&gt;vchordrq&lt;/code&gt; automatically synthesizes a root node above them so flat or multi-root centroid tables do not need a manual dummy root row.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Strict &lt;code&gt;probes&lt;/code&gt; and &lt;code&gt;alpha&lt;/code&gt; validation&lt;/strong&gt;: Both &lt;code&gt;SET vchordrq.probes&lt;/code&gt; and the per-index &lt;code&gt;probes&lt;/code&gt; storage option validate every comma-separated integer (&lt;code&gt;1..=4294967295&lt;/code&gt;, permitting surrounding whitespace and at most one trailing comma) and reject invalid strings immediately. For &lt;code&gt;vchordg&lt;/code&gt;, the &lt;code&gt;alpha&lt;/code&gt; build option must contain &lt;code&gt;1..=8&lt;/code&gt; non-decreasing floats in &lt;code&gt;[1.0, 2.0)&lt;/code&gt; whose first element is &lt;code&gt;1.0&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;quantization-types&#34;&gt;Quantization types (&lt;code&gt;rabitq4&lt;/code&gt;, &lt;code&gt;rabitq8&lt;/code&gt;, and &lt;code&gt;halfvec&lt;/code&gt;)&#xA;&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Dimensions and code ranges&lt;/strong&gt;: &lt;code&gt;rabitq4&lt;/code&gt; and &lt;code&gt;rabitq8&lt;/code&gt; enforce &lt;code&gt;1..=65535&lt;/code&gt; dimensions (&lt;code&gt;typmod&lt;/code&gt; validation and input parsing) and verify that every quantized code lies in &lt;code&gt;0..=15&lt;/code&gt; (&lt;code&gt;rabitq4&lt;/code&gt;) or &lt;code&gt;0..=255&lt;/code&gt; (&lt;code&gt;rabitq8&lt;/code&gt;), rejecting trailing tokens or malformed brackets.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Overflow rejection with &lt;code&gt;pg_dump&lt;/code&gt; / &lt;code&gt;pg_restore&lt;/code&gt; compatibility&lt;/strong&gt;: &lt;code&gt;quantize_to_rabitq4&lt;/code&gt; and &lt;code&gt;quantize_to_rabitq8&lt;/code&gt; reject input vectors whose norm or dot product overflows &lt;code&gt;f32&lt;/code&gt; (&lt;code&gt;NaN&lt;/code&gt; / &lt;code&gt;Infinity&lt;/code&gt;), and produce finite metadata for zero or constant vectors. At the same time, the &lt;code&gt;rabitq4&lt;/code&gt; and &lt;code&gt;rabitq8&lt;/code&gt; text and binary input functions still accept legacy &lt;code&gt;NaN&lt;/code&gt; / &lt;code&gt;Inf&lt;/code&gt; metadata fields so existing backups created under &lt;code&gt;1.1.x&lt;/code&gt; restore without error.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;f32&lt;/code&gt; accumulation for &lt;code&gt;halfvec&lt;/code&gt; and clamped L2 roots&lt;/strong&gt;: &lt;code&gt;halfvec&lt;/code&gt; distance operators (&lt;code&gt;&amp;lt;-&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;#&amp;gt;&lt;/code&gt;) now accumulate in &lt;code&gt;f32&lt;/code&gt; rather than &lt;code&gt;f16&lt;/code&gt; so large or high-dimensional &lt;code&gt;halfvec&lt;/code&gt; values do not overflow to &lt;code&gt;Infinity&lt;/code&gt;. Quantized L2 distance functions clamp tiny negative floating-point rounding residuals to &lt;code&gt;0.0&lt;/code&gt; before calling &lt;code&gt;sqrt&lt;/code&gt;, eliminating spurious &lt;code&gt;NaN&lt;/code&gt; results on near-identical vectors.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;recall-and-security&#34;&gt;Recall evaluation and prewarm privileges&#xA;&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;vchordrq_evaluate_query_recall&lt;/code&gt; improvements&lt;/strong&gt;:&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Multi-vector MaxSim (&lt;code&gt;@#&lt;/code&gt;) queries now require &lt;code&gt;exact_search =&amp;gt; true&lt;/code&gt; (attempting approximate ground-truth probing with &lt;code&gt;@#&lt;/code&gt; raises an error explaining why &lt;code&gt;exact_search =&amp;gt; true&lt;/code&gt; is required).&lt;/li&gt;&#xA;&lt;li&gt;When &lt;code&gt;accu_probes&lt;/code&gt; is omitted, &lt;code&gt;vchordrq_evaluate_query_recall&lt;/code&gt; generates a &lt;code&gt;65535&lt;/code&gt; entry for each level of a multi-level &lt;code&gt;lists&lt;/code&gt; index (for example, &lt;code&gt;&#39;65535,65535&#39;&lt;/code&gt; on a 2-level index) instead of a single &lt;code&gt;&#39;65535&#39;&lt;/code&gt; value that would fail level-count validation.&lt;/li&gt;&#xA;&lt;li&gt;Temporary changes to &lt;code&gt;vchordrq.probes&lt;/code&gt;, &lt;code&gt;vchordrq.epsilon&lt;/code&gt;, and &lt;code&gt;vchordrq.enable_scan&lt;/code&gt; inside &lt;code&gt;vchordrq_evaluate_query_recall&lt;/code&gt; are restored to their prior values and GUC sources (&lt;code&gt;RESET&lt;/code&gt; when previously unset) even if the query errors out.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Prewarm and query-sampling privilege checks&lt;/strong&gt;:&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;vchordrq_prewarm(regclass, integer)&lt;/code&gt; and &lt;code&gt;vchordg_prewarm(regclass)&lt;/code&gt; verify that the calling role owns the underlying table or holds table-level &lt;code&gt;SELECT&lt;/code&gt; privilege before warming index pages into shared buffers.&lt;/li&gt;&#xA;&lt;li&gt;Enabling or configuring query sampling (&lt;code&gt;vchordrq.query_sampling_*&lt;/code&gt;) checks that the role owns the table, holds table-level &lt;code&gt;SELECT&lt;/code&gt;, or is a member of &lt;code&gt;pg_read_all_stats&lt;/code&gt;. Similarly, reading &lt;code&gt;vchordrq_sampled_queries&lt;/code&gt; filters out recorded queries from indexes whose underlying tables the current role is not allowed to read.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;known-issues&#34;&gt;Known issues&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Package QA for &lt;code&gt;1.2.0&lt;/code&gt; found two cases where results can come back out of distance order. Both are tracked in &lt;a href=&#34;https://github.com/pgsty/vchord/issues/17&#34;&gt;pgsty/vchord#17&lt;/a&gt; for a later release. Recall is not affected.&lt;/p&gt;</description>
      </item>
    <item>
        <title>pg_tokenizer 1.0.0: First Release Under PGSTY</title>
        <link>https://vchord.pgsty.com/blog/release/pg-tokenizer-1.0.0/</link>
        <pubDate>Tue, 03 Nov 2026 00:00:00 +0000</pubDate>
        
        <guid>https://vchord.pgsty.com/blog/release/pg-tokenizer-1.0.0/</guid>
        <description>&lt;p&gt;&lt;strong&gt;pg_tokenizer 1.0.0&lt;/strong&gt; is the first release maintained by PGSTY in &lt;a href=&#34;https://github.com/pgsty/pg_tokenizer&#34;&gt;&lt;code&gt;pgsty/pg_tokenizer&lt;/code&gt;&lt;/a&gt; under the Apache-2.0 license. It connects the multilingual analyzers and vocabulary models to PostgreSQL&amp;rsquo;s native full-text search, makes corpus-trained custom models safe under concurrent writes, and lets non-superuser database owners manage their own tokenizer definitions.&lt;/p&gt;&#xA;&lt;p&gt;The existing &lt;code&gt;tokenize(text, tokenizer_name)&lt;/code&gt; interface still returns &lt;code&gt;INT[]&lt;/code&gt;. Applications using the historical &lt;code&gt;vchord_bm25&lt;/code&gt; 0.3.x &lt;code&gt;bm25vector&lt;/code&gt; workflow can retain that interface; applications using GIN/GiST or the &lt;a href=&#34;https://vchord.pgsty.com/blog/release/vchord-bm25-1.0.0/&#34;&gt;&lt;code&gt;vchord_bm25&lt;/code&gt; 1.0.0&lt;/a&gt; API can use the new &lt;code&gt;tsvector&lt;/code&gt; functions. &lt;code&gt;pg_tokenizer&lt;/code&gt; does not require either BM25 or vector-search extensions.&lt;/p&gt;</description>
      </item>
    <item>
        <title>vchord_bm25 1.0.0: First Release Under PGSTY</title>
        <link>https://vchord.pgsty.com/blog/release/vchord-bm25-1.0.0/</link>
        <pubDate>Tue, 03 Nov 2026 00:00:00 +0000</pubDate>
        
        <guid>https://vchord.pgsty.com/blog/release/vchord-bm25-1.0.0/</guid>
        <description>&lt;p&gt;&lt;strong&gt;vchord_bm25 1.0.0&lt;/strong&gt; is the first release maintained by PGSTY in &lt;a href=&#34;https://github.com/pgsty/vchord_bm25&#34;&gt;&lt;code&gt;pgsty/vchord_bm25&lt;/code&gt;&lt;/a&gt; under the AGPL-3.0 license. It replaces the custom &lt;code&gt;bm25vector&lt;/code&gt; data type and fixed &lt;code&gt;bm25_catalog&lt;/code&gt; schema from 0.3.x with native PostgreSQL &lt;code&gt;tsvector&lt;/code&gt; indexing, reworks the Block-WeakAnd (WAND) scan and storage engine for continuing scans and concurrent &lt;code&gt;VACUUM&lt;/code&gt;, and enforces table privileges and Row-Level Security (RLS) across both indexed scans and scalar scoring.&lt;/p&gt;&#xA;&lt;div class=&#34;td-callout td-callout--note&#34; role=&#34;note&#34;&gt;&#xA;  &lt;div class=&#34;td-callout__title&#34;&gt;&lt;i class=&#34;td-callout__icon fa-solid fa-circle-info&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;&lt;span class=&#34;td-callout__label&#34;&gt;Extension &amp;amp; Release Links&lt;/span&gt;&lt;/div&gt;&#xA;  &lt;div class=&#34;td-callout__body&#34;&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;GitHub Release&lt;/strong&gt;: &lt;a href=&#34;https://github.com/pgsty/vchord_bm25/releases/tag/1.0.0&#34;&gt;&lt;code&gt;pgsty/vchord_bm25 1.0.0&lt;/code&gt;&lt;/a&gt; · &lt;a href=&#34;https://github.com/pgsty/vchord_bm25&#34;&gt;Source Repository&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Extension Catalog&lt;/strong&gt;: &lt;a href=&#34;https://pgext.cloud/e/vchord_bm25&#34;&gt;pgext.cloud (&lt;code&gt;vchord_bm25&lt;/code&gt;)&lt;/a&gt; · &lt;a href=&#34;https://pigsty.io/ext/e/vchord_bm25/&#34;&gt;Pigsty Extension Page (&lt;code&gt;pigsty.io&lt;/code&gt;)&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Binary Packages&lt;/strong&gt;: &lt;a href=&#34;https://vchord.pgsty.com/download/&#34;&gt;RPM / DEB Repository Setup&lt;/a&gt; · &lt;a href=&#34;https://vchord.pgsty.com/docs/about/sbom/&#34;&gt;SBOM &amp;amp; License Inventory&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;&lt;h2 id=&#34;changes&#34;&gt;Changes in 1.0.0&#xA;&lt;/h2&gt;&#xA;&lt;div class=&#34;td-table-scroll td-table-scroll--static&#34;&gt;&#xA;&lt;table class=&#34;wide expand td-table--wide&#34;&gt;&#xA;  &lt;thead&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;th scope=&#34;col&#34; style=&#34;text-align: left&#34;&gt;Area&lt;/th&gt;&#xA;      &lt;th scope=&#34;col&#34; style=&#34;text-align: left&#34;&gt;Change&lt;/th&gt;&#xA;    &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Native &lt;code&gt;tsvector&lt;/code&gt; API&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Indexes standard PostgreSQL &lt;code&gt;tsvector&lt;/code&gt; columns with &lt;code&gt;USING bm25 (... bm25_ops)&lt;/code&gt;. &lt;code&gt;to_bm25query(tsvector, regclass)&lt;/code&gt; builds queries, and &lt;code&gt;&amp;lt;&amp;amp;&amp;gt;&lt;/code&gt; returns negative &lt;code&gt;double precision&lt;/code&gt; BM25 scores (&lt;code&gt;+0.0&lt;/code&gt; for zero scores).&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Relocatable schema&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Extension objects install into the target schema (&lt;code&gt;public&lt;/code&gt; by default) and support &lt;code&gt;ALTER EXTENSION vchord_bm25 SET SCHEMA&lt;/code&gt;. The fixed &lt;code&gt;bm25_catalog&lt;/code&gt; schema is no longer required.&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Continuing WAND scans&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;bm25.limit&lt;/code&gt; and the index &lt;code&gt;limit&lt;/code&gt; reloption (&lt;code&gt;0..=65535&lt;/code&gt;, default &lt;code&gt;100&lt;/code&gt;, &lt;code&gt;0&lt;/code&gt; selects &lt;code&gt;100&lt;/code&gt;) set the initial WAND top-K batch size rather than a hard result cap; scans continue through matching, zero-score, and &lt;code&gt;NULL&lt;/code&gt; rows as needed.&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Shared statistics cache&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Prepared corpus statistics and growing-segment scores are cached per &lt;code&gt;(index, query)&lt;/code&gt; within &lt;code&gt;work_mem&lt;/code&gt; and shared between indexed scans and scalar &lt;code&gt;&amp;lt;&amp;amp;&amp;gt;&lt;/code&gt; evaluations (&lt;code&gt;PARALLEL RESTRICTED&lt;/code&gt;). &lt;code&gt;NULL&lt;/code&gt; rows are excluded from document count, and WAND upper bounds adjust for &lt;code&gt;avgdl&lt;/code&gt; drift.&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Concurrent maintenance&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Format 4 storage uses generation stamps, a growing segment, and one crash-resumable retired generation. &lt;code&gt;VACUUM&lt;/code&gt; and &lt;code&gt;bm25_maintain(regclass)&lt;/code&gt; merge growing tuples under &lt;code&gt;ShareUpdateExclusiveLock&lt;/code&gt; using PostgreSQL&amp;rsquo;s &lt;code&gt;GlobalVis&lt;/code&gt; horizon without taking &lt;code&gt;AccessExclusiveLock&lt;/code&gt; or allocating an XID. &lt;code&gt;ANALYZE&lt;/code&gt; is a no-op.&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Standby replay&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;VACUUM&lt;/code&gt; tombstones only already-pruned heap TIDs, raising no false standby conflicts; only page recycling raises SQLSTATE &lt;code&gt;40001&lt;/code&gt; (&lt;code&gt;serialization_failure&lt;/code&gt;) if an older standby query is still reading a retired generation.&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Corpus permissions &amp;amp; RLS&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Both indexed scans and scalar &lt;code&gt;&amp;lt;&amp;amp;&amp;gt;&lt;/code&gt; check current-role table &lt;code&gt;SELECT&lt;/code&gt; (or column &lt;code&gt;SELECT&lt;/code&gt; for simple single-column non-partial indexes) and reject queries when RLS policies apply (&lt;code&gt;0A000&lt;/code&gt;), preventing corpus-statistic leaks. Scalar scoring locks the heap before the index.&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Build &amp;amp; platform support&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align: left&#34;&gt;Upgraded to &lt;code&gt;pgrx 0.19.3&lt;/code&gt; for PostgreSQL 14–18 on Linux x86_64 and aarch64, with runtime SIMD dispatch (&lt;code&gt;AVX512&lt;/code&gt; / &lt;code&gt;AVX2&lt;/code&gt; / &lt;code&gt;NEON&lt;/code&gt;) and responsive query cancellation outside page locks.&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;h2 id=&#34;native-tsvector&#34;&gt;Native &lt;code&gt;tsvector&lt;/code&gt; indexing and relocatable schema&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;In &lt;code&gt;vchord_bm25 1.0.0&lt;/code&gt;, documents are stored as standard PostgreSQL &lt;code&gt;tsvector&lt;/code&gt; values with lexeme positions (&lt;code&gt;1..=16383&lt;/code&gt;, up to 256 positions per lexeme). Each position entry counts as one occurrence of that lexeme when computing term frequency (&lt;code&gt;tf&lt;/code&gt;) and document length (&lt;code&gt;dl&lt;/code&gt;); lexemes without positions are ignored by BM25 scoring.&lt;/p&gt;</description>
      </item>
    
  </channel>
</rss>
