| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2026 Tiger Data, Inc. | ||
| 3 | * Licensed under the PostgreSQL License. See LICENSE for details. | ||
| 4 | * | ||
| 5 | * index_base.h - Common index descriptor for search | ||
| 6 | * | ||
| 7 | * PrismIndexBase contains the fields needed by the shared search | ||
| 8 | * path. Standalone embeds it in PrismIndex; PG populates it from | ||
| 9 | * the meta page and amcache. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef PRISM_INDEX_BASE_H | ||
| 13 | #define PRISM_INDEX_BASE_H | ||
| 14 | |||
| 15 | #include "core/types.h" | ||
| 16 | #include "index/centroid_page.h" | ||
| 17 | #include "index/storage.h" | ||
| 18 | #include "quant/rabitq.h" | ||
| 19 | |||
| 20 | /* | ||
| 21 | * Block 0 holds the metapage, so the centroid region a build reserves | ||
| 22 | * starts here and runs to first_posting. | ||
| 23 | */ | ||
| 24 | #define PRISM_FIRST_CENTROID_BLKNO 1 | ||
| 25 | |||
| 26 | /* | ||
| 27 | * Posting pages the scan cost estimate prices, and what prism_index_settings | ||
| 28 | * reports as posting_pages. | ||
| 29 | * | ||
| 30 | * Block 0 is the metapage and the centroid region a build reserves starts at | ||
| 31 | * PRISM_FIRST_CENTROID_BLKNO, so on a freshly built index the posting pages | ||
| 32 | * are everything from first_posting onward. That range stops measuring them | ||
| 33 | * once a split overflows a level-0 centroid page: the split extends the | ||
| 34 | * relation and chains the new centroid page past the posting region, so the | ||
| 35 | * page is both counted by ncentroid_pages and, to a range measurement, a | ||
| 36 | * posting page. Taking the maintained count out of the relation's size | ||
| 37 | * instead is layout-independent and leaves it in one term only. | ||
| 38 | * | ||
| 39 | * num_pages is the relation's current size, not pg_class.relpages: both | ||
| 40 | * callers read it straight from the relation, the planner included, so the | ||
| 41 | * count never lags a statistics update. | ||
| 42 | * | ||
| 43 | * An upper bound rather than an exact count: free and new pages from | ||
| 44 | * extension slack are included, the same over-count the estimate already | ||
| 45 | * documents for a bloated index. | ||
| 46 | */ | ||
| 47 | static inline double | ||
| 48 | 421 | prism_index_posting_pages(double num_pages, double ncentroid_pages) | |
| 49 | { | ||
| 50 | 421 | double n = num_pages - (double)PRISM_FIRST_CENTROID_BLKNO - | |
| 51 | ncentroid_pages; | ||
| 52 | |||
| 53 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 415 times.
|
421 | return n < 1.0 ? 1.0 : n; |
| 54 | } | ||
| 55 | |||
| 56 | typedef struct PrismIndexBase | ||
| 57 | { | ||
| 58 | /* RaBitQ (must outlive the search context) */ | ||
| 59 | RaBitQParams *params; | ||
| 60 | float *pt_global_mean; | ||
| 61 | uint64_t rabitq_seed; | ||
| 62 | |||
| 63 | /* PG: both point to the same VsPgStorage (one index relation). | ||
| 64 | * Standalone: separate ArrayPageStorage for centroids vs postings. */ | ||
| 65 | VsStorage *centroid_storage; | ||
| 66 | VsStorage *posting_storage; | ||
| 67 | |||
| 68 | /* Non-NULL for inline page access (standalone postings) */ | ||
| 69 | char *page_base; | ||
| 70 | |||
| 71 | /* Index metadata */ | ||
| 72 | Dimension dim; | ||
| 73 | uint8_t nlevels; | ||
| 74 | /* Children per tree node; floors the intermediate beam width so the | ||
| 75 | * top-nprobe leaves stay reachable (0 = unknown, no floor). */ | ||
| 76 | uint8_t fan_out; | ||
| 77 | /* Leaf (posting-list) count; when nprobe covers every leaf the | ||
| 78 | * intermediate beam keeps whole levels so no subtree is pruned | ||
| 79 | * (0 = unknown, no full-coverage floor). */ | ||
| 80 | uint32_t nlist; | ||
| 81 | /* | ||
| 82 | * Root of the centroid tree, which every descent starts from -- NOT the | ||
| 83 | * first block of the centroid region, despite the name. Where the root | ||
| 84 | * sits inside the region depends on the build: a serial build writes the | ||
| 85 | * centroid pages post-order and the root lands last, a parallel one | ||
| 86 | * places it first. The region itself is always | ||
| 87 | * [PRISM_FIRST_CENTROID_BLKNO, first_posting). | ||
| 88 | */ | ||
| 89 | BlockNumber first_centroid; | ||
| 90 | /* | ||
| 91 | * First block of the posting-head region; leaf c's head is | ||
| 92 | * first_posting + c. Fixed for the life of the index, since that formula | ||
| 93 | * is how every head is located, so nothing can be inserted below it. | ||
| 94 | */ | ||
| 95 | BlockNumber first_posting; | ||
| 96 | /* | ||
| 97 | * Centroid pages reachable from first_centroid. Maintained rather than | ||
| 98 | * derived, because a split with no room on a level-0 page extends the | ||
| 99 | * relation and chains the new page past first_posting, after which the | ||
| 100 | * reserved block range no longer measures the count. | ||
| 101 | */ | ||
| 102 | uint32_t ncentroid_pages; | ||
| 103 | DistanceMetric metric; | ||
| 104 | PrismCentroidFormat centroid_format; | ||
| 105 | int fastscan; /* 0=off, 8=uint8, 16=uint16 hacc */ | ||
| 106 | float centroid_error_scale; /* scales centroid pruning error (1=default) */ | ||
| 107 | float centroid_beam_scale; /* intermediate beam width / nprobe | ||
| 108 | (0.25=default) */ | ||
| 109 | /* Build-only: exact internal-node centroids for the build descent | ||
| 110 | * (see PrismExactInternalCentroids in centroid_search.h). NULL — the | ||
| 111 | * default everywhere the base is zero-initialized — keeps the | ||
| 112 | * estimated scoring; the query and insert paths never set it. */ | ||
| 113 | const struct PrismExactInternalCentroids *exact_internal; | ||
| 114 | } PrismIndexBase; | ||
| 115 | |||
| 116 | static inline RaBitQParams * | ||
| 117 | 18253 | prism_index_ensure_rabitq(PrismIndexBase *idx) | |
| 118 | { | ||
| 119 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 18253 times.
|
18253 | if (idx->params == NULL) |
| 120 | { | ||
| 121 | ✗ | idx->params = vs_rabitq_create(idx->dim, idx->rabitq_seed); | |
| 122 | ✗ | if (idx->pt_global_mean != NULL) | |
| 123 | ✗ | vs_rabitq_rotate( | |
| 124 | ✗ | idx->params, idx->pt_global_mean, idx->pt_global_mean); | |
| 125 | } | ||
| 126 | 18253 | return idx->params; | |
| 127 | } | ||
| 128 | |||
| 129 | #endif /* PRISM_INDEX_BASE_H */ | ||
| 130 |