| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2026 Tiger Data, Inc. | ||
| 3 | * Licensed under the PostgreSQL License. See LICENSE for details. | ||
| 4 | * | ||
| 5 | * pg_compat.h - Standalone definitions for PostgreSQL page types | ||
| 6 | * | ||
| 7 | * Provides binary-compatible definitions of BlockNumber, ItemPointerData, | ||
| 8 | * PageHeaderData, and page functions for standalone builds. In PG builds, | ||
| 9 | * the real PostgreSQL headers are used instead. | ||
| 10 | * | ||
| 11 | * This enables centroid page code to compile and test without PostgreSQL | ||
| 12 | * while producing pages with identical binary layout. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifndef VS_PG_COMPAT_H | ||
| 16 | #define VS_PG_COMPAT_H | ||
| 17 | |||
| 18 | /* Only for standalone builds — PG builds use real headers */ | ||
| 19 | #ifdef VS_STANDALONE | ||
| 20 | |||
| 21 | #include <assert.h> | ||
| 22 | #include <stdint.h> | ||
| 23 | #include <string.h> | ||
| 24 | |||
| 25 | /* | ||
| 26 | * PostgreSQL's Assert() in standalone builds. Maps to the C library assert, | ||
| 27 | * which is likewise compiled out when NDEBUG is defined, so shared code can | ||
| 28 | * use Assert() under either back-end. | ||
| 29 | */ | ||
| 30 | #ifndef Assert | ||
| 31 | #define Assert(condition) assert(condition) | ||
| 32 | #endif | ||
| 33 | |||
| 34 | typedef uintptr_t Datum; | ||
| 35 | |||
| 36 | /* Opaque DSM segment; unused in standalone (threads share the heap). */ | ||
| 37 | typedef struct dsm_segment dsm_segment; | ||
| 38 | typedef char *Pointer; | ||
| 39 | |||
| 40 | #define PointerGetDatum(p) ((Datum)(p)) | ||
| 41 | #define DatumGetPointer(d) ((Pointer)(d)) | ||
| 42 | |||
| 43 | typedef uint32_t BlockNumber; | ||
| 44 | typedef uint16_t OffsetNumber; | ||
| 45 | typedef uint16_t LocationIndex; | ||
| 46 | typedef char *Page; | ||
| 47 | |||
| 48 | #define InvalidBlockNumber ((BlockNumber)0xFFFFFFFF) | ||
| 49 | #define InvalidOffsetNumber ((OffsetNumber)0) | ||
| 50 | #define BLCKSZ 8192 | ||
| 51 | |||
| 52 | /* Alignment macros matching PostgreSQL (8-byte on 64-bit) */ | ||
| 53 | #define MAXALIGN(len) (((uintptr_t)(len) + 7) & ~(uintptr_t)7) | ||
| 54 | |||
| 55 | /* Single-palloc ceiling, as in PG's memutils.h (vs_alloc maps to malloc in | ||
| 56 | * standalone, but the shared build code sizes against one limit). */ | ||
| 57 | #define MaxAllocSize ((size_t)0x3fffffff) | ||
| 58 | |||
| 59 | /* Page layout version — matches PostgreSQL 8.3+ */ | ||
| 60 | #define PG_PAGE_LAYOUT_VERSION 4 | ||
| 61 | |||
| 62 | /* | ||
| 63 | * PageHeaderData — binary-compatible with PostgreSQL's PageHeaderData | ||
| 64 | * (24 bytes). We use pd_lower/pd_upper/pd_special to track page regions. | ||
| 65 | */ | ||
| 66 | typedef struct PageHeaderData | ||
| 67 | { | ||
| 68 | uint64_t pd_lsn; /* 8B - WAL LSN (unused standalone) */ | ||
| 69 | uint16_t pd_checksum; /* 2B */ | ||
| 70 | uint16_t pd_flags; /* 2B */ | ||
| 71 | LocationIndex pd_lower; /* 2B - end of forward-growing region */ | ||
| 72 | LocationIndex pd_upper; /* 2B - start of backward-growing region */ | ||
| 73 | LocationIndex pd_special; /* 2B - start of special/opaque area */ | ||
| 74 | uint16_t pd_pagesize_version; /* 2B - page size | version */ | ||
| 75 | uint32_t pd_prune_xid; /* 4B - (unused standalone) */ | ||
| 76 | } PageHeaderData; | ||
| 77 | |||
| 78 | typedef PageHeaderData *PageHeader; | ||
| 79 | |||
| 80 | #define SizeOfPageHeaderData (sizeof(PageHeaderData)) | ||
| 81 | |||
| 82 | /* | ||
| 83 | * PageGetContents — returns pointer to page content area. | ||
| 84 | * For pages without line pointers, content starts after the header. | ||
| 85 | */ | ||
| 86 | static inline char * | ||
| 87 | 15684962 | PageGetContents(Page page) | |
| 88 | { | ||
| 89 | 15684962 | return (char *)page + MAXALIGN(SizeOfPageHeaderData); | |
| 90 | } | ||
| 91 | |||
| 92 | /* | ||
| 93 | * PageGetSpecialPointer — returns pointer to special (opaque) area. | ||
| 94 | * Matches PostgreSQL's PageGetSpecialPointer macro. | ||
| 95 | */ | ||
| 96 | #define PageGetSpecialPointer(page) ((page) + ((PageHeader)(page))->pd_special) | ||
| 97 | |||
| 98 | /* | ||
| 99 | * PageIsNew — true if page has not been initialized by PageInit. | ||
| 100 | */ | ||
| 101 | static inline bool | ||
| 102 | 784 | PageIsNew(const Page page) | |
| 103 | { | ||
| 104 | 784 | return ((const PageHeaderData *)page)->pd_upper == 0; | |
| 105 | } | ||
| 106 | |||
| 107 | /* | ||
| 108 | * PageGetPageSize — extract page size from pd_pagesize_version. | ||
| 109 | */ | ||
| 110 | static inline uint16_t | ||
| 111 | 784 | PageGetPageSize(const Page page) | |
| 112 | { | ||
| 113 | 784 | return ((const PageHeaderData *)page)->pd_pagesize_version & | |
| 114 | (uint16_t)0xFF00; | ||
| 115 | } | ||
| 116 | |||
| 117 | /* | ||
| 118 | * PageGetSpecialSize — size of the special (opaque) area, as PageInit | ||
| 119 | * reserved it (MAXALIGN'd). Matches PostgreSQL's PageGetSpecialSize. | ||
| 120 | */ | ||
| 121 | static inline uint16_t | ||
| 122 | 784 | PageGetSpecialSize(const Page page) | |
| 123 | { | ||
| 124 | 784 | return (uint16_t)(PageGetPageSize(page) - | |
| 125 | 784 | ((const PageHeaderData *)page)->pd_special); | |
| 126 | } | ||
| 127 | |||
| 128 | /* | ||
| 129 | * PageInit — initialize a page with standard PG header layout. | ||
| 130 | * | ||
| 131 | * Matches PostgreSQL's PageInit: zeroes the page, sets pd_lower to | ||
| 132 | * the end of the header, pd_upper and pd_special to pageSize minus | ||
| 133 | * MAXALIGN'd special size, and packs size+version. | ||
| 134 | */ | ||
| 135 | static inline void | ||
| 136 | 4866 | PageInit(Page page, uint16_t pageSize, uint16_t specialSize) | |
| 137 | { | ||
| 138 | 4866 | PageHeader header = (PageHeader)page; | |
| 139 | |||
| 140 | 4866 | specialSize = (uint16_t)MAXALIGN(specialSize); | |
| 141 | |||
| 142 | 4866 | memset(page, 0, pageSize); | |
| 143 | |||
| 144 | 4866 | header->pd_lower = SizeOfPageHeaderData; | |
| 145 | 4866 | header->pd_upper = pageSize - specialSize; | |
| 146 | 4866 | header->pd_special = pageSize - specialSize; | |
| 147 | 4866 | header->pd_pagesize_version = (pageSize & 0xFF00) | PG_PAGE_LAYOUT_VERSION; | |
| 148 | 4866 | } | |
| 149 | |||
| 150 | /* | ||
| 151 | * ItemPointerData — 6-byte heap TID, binary-compatible with PG. | ||
| 152 | * BlockIdData is two uint16 (high/low halves of BlockNumber). | ||
| 153 | */ | ||
| 154 | typedef struct BlockIdData | ||
| 155 | { | ||
| 156 | uint16_t bi_hi; | ||
| 157 | uint16_t bi_lo; | ||
| 158 | } BlockIdData; | ||
| 159 | |||
| 160 | typedef struct ItemPointerData | ||
| 161 | { | ||
| 162 | BlockIdData ip_blkid; | ||
| 163 | OffsetNumber ip_posid; | ||
| 164 | } __attribute__((packed, aligned(2))) ItemPointerData; | ||
| 165 | |||
| 166 | typedef ItemPointerData *ItemPointer; | ||
| 167 | |||
| 168 | static inline BlockNumber | ||
| 169 | 726914 | ItemPointerGetBlockNumber(const ItemPointerData *tip) | |
| 170 | { | ||
| 171 | 1453828 | return ((BlockNumber)tip->ip_blkid.bi_hi << 16) | | |
| 172 | 726914 | (BlockNumber)tip->ip_blkid.bi_lo; | |
| 173 | } | ||
| 174 | |||
| 175 | static inline OffsetNumber | ||
| 176 | 576644 | ItemPointerGetOffsetNumber(const ItemPointerData *tip) | |
| 177 | { | ||
| 178 | 576644 | return tip->ip_posid; | |
| 179 | } | ||
| 180 | |||
| 181 | static inline void | ||
| 182 | 13443536 | ItemPointerSet(ItemPointerData *tip, BlockNumber blk, OffsetNumber off) | |
| 183 | { | ||
| 184 | 13443536 | tip->ip_blkid.bi_hi = (uint16_t)(blk >> 16); | |
| 185 | 13443536 | tip->ip_blkid.bi_lo = (uint16_t)blk; | |
| 186 | 13443536 | tip->ip_posid = off; | |
| 187 | 13443536 | } | |
| 188 | |||
| 189 | static inline void | ||
| 190 | ItemPointerSetInvalid(ItemPointerData *tip) | ||
| 191 | { | ||
| 192 | tip->ip_blkid.bi_hi = 0xFFFF; | ||
| 193 | tip->ip_blkid.bi_lo = 0xFFFF; | ||
| 194 | tip->ip_posid = InvalidOffsetNumber; | ||
| 195 | } | ||
| 196 | |||
| 197 | /* | ||
| 198 | * Types/macros the shared parallel build (parallel_build.h) expects from | ||
| 199 | * PostgreSQL's postgres.h / utils/rel.h. | ||
| 200 | */ | ||
| 201 | typedef size_t Size; | ||
| 202 | |||
| 203 | #define BUFFERALIGN(len) MAXALIGN(len) | ||
| 204 | #define UINT64CONST(x) (x##ULL) | ||
| 205 | |||
| 206 | /* PG checks for query cancellation in long loops; nothing to do standalone. */ | ||
| 207 | #define CHECK_FOR_INTERRUPTS() ((void)0) | ||
| 208 | |||
| 209 | /* The wait-event class is PG instrumentation; the barrier/latch shims ignore | ||
| 210 | * the argument, so any value will do. */ | ||
| 211 | #define WAIT_EVENT_PARALLEL_CREATE_INDEX_SCAN 0 | ||
| 212 | |||
| 213 | /* | ||
| 214 | * Standalone Relation: the parallel build's "heap" carries the in-memory | ||
| 215 | * vector array it scans; its "index" carries a live page count so the few | ||
| 216 | * RelationGetNumberOfBlocks() call sites resolve. The shared driver never | ||
| 217 | * dereferences a Relation directly — the standalone back-end opens these out | ||
| 218 | * of its shared state — so only RelationGetNumberOfBlocks needs the fields. | ||
| 219 | */ | ||
| 220 | typedef struct RelationData | ||
| 221 | { | ||
| 222 | const float *vectors; /* heap: vectors[nvecs * dim] */ | ||
| 223 | uint32_t nvecs; /* heap: vector count */ | ||
| 224 | uint32_t dim; /* heap: dimension */ | ||
| 225 | const uint32_t *page_count; /* index: live page count (NULL for a heap) */ | ||
| 226 | } RelationData; | ||
| 227 | |||
| 228 | typedef struct RelationData *Relation; | ||
| 229 | |||
| 230 | static inline BlockNumber | ||
| 231 | 270 | RelationGetNumberOfBlocks(Relation rel) | |
| 232 | { | ||
| 233 |
2/2✓ Branch 0 taken 82 times.
✓ Branch 1 taken 188 times.
|
270 | if (rel->page_count != NULL) |
| 234 | 82 | return (BlockNumber)*rel->page_count; | |
| 235 | |||
| 236 | /* | ||
| 237 | * Heap: report a block count from which the build's row estimate | ||
| 238 | * (blocks * BLCKSZ / bytes-per-tuple) recovers the true vector count. | ||
| 239 | */ | ||
| 240 | 188 | uint64_t bytes_per_tuple = (uint64_t)rel->dim * sizeof(float) + 32; | |
| 241 | 188 | return (BlockNumber)(((uint64_t)rel->nvecs * bytes_per_tuple + BLCKSZ - | |
| 242 | 188 | 1) / | |
| 243 | BLCKSZ); | ||
| 244 | } | ||
| 245 | |||
| 246 | /* | ||
| 247 | * The shared build asks for an IndexInfo only to read ii_ParallelWorkers (the | ||
| 248 | * leader) and to hand back to prism_build_scan (ignored in standalone). A | ||
| 249 | * small struct covers both; BuildIndexInfo is unused by the standalone scan. | ||
| 250 | */ | ||
| 251 | typedef struct IndexInfo | ||
| 252 | { | ||
| 253 | int ii_ParallelWorkers; | ||
| 254 | } IndexInfo; | ||
| 255 | |||
| 256 | static inline IndexInfo * | ||
| 257 | 106 | BuildIndexInfo(Relation rel) | |
| 258 | { | ||
| 259 | (void)rel; | ||
| 260 | 106 | return NULL; | |
| 261 | } | ||
| 262 | |||
| 263 | /* PG per-worker instrumentation — no-ops in standalone (single process). */ | ||
| 264 | typedef struct WalUsage | ||
| 265 | { | ||
| 266 | int unused; | ||
| 267 | } WalUsage; | ||
| 268 | |||
| 269 | typedef struct BufferUsage | ||
| 270 | { | ||
| 271 | int unused; | ||
| 272 | } BufferUsage; | ||
| 273 | |||
| 274 | #define InstrAccumParallelQuery(buf, wal) ((void)(buf), (void)(wal)) | ||
| 275 | |||
| 276 | #endif /* VS_STANDALONE */ | ||
| 277 | #endif /* VS_PG_COMPAT_H */ | ||
| 278 |