| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2026 Tiger Data, Inc. | ||
| 3 | * Licensed under the PostgreSQL License. See LICENSE for details. | ||
| 4 | * | ||
| 5 | * vs_pg.c - pg_vectorsearch PostgreSQL extension entry point | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <postgres.h> | ||
| 9 | |||
| 10 | #include "vs_config.h" | ||
| 11 | |||
| 12 | #include <access/reloptions.h> | ||
| 13 | #include <catalog/namespace.h> | ||
| 14 | #include <fmgr.h> | ||
| 15 | #include <utils/builtins.h> | ||
| 16 | #include <utils/guc.h> | ||
| 17 | |||
| 18 | #include "algo/distance.h" | ||
| 19 | #include "algo/kmeans.h" | ||
| 20 | #include "explain.h" | ||
| 21 | #include "git_commit.h" | ||
| 22 | #include "index/index_build.h" | ||
| 23 | #include "index/posting_page.h" | ||
| 24 | #include "index/query_scan.h" | ||
| 25 | #include "pg/bufstorage.h" | ||
| 26 | #include "scan.h" | ||
| 27 | #include "scan_bound.h" | ||
| 28 | #include "support_pg.h" | ||
| 29 | |||
| 30 | 255 | PG_MODULE_MAGIC; | |
| 31 | |||
| 32 | /* GUC variables */ | ||
| 33 | int prism_distance_mode = VS_DISTANCE_MODE_DEFAULT; | ||
| 34 | int prism_nprobe = 0; | ||
| 35 | int prism_query_limit = 0; | ||
| 36 | int prism_fastscan_bits = 16; | ||
| 37 | bool prism_rerank = true; | ||
| 38 | bool prism_log_build_stats = false; | ||
| 39 | double prism_centroid_error_scale = 0.0; | ||
| 40 | double prism_centroid_beam_scale = 0.5; | ||
| 41 | int prism_leaf_refine_threshold = 0; | ||
| 42 | static bool prism_recent_buffers = true; | ||
| 43 | double prism_probe_expand = 2.0; | ||
| 44 | static int prism_rerank_pool = 0; | ||
| 45 | |||
| 46 | static void | ||
| 47 | 255 | vs_recent_buffers_assign_hook(bool newval, void *extra) | |
| 48 | { | ||
| 49 | 255 | vs_pg_storage_set_recent_buffers(newval); | |
| 50 | 255 | } | |
| 51 | |||
| 52 | static void | ||
| 53 | 257 | vs_probe_expand_assign_hook(double newval, void *extra) | |
| 54 | { | ||
| 55 | 257 | prism_query_set_probe_expand(newval); | |
| 56 | 257 | } | |
| 57 | |||
| 58 | static void | ||
| 59 | 266 | vs_rerank_pool_assign_hook(int newval, void *extra) | |
| 60 | { | ||
| 61 | 266 | prism_query_set_rerank_pool((int32_t)newval); | |
| 62 | 266 | } | |
| 63 | |||
| 64 | static const struct config_enum_entry vs_distance_mode_options[] = { | ||
| 65 | {"default", VS_DISTANCE_MODE_DEFAULT, false}, | ||
| 66 | {"asymmetric", VS_DISTANCE_MODE_ASYMMETRIC, false}, | ||
| 67 | {"symmetric", VS_DISTANCE_MODE_SYMMETRIC, false}, | ||
| 68 | {NULL, 0, false}, | ||
| 69 | }; | ||
| 70 | |||
| 71 | static const struct config_enum_entry vs_fastscan_bits_options[] = { | ||
| 72 | {"8", 8, false}, | ||
| 73 | {"16", 16, false}, | ||
| 74 | {NULL, 0, false}, | ||
| 75 | }; | ||
| 76 | |||
| 77 | /* Index reloptions */ | ||
| 78 | relopt_kind prism_relopt_kind; | ||
| 79 | |||
| 80 | static relopt_enum_elt_def distance_mode_relopt_members[] = { | ||
| 81 | {"asymmetric", VS_DISTANCE_MODE_ASYMMETRIC}, | ||
| 82 | {"symmetric", VS_DISTANCE_MODE_SYMMETRIC}, | ||
| 83 | {NULL, 0}, | ||
| 84 | }; | ||
| 85 | |||
| 86 | /* "true"/"false" are accepted aliases for "on"/"off". */ | ||
| 87 | static relopt_enum_elt_def centroid_compression_relopt_members[] = { | ||
| 88 | {"auto", PRISM_CENTROID_COMPRESSION_AUTO}, | ||
| 89 | {"on", PRISM_CENTROID_COMPRESSION_ON}, | ||
| 90 | {"true", PRISM_CENTROID_COMPRESSION_ON}, | ||
| 91 | {"off", PRISM_CENTROID_COMPRESSION_OFF}, | ||
| 92 | {"false", PRISM_CENTROID_COMPRESSION_OFF}, | ||
| 93 | {NULL, 0}, | ||
| 94 | }; | ||
| 95 | |||
| 96 | /* Shared by the `fastscan` and `centroid_fastscan` options. */ | ||
| 97 | static relopt_enum_elt_def fastscan_mode_relopt_members[] = { | ||
| 98 | {"auto", PRISM_FASTSCAN_MODE_AUTO}, | ||
| 99 | {"on", PRISM_FASTSCAN_MODE_ON}, | ||
| 100 | {"true", PRISM_FASTSCAN_MODE_ON}, | ||
| 101 | {"off", PRISM_FASTSCAN_MODE_OFF}, | ||
| 102 | {"false", PRISM_FASTSCAN_MODE_OFF}, | ||
| 103 | {NULL, 0}, | ||
| 104 | }; | ||
| 105 | |||
| 106 | void _PG_init(void); | ||
| 107 | |||
| 108 | void | ||
| 109 | _PG_init(void) | ||
| 110 | { | ||
| 111 | 255 | DefineCustomEnumVariable( | |
| 112 | VS_GUC_PREFIX ".distance_mode", | ||
| 113 | "RaBitQ distance computation mode.", | ||
| 114 | "default (use index setting), asymmetric, or symmetric", | ||
| 115 | &prism_distance_mode, | ||
| 116 | VS_DISTANCE_MODE_DEFAULT, | ||
| 117 | vs_distance_mode_options, | ||
| 118 | PGC_USERSET, | ||
| 119 | 0, | ||
| 120 | NULL, | ||
| 121 | NULL, | ||
| 122 | NULL); | ||
| 123 | |||
| 124 | 255 | DefineCustomIntVariable( | |
| 125 | VS_GUC_PREFIX ".nprobe", | ||
| 126 | "Number of clusters to probe per query.", | ||
| 127 | "0 derives it from the index's cluster count " | ||
| 128 | "(~0.5*sqrt(nlist), targeting ~0.95 recall). Lower it for " | ||
| 129 | "speed, raise it for recall.", | ||
| 130 | &prism_nprobe, | ||
| 131 | 0, | ||
| 132 | 0, | ||
| 133 | 10000, | ||
| 134 | PGC_USERSET, | ||
| 135 | 0, | ||
| 136 | NULL, | ||
| 137 | NULL, | ||
| 138 | NULL); | ||
| 139 | |||
| 140 | 255 | DefineCustomIntVariable( | |
| 141 | VS_GUC_PREFIX ".query_limit", | ||
| 142 | "Caps the top-k an index scan is sized for (0 = no cap).", | ||
| 143 | "A scan sizes its top-k from the LIMIT above it, inflated by " | ||
| 144 | "the planner's selectivity estimate for any filter the " | ||
| 145 | "executor applies above it, and bounded by work_mem -- so a " | ||
| 146 | "query wanting more rows than that budget affords is answered " | ||
| 147 | "by raising work_mem. With no LIMIT to size from, the query " | ||
| 148 | "has asked for every row in order and the scan is sized for " | ||
| 149 | "what work_mem affords or the table's estimated row count, " | ||
| 150 | "whichever is smaller. Set this to cap that: it lowers the " | ||
| 151 | "sizing when it is below what the query asked for and is " | ||
| 152 | "ignored otherwise, which bounds a query that has no LIMIT or " | ||
| 153 | "one set far above the rows actually read. No scan is sized " | ||
| 154 | "below 10 rows.", | ||
| 155 | &prism_query_limit, | ||
| 156 | 0, | ||
| 157 | 0, | ||
| 158 | INT_MAX, | ||
| 159 | PGC_USERSET, | ||
| 160 | 0, | ||
| 161 | NULL, | ||
| 162 | NULL, | ||
| 163 | NULL); | ||
| 164 | |||
| 165 | 255 | DefineCustomIntVariable( | |
| 166 | VS_GUC_PREFIX ".leaf_refine_threshold", | ||
| 167 | "Sample-per-leaf count below which a subsampled build refines " | ||
| 168 | "the leaf centroids on the full table.", | ||
| 169 | "The k-means sample trains each leaf's encode reference; the " | ||
| 170 | "reference's error shrinks with the leaf's sample count " | ||
| 171 | "(stderr ~ spread/sqrt(n)), so with enough samples per leaf the " | ||
| 172 | "full-table refine scan buys no recall. Below this many samples " | ||
| 173 | "per leaf the sample mean is noisy and the build re-centers the " | ||
| 174 | "references from the whole table (one extra scan). 0 (the " | ||
| 175 | "default) disables refinement; a large value refines whenever " | ||
| 176 | "the sample was bounded below the table.", | ||
| 177 | &prism_leaf_refine_threshold, | ||
| 178 | 0, | ||
| 179 | 0, | ||
| 180 | INT_MAX, | ||
| 181 | PGC_USERSET, | ||
| 182 | 0, | ||
| 183 | NULL, | ||
| 184 | NULL, | ||
| 185 | NULL); | ||
| 186 | |||
| 187 | 255 | DefineCustomEnumVariable( | |
| 188 | VS_GUC_PREFIX ".fastscan_bits", | ||
| 189 | "Fastscan LUT quantization bits.", | ||
| 190 | "8 is faster, 16 is more accurate", | ||
| 191 | &prism_fastscan_bits, | ||
| 192 | 16, | ||
| 193 | vs_fastscan_bits_options, | ||
| 194 | PGC_USERSET, | ||
| 195 | 0, | ||
| 196 | NULL, | ||
| 197 | NULL, | ||
| 198 | NULL); | ||
| 199 | |||
| 200 | 255 | DefineCustomBoolVariable( | |
| 201 | VS_GUC_PREFIX ".rerank", | ||
| 202 | "Enable reranking with exact distances.", | ||
| 203 | NULL, | ||
| 204 | &prism_rerank, | ||
| 205 | true, | ||
| 206 | PGC_USERSET, | ||
| 207 | 0, | ||
| 208 | NULL, | ||
| 209 | NULL, | ||
| 210 | NULL); | ||
| 211 | |||
| 212 | 255 | DefineCustomBoolVariable( | |
| 213 | VS_GUC_PREFIX ".log_build_stats", | ||
| 214 | "Log per-phase index-build resource statistics.", | ||
| 215 | "When on, each build phase logs its elapsed time, build-heap " | ||
| 216 | "usage, and CPU/maxrss (via the server's ShowUsage), plus a final " | ||
| 217 | "summary, at LOG. Off by default; modeled on the core btree " | ||
| 218 | "log_btree_build_stats developer option. The up-front " | ||
| 219 | "planned-allocation line is logged regardless of this setting.", | ||
| 220 | &prism_log_build_stats, | ||
| 221 | false, | ||
| 222 | PGC_SUSET, | ||
| 223 | GUC_NOT_IN_SAMPLE, | ||
| 224 | NULL, | ||
| 225 | NULL, | ||
| 226 | NULL); | ||
| 227 | |||
| 228 | 255 | DefineCustomRealVariable( | |
| 229 | VS_GUC_PREFIX ".centroid_error_scale", | ||
| 230 | "Centroid-search beam width, as a multiple of the RaBitQ " | ||
| 231 | "distance-error margin.", | ||
| 232 | "During centroid routing each candidate centroid has an " | ||
| 233 | "approximate (RaBitQ-quantized) distance plus an error margin; " | ||
| 234 | "this multiplies that margin when deciding which centroids the " | ||
| 235 | "beam keeps at each tree level. 0 (default) ignores the margin " | ||
| 236 | "and " | ||
| 237 | "keeps only the closest centroids by point estimate -- the " | ||
| 238 | "narrowest and fastest beam. 1 widens the beam to also keep " | ||
| 239 | "centroids whose error interval still overlaps the cutoff -- ones " | ||
| 240 | "that might rank among the closest once quantization error is " | ||
| 241 | "accounted for -- the most recall-conservative setting, at the " | ||
| 242 | "cost of scoring more centroids. Larger values widen it further. " | ||
| 243 | "Applies to both compressed centroid formats (RaBitQ and " | ||
| 244 | "FASTSCAN); float/half centroid pages have exact distances (no " | ||
| 245 | "error margin) and are unaffected.", | ||
| 246 | &prism_centroid_error_scale, | ||
| 247 | 0.0, | ||
| 248 | 0.0, | ||
| 249 | 10.0, | ||
| 250 | PGC_USERSET, | ||
| 251 | 0, | ||
| 252 | NULL, | ||
| 253 | NULL, | ||
| 254 | NULL); | ||
| 255 | |||
| 256 | 255 | DefineCustomRealVariable( | |
| 257 | VS_GUC_PREFIX ".centroid_beam_scale", | ||
| 258 | "Intermediate centroid beam width as a fraction of nprobe.", | ||
| 259 | "Sets the beam width at the intermediate tree levels to this " | ||
| 260 | "fraction of nprobe; the leaf level always returns the full " | ||
| 261 | "nprobe, and the beam never drops below a small floor of " | ||
| 262 | "candidates (or nprobe itself, whichever is less) — at small " | ||
| 263 | "nprobe a scaled-down beam saves next to nothing and " | ||
| 264 | "mis-routes. 0.5 (default) matches the benchmark-tuned " | ||
| 265 | "routing shape. 1.0 keeps the full beam (beam_width = nprobe) " | ||
| 266 | "at every level -- the widest and most recall-conservative " | ||
| 267 | "setting. Smaller values score fewer centroids and are faster " | ||
| 268 | "at high nprobe, with a small recall risk if a near leaf's " | ||
| 269 | "ancestor falls outside the narrowed beam.", | ||
| 270 | &prism_centroid_beam_scale, | ||
| 271 | 0.5, | ||
| 272 | 0.01, | ||
| 273 | 1.0, | ||
| 274 | PGC_USERSET, | ||
| 275 | 0, | ||
| 276 | NULL, | ||
| 277 | NULL, | ||
| 278 | NULL); | ||
| 279 | |||
| 280 | 255 | DefineCustomBoolVariable( | |
| 281 | VS_GUC_PREFIX ".recent_buffers", | ||
| 282 | "Re-pin index pages via a backend-local buffer-id cache.", | ||
| 283 | "Skips the shared buffer-mapping hash lookup (a large share of " | ||
| 284 | "warm scan CPU) by remembering each block's buffer id and " | ||
| 285 | "re-pinning it with ReadRecentBuffer. Stale ids fall back to a " | ||
| 286 | "normal read and self-heal. Costs 4 bytes per index block per " | ||
| 287 | "backend.", | ||
| 288 | &prism_recent_buffers, | ||
| 289 | true, | ||
| 290 | PGC_USERSET, | ||
| 291 | 0, | ||
| 292 | NULL, | ||
| 293 | vs_recent_buffers_assign_hook, | ||
| 294 | NULL); | ||
| 295 | |||
| 296 | 255 | DefineCustomRealVariable( | |
| 297 | VS_GUC_PREFIX ".probe_expand", | ||
| 298 | "Probe-candidate expansion factor for exact centroid re-rank.", | ||
| 299 | "Routes ceil(nprobe * expand) leaf candidates through the " | ||
| 300 | "centroid beam, re-ranks them by exact query-centroid distance " | ||
| 301 | "(the full-precision rotated centroid on each cluster's first " | ||
| 302 | "posting page), and scans only the best nprobe in that order. " | ||
| 303 | "Corrects the probe-order noise of compressed (RaBitQ) centroid " | ||
| 304 | "routing; skipped automatically for indexes with exact " | ||
| 305 | "float/half centroids. 1.0 means no expansion (identity). " | ||
| 306 | "Gains saturate around 2 (the default); the extra routed " | ||
| 307 | "candidates are capped at 256, which bounds the overhead at " | ||
| 308 | "large nprobe while retaining nearly all of the recall gain.", | ||
| 309 | &prism_probe_expand, | ||
| 310 | 2.0, | ||
| 311 | 1.0, | ||
| 312 | 16.0, | ||
| 313 | PGC_USERSET, | ||
| 314 | 0, | ||
| 315 | NULL, | ||
| 316 | vs_probe_expand_assign_hook, | ||
| 317 | NULL); | ||
| 318 | |||
| 319 | 255 | DefineCustomIntVariable( | |
| 320 | VS_GUC_PREFIX ".rerank_pool", | ||
| 321 | "Max candidates to exact-rerank per query (0 = automatic, " | ||
| 322 | "-1 = unlimited).", | ||
| 323 | "Rerank only the most promising candidates by approximate " | ||
| 324 | "distance, bounding the exact-distance heap fetches. 0 (the " | ||
| 325 | "default) caps at 3 * k * nprobe^0.15, growing to 1/8th of " | ||
| 326 | "the candidate buffer when noisy distance estimates flood " | ||
| 327 | "it; -1 reranks every threshold survivor; positive values " | ||
| 328 | "set an absolute cap. The effective cap is never below the " | ||
| 329 | "query's k, so results are never truncated.", | ||
| 330 | &prism_rerank_pool, | ||
| 331 | 0, | ||
| 332 | -1, | ||
| 333 | 1000000, | ||
| 334 | PGC_USERSET, | ||
| 335 | 0, | ||
| 336 | NULL, | ||
| 337 | vs_rerank_pool_assign_hook, | ||
| 338 | NULL); | ||
| 339 | |||
| 340 | 255 | MarkGUCPrefixReserved(VS_GUC_PREFIX); | |
| 341 | |||
| 342 | 255 | vs_cblas_pin_single_thread(); | |
| 343 | |||
| 344 | 255 | prism_relopt_kind = add_reloption_kind(); | |
| 345 | 255 | add_enum_reloption( | |
| 346 | prism_relopt_kind, | ||
| 347 | "distance_mode", | ||
| 348 | "RaBitQ distance computation mode", | ||
| 349 | distance_mode_relopt_members, | ||
| 350 | VS_DISTANCE_MODE_ASYMMETRIC, | ||
| 351 | "symmetric is faster but has larger estimation error", | ||
| 352 | NoLock); | ||
| 353 | 255 | add_int_reloption( | |
| 354 | prism_relopt_kind, | ||
| 355 | "fan_out", | ||
| 356 | "Children per tree node (2-255)", | ||
| 357 | PRISM_DEFAULT_FAN_OUT, | ||
| 358 | PRISM_MIN_FAN_OUT, | ||
| 359 | PRISM_MAX_FAN_OUT, | ||
| 360 | NoLock); | ||
| 361 | 255 | add_int_reloption( | |
| 362 | prism_relopt_kind, | ||
| 363 | "nlist", | ||
| 364 | "Number of IVF clusters (0 = auto from sqrt(rows))", | ||
| 365 | PRISM_DEFAULT_NLIST, | ||
| 366 | PRISM_MIN_NLIST, | ||
| 367 | PRISM_MAX_NLIST, | ||
| 368 | NoLock); | ||
| 369 | 255 | add_int_reloption( | |
| 370 | prism_relopt_kind, | ||
| 371 | "target_pages", | ||
| 372 | "Posting pages each list rests at (1-64)", | ||
| 373 | PRISM_DEFAULT_TARGET_PAGES, | ||
| 374 | PRISM_MIN_TARGET_PAGES, | ||
| 375 | PRISM_MAX_TARGET_PAGES, | ||
| 376 | NoLock); | ||
| 377 | 255 | add_int_reloption( | |
| 378 | prism_relopt_kind, | ||
| 379 | "kmeans_nredo", | ||
| 380 | "K-means restarts for cluster quality (1 = no restart)", | ||
| 381 | 1, | ||
| 382 | 1, | ||
| 383 | 20, | ||
| 384 | NoLock); | ||
| 385 | 255 | add_real_reloption( | |
| 386 | prism_relopt_kind, | ||
| 387 | "soar_lambda", | ||
| 388 | "SOAR replication lambda (0 = off)", | ||
| 389 | PRISM_DEFAULT_SOAR_LAMBDA, | ||
| 390 | 0.0, | ||
| 391 | 100.0, | ||
| 392 | NoLock); | ||
| 393 | 255 | add_real_reloption( | |
| 394 | prism_relopt_kind, | ||
| 395 | "boundary_epsilon", | ||
| 396 | "Boundary replication gap threshold (0 = off)", | ||
| 397 | PRISM_DEFAULT_BOUNDARY_EPSILON, | ||
| 398 | 0.0, | ||
| 399 | 100.0, | ||
| 400 | NoLock); | ||
| 401 | 255 | add_enum_reloption( | |
| 402 | prism_relopt_kind, | ||
| 403 | "centroid_compression", | ||
| 404 | "RaBitQ compression for centroid pages", | ||
| 405 | centroid_compression_relopt_members, | ||
| 406 | PRISM_CENTROID_COMPRESSION_AUTO, | ||
| 407 | "auto compresses for L2/cosine and skips inner product", | ||
| 408 | NoLock); | ||
| 409 | 255 | add_enum_reloption( | |
| 410 | prism_relopt_kind, | ||
| 411 | "fastscan", | ||
| 412 | "VPSHUFB fastscan posting page format", | ||
| 413 | fastscan_mode_relopt_members, | ||
| 414 | PRISM_FASTSCAN_MODE_AUTO, | ||
| 415 | "auto uses it up to the dimension where a group fits a page", | ||
| 416 | NoLock); | ||
| 417 | 255 | add_enum_reloption( | |
| 418 | prism_relopt_kind, | ||
| 419 | "centroid_fastscan", | ||
| 420 | "FASTSCAN-format centroid pages", | ||
| 421 | fastscan_mode_relopt_members, | ||
| 422 | PRISM_FASTSCAN_MODE_AUTO, | ||
| 423 | "auto follows the resolved centroid compression and the " | ||
| 424 | "dimension limit; on errors where either is unavailable", | ||
| 425 | NoLock); | ||
| 426 | |||
| 427 | 255 | vs_distance_init(); | |
| 428 | 255 | vs_rabitq_init_simd(); | |
| 429 | 255 | prism_explain_init(); | |
| 430 | 255 | prism_scan_bound_init(); | |
| 431 | 255 | } | |
| 432 | |||
| 433 | /* ---------------------------------------------------------------- | ||
| 434 | * Validation helpers | ||
| 435 | * ---------------------------------------------------------------- */ | ||
| 436 | |||
| 437 | void | ||
| 438 | 46857 | vs_pg_check_dim_valid(int dim) | |
| 439 | { | ||
| 440 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 46857 times.
|
46857 | if (dim < 1) |
| 441 | ✗ | ereport(ERROR, | |
| 442 | (errcode(ERRCODE_DATA_EXCEPTION), | ||
| 443 | errmsg("vec32 must have at least 1 dimension"))); | ||
| 444 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 46857 times.
|
46857 | if (dim > VEC32_MAX_DIM) |
| 445 | ✗ | ereport(ERROR, | |
| 446 | (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), | ||
| 447 | errmsg("vec32 cannot have more than %d dimensions", | ||
| 448 | VEC32_MAX_DIM))); | ||
| 449 | 46857 | } | |
| 450 | |||
| 451 | /* | ||
| 452 | * rabitq_params_generate() builds a dim x dim orthogonal matrix by | ||
| 453 | * Gram-Schmidt -- O(dim^3) in time, O(dim^2) in memory -- and is callable by | ||
| 454 | * any role, so an oversized dim is a CPU/memory denial-of-service vector (at | ||
| 455 | * the generic vector cap a single call runs for minutes at 100% CPU). Bound | ||
| 456 | * it by PRISM_INDEX_MAX_DIM: generating params for a dimension no prism index | ||
| 457 | * can hold is pointless, so the largest indexable dimension is the natural | ||
| 458 | * ceiling, and it tracks the index limit automatically. | ||
| 459 | */ | ||
| 460 | void | ||
| 461 | 9 | vs_pg_check_rabitq_params_dim_valid(int dim) | |
| 462 | { | ||
| 463 | 9 | vs_pg_check_dim_valid(dim); | |
| 464 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
|
9 | if (dim > PRISM_INDEX_MAX_DIM) |
| 465 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | ereport(ERROR, |
| 466 | (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), | ||
| 467 | errmsg("cannot generate rabitq_params for more than %d " | ||
| 468 | "dimensions", | ||
| 469 | PRISM_INDEX_MAX_DIM), | ||
| 470 | errhint("Building the transform matrix is O(dim^3); no " | ||
| 471 | "prism index supports more dimensions than this."))); | ||
| 472 | 8 | } | |
| 473 | |||
| 474 | void | ||
| 475 | 77872 | vs_pg_check_dims_match(int dim_a, int dim_b) | |
| 476 | { | ||
| 477 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 77869 times.
|
77872 | if (dim_a != dim_b) |
| 478 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | ereport(ERROR, |
| 479 | (errcode(ERRCODE_DATA_EXCEPTION), | ||
| 480 | errmsg("different vec32 dimensions %d and %d", | ||
| 481 | dim_a, | ||
| 482 | dim_b))); | ||
| 483 | 77869 | } | |
| 484 | |||
| 485 | void | ||
| 486 | 333327 | vs_pg_check_expected_dim(int actual, int expected) | |
| 487 | { | ||
| 488 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 333324 times.
|
333327 | if (expected != -1 && actual != expected) |
| 489 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | ereport(ERROR, |
| 490 | (errcode(ERRCODE_DATA_EXCEPTION), | ||
| 491 | errmsg("expected %d dimensions, not %d", expected, actual))); | ||
| 492 | 333324 | } | |
| 493 | |||
| 494 | void | ||
| 495 | 18057204 | vs_pg_check_value_finite(float val) | |
| 496 | { | ||
| 497 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 18057202 times.
|
18057204 | if (isinf(val)) |
| 498 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | ereport(ERROR, |
| 499 | (errcode(ERRCODE_DATA_EXCEPTION), | ||
| 500 | errmsg("infinite value not allowed in vec32"))); | ||
| 501 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 18057200 times.
|
18057202 | if (isnan(val)) |
| 502 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | ereport(ERROR, |
| 503 | (errcode(ERRCODE_DATA_EXCEPTION), | ||
| 504 | errmsg("NaN value not allowed in vec32"))); | ||
| 505 | 18057200 | } | |
| 506 | |||
| 507 | /* ---------------------------------------------------------------- | ||
| 508 | * Build identity | ||
| 509 | * ---------------------------------------------------------------- */ | ||
| 510 | |||
| 511 | /* | ||
| 512 | * Return the git commit the extension was built from. The string is | ||
| 513 | * baked into the binary via vcs_tag at build time (see meson.build). | ||
| 514 | * Rekall and other tooling use this to identify a build for | ||
| 515 | * benchmark reports. | ||
| 516 | */ | ||
| 517 | 9 | PG_FUNCTION_INFO_V1(vs_git_commit); | |
| 518 | |||
| 519 | Datum | ||
| 520 | 2 | vs_git_commit(PG_FUNCTION_ARGS) | |
| 521 | { | ||
| 522 | 2 | PG_RETURN_TEXT_P(cstring_to_text(VS_GIT_COMMIT)); | |
| 523 | } | ||
| 524 | |||
| 525 | /* | ||
| 526 | * Return the extension version the binary was built as. Comes from | ||
| 527 | * meson.build via vs_config.h, the single source of truth for the | ||
| 528 | * version string. | ||
| 529 | */ | ||
| 530 | 19 | PG_FUNCTION_INFO_V1(vs_extension_version); | |
| 531 | |||
| 532 | Datum | ||
| 533 | 12 | vs_extension_version(PG_FUNCTION_ARGS) | |
| 534 | { | ||
| 535 | 12 | PG_RETURN_TEXT_P(cstring_to_text(VS_VERSION)); | |
| 536 | } | ||
| 537 | |||
| 538 | /* ---------------------------------------------------------------- | ||
| 539 | * Metric identifier support functions (FUNCTION 2 in opclasses) | ||
| 540 | * ---------------------------------------------------------------- */ | ||
| 541 | |||
| 542 | 45 | PG_FUNCTION_INFO_V1(prism_metric_l2); | |
| 543 | |||
| 544 | Datum | ||
| 545 | 180 | prism_metric_l2(PG_FUNCTION_ARGS) | |
| 546 | { | ||
| 547 | 180 | PG_RETURN_INT32(DISTANCE_L2); | |
| 548 | } | ||
| 549 | |||
| 550 | 9 | PG_FUNCTION_INFO_V1(prism_metric_ip); | |
| 551 | |||
| 552 | Datum | ||
| 553 | 3 | prism_metric_ip(PG_FUNCTION_ARGS) | |
| 554 | { | ||
| 555 | 3 | PG_RETURN_INT32(DISTANCE_INNER_PRODUCT); | |
| 556 | } | ||
| 557 | |||
| 558 | 13 | PG_FUNCTION_INFO_V1(prism_metric_cosine); | |
| 559 | |||
| 560 | Datum | ||
| 561 | 13 | prism_metric_cosine(PG_FUNCTION_ARGS) | |
| 562 | { | ||
| 563 | 13 | PG_RETURN_INT32(DISTANCE_COSINE); | |
| 564 | } | ||
| 565 |