| 1 | /*------------------------------------------------------------------------- |
| 2 | * |
| 3 | * ginscan.c |
| 4 | * routines to manage scans of inverted index relations |
| 5 | * |
| 6 | * |
| 7 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
| 8 | * Portions Copyright (c) 1994, Regents of the University of California |
| 9 | * |
| 10 | * IDENTIFICATION |
| 11 | * src/backend/access/gin/ginscan.c |
| 12 | *------------------------------------------------------------------------- |
| 13 | */ |
| 14 | |
| 15 | #include "postgres.h" |
| 16 | |
| 17 | #include "access/gin_private.h" |
| 18 | #include "access/relscan.h" |
| 19 | #include "pgstat.h" |
| 20 | #include "utils/memutils.h" |
| 21 | #include "utils/rel.h" |
| 22 | |
| 23 | |
| 24 | IndexScanDesc |
| 25 | ginbeginscan(Relation rel, int nkeys, int norderbys) |
| 26 | { |
| 27 | IndexScanDesc scan; |
| 28 | GinScanOpaque so; |
| 29 | |
| 30 | /* no order by operators allowed */ |
| 31 | Assert(norderbys == 0); |
| 32 | |
| 33 | scan = RelationGetIndexScan(rel, nkeys, norderbys); |
| 34 | |
| 35 | /* allocate private workspace */ |
| 36 | so = (GinScanOpaque) palloc(sizeof(GinScanOpaqueData)); |
| 37 | so->keys = NULL; |
| 38 | so->nkeys = 0; |
| 39 | so->tempCtx = AllocSetContextCreate(CurrentMemoryContext, |
| 40 | "Gin scan temporary context" , |
| 41 | ALLOCSET_DEFAULT_SIZES); |
| 42 | so->keyCtx = AllocSetContextCreate(CurrentMemoryContext, |
| 43 | "Gin scan key context" , |
| 44 | ALLOCSET_DEFAULT_SIZES); |
| 45 | initGinState(&so->ginstate, scan->indexRelation); |
| 46 | |
| 47 | scan->opaque = so; |
| 48 | |
| 49 | return scan; |
| 50 | } |
| 51 | |
| 52 | /* |
| 53 | * Create a new GinScanEntry, unless an equivalent one already exists, |
| 54 | * in which case just return it |
| 55 | */ |
| 56 | static GinScanEntry |
| 57 | ginFillScanEntry(GinScanOpaque so, OffsetNumber attnum, |
| 58 | StrategyNumber strategy, int32 searchMode, |
| 59 | Datum queryKey, GinNullCategory queryCategory, |
| 60 | bool isPartialMatch, Pointer ) |
| 61 | { |
| 62 | GinState *ginstate = &so->ginstate; |
| 63 | GinScanEntry scanEntry; |
| 64 | uint32 i; |
| 65 | |
| 66 | /* |
| 67 | * Look for an existing equivalent entry. |
| 68 | * |
| 69 | * Entries with non-null extra_data are never considered identical, since |
| 70 | * we can't know exactly what the opclass might be doing with that. |
| 71 | */ |
| 72 | if (extra_data == NULL) |
| 73 | { |
| 74 | for (i = 0; i < so->totalentries; i++) |
| 75 | { |
| 76 | GinScanEntry prevEntry = so->entries[i]; |
| 77 | |
| 78 | if (prevEntry->extra_data == NULL && |
| 79 | prevEntry->isPartialMatch == isPartialMatch && |
| 80 | prevEntry->strategy == strategy && |
| 81 | prevEntry->searchMode == searchMode && |
| 82 | prevEntry->attnum == attnum && |
| 83 | ginCompareEntries(ginstate, attnum, |
| 84 | prevEntry->queryKey, |
| 85 | prevEntry->queryCategory, |
| 86 | queryKey, |
| 87 | queryCategory) == 0) |
| 88 | { |
| 89 | /* Successful match */ |
| 90 | return prevEntry; |
| 91 | } |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | /* Nope, create a new entry */ |
| 96 | scanEntry = (GinScanEntry) palloc(sizeof(GinScanEntryData)); |
| 97 | scanEntry->queryKey = queryKey; |
| 98 | scanEntry->queryCategory = queryCategory; |
| 99 | scanEntry->isPartialMatch = isPartialMatch; |
| 100 | scanEntry->extra_data = extra_data; |
| 101 | scanEntry->strategy = strategy; |
| 102 | scanEntry->searchMode = searchMode; |
| 103 | scanEntry->attnum = attnum; |
| 104 | |
| 105 | scanEntry->buffer = InvalidBuffer; |
| 106 | ItemPointerSetMin(&scanEntry->curItem); |
| 107 | scanEntry->matchBitmap = NULL; |
| 108 | scanEntry->matchIterator = NULL; |
| 109 | scanEntry->matchResult = NULL; |
| 110 | scanEntry->list = NULL; |
| 111 | scanEntry->nlist = 0; |
| 112 | scanEntry->offset = InvalidOffsetNumber; |
| 113 | scanEntry->isFinished = false; |
| 114 | scanEntry->reduceResult = false; |
| 115 | |
| 116 | /* Add it to so's array */ |
| 117 | if (so->totalentries >= so->allocentries) |
| 118 | { |
| 119 | so->allocentries *= 2; |
| 120 | so->entries = (GinScanEntry *) |
| 121 | repalloc(so->entries, so->allocentries * sizeof(GinScanEntry)); |
| 122 | } |
| 123 | so->entries[so->totalentries++] = scanEntry; |
| 124 | |
| 125 | return scanEntry; |
| 126 | } |
| 127 | |
| 128 | /* |
| 129 | * Initialize the next GinScanKey using the output from the extractQueryFn |
| 130 | */ |
| 131 | static void |
| 132 | ginFillScanKey(GinScanOpaque so, OffsetNumber attnum, |
| 133 | StrategyNumber strategy, int32 searchMode, |
| 134 | Datum query, uint32 nQueryValues, |
| 135 | Datum *queryValues, GinNullCategory *queryCategories, |
| 136 | bool *partial_matches, Pointer *) |
| 137 | { |
| 138 | GinScanKey key = &(so->keys[so->nkeys++]); |
| 139 | GinState *ginstate = &so->ginstate; |
| 140 | uint32 nUserQueryValues = nQueryValues; |
| 141 | uint32 i; |
| 142 | |
| 143 | /* Non-default search modes add one "hidden" entry to each key */ |
| 144 | if (searchMode != GIN_SEARCH_MODE_DEFAULT) |
| 145 | nQueryValues++; |
| 146 | key->nentries = nQueryValues; |
| 147 | key->nuserentries = nUserQueryValues; |
| 148 | |
| 149 | key->scanEntry = (GinScanEntry *) palloc(sizeof(GinScanEntry) * nQueryValues); |
| 150 | key->entryRes = (GinTernaryValue *) palloc0(sizeof(GinTernaryValue) * nQueryValues); |
| 151 | |
| 152 | key->query = query; |
| 153 | key->queryValues = queryValues; |
| 154 | key->queryCategories = queryCategories; |
| 155 | key->extra_data = extra_data; |
| 156 | key->strategy = strategy; |
| 157 | key->searchMode = searchMode; |
| 158 | key->attnum = attnum; |
| 159 | |
| 160 | ItemPointerSetMin(&key->curItem); |
| 161 | key->curItemMatches = false; |
| 162 | key->recheckCurItem = false; |
| 163 | key->isFinished = false; |
| 164 | key->nrequired = 0; |
| 165 | key->nadditional = 0; |
| 166 | key->requiredEntries = NULL; |
| 167 | key->additionalEntries = NULL; |
| 168 | |
| 169 | ginInitConsistentFunction(ginstate, key); |
| 170 | |
| 171 | for (i = 0; i < nQueryValues; i++) |
| 172 | { |
| 173 | Datum queryKey; |
| 174 | GinNullCategory queryCategory; |
| 175 | bool isPartialMatch; |
| 176 | Pointer ; |
| 177 | |
| 178 | if (i < nUserQueryValues) |
| 179 | { |
| 180 | /* set up normal entry using extractQueryFn's outputs */ |
| 181 | queryKey = queryValues[i]; |
| 182 | queryCategory = queryCategories[i]; |
| 183 | isPartialMatch = |
| 184 | (ginstate->canPartialMatch[attnum - 1] && partial_matches) |
| 185 | ? partial_matches[i] : false; |
| 186 | this_extra = (extra_data) ? extra_data[i] : NULL; |
| 187 | } |
| 188 | else |
| 189 | { |
| 190 | /* set up hidden entry */ |
| 191 | queryKey = (Datum) 0; |
| 192 | switch (searchMode) |
| 193 | { |
| 194 | case GIN_SEARCH_MODE_INCLUDE_EMPTY: |
| 195 | queryCategory = GIN_CAT_EMPTY_ITEM; |
| 196 | break; |
| 197 | case GIN_SEARCH_MODE_ALL: |
| 198 | queryCategory = GIN_CAT_EMPTY_QUERY; |
| 199 | break; |
| 200 | case GIN_SEARCH_MODE_EVERYTHING: |
| 201 | queryCategory = GIN_CAT_EMPTY_QUERY; |
| 202 | break; |
| 203 | default: |
| 204 | elog(ERROR, "unexpected searchMode: %d" , searchMode); |
| 205 | queryCategory = 0; /* keep compiler quiet */ |
| 206 | break; |
| 207 | } |
| 208 | isPartialMatch = false; |
| 209 | this_extra = NULL; |
| 210 | |
| 211 | /* |
| 212 | * We set the strategy to a fixed value so that ginFillScanEntry |
| 213 | * can combine these entries for different scan keys. This is |
| 214 | * safe because the strategy value in the entry struct is only |
| 215 | * used for partial-match cases. It's OK to overwrite our local |
| 216 | * variable here because this is the last loop iteration. |
| 217 | */ |
| 218 | strategy = InvalidStrategy; |
| 219 | } |
| 220 | |
| 221 | key->scanEntry[i] = ginFillScanEntry(so, attnum, |
| 222 | strategy, searchMode, |
| 223 | queryKey, queryCategory, |
| 224 | isPartialMatch, this_extra); |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | /* |
| 229 | * Release current scan keys, if any. |
| 230 | */ |
| 231 | void |
| 232 | ginFreeScanKeys(GinScanOpaque so) |
| 233 | { |
| 234 | uint32 i; |
| 235 | |
| 236 | if (so->keys == NULL) |
| 237 | return; |
| 238 | |
| 239 | for (i = 0; i < so->totalentries; i++) |
| 240 | { |
| 241 | GinScanEntry entry = so->entries[i]; |
| 242 | |
| 243 | if (entry->buffer != InvalidBuffer) |
| 244 | ReleaseBuffer(entry->buffer); |
| 245 | if (entry->list) |
| 246 | pfree(entry->list); |
| 247 | if (entry->matchIterator) |
| 248 | tbm_end_iterate(entry->matchIterator); |
| 249 | if (entry->matchBitmap) |
| 250 | tbm_free(entry->matchBitmap); |
| 251 | } |
| 252 | |
| 253 | MemoryContextResetAndDeleteChildren(so->keyCtx); |
| 254 | |
| 255 | so->keys = NULL; |
| 256 | so->nkeys = 0; |
| 257 | so->entries = NULL; |
| 258 | so->totalentries = 0; |
| 259 | } |
| 260 | |
| 261 | void |
| 262 | ginNewScanKey(IndexScanDesc scan) |
| 263 | { |
| 264 | ScanKey scankey = scan->keyData; |
| 265 | GinScanOpaque so = (GinScanOpaque) scan->opaque; |
| 266 | int i; |
| 267 | bool hasNullQuery = false; |
| 268 | MemoryContext oldCtx; |
| 269 | |
| 270 | /* |
| 271 | * Allocate all the scan key information in the key context. (If |
| 272 | * extractQuery leaks anything there, it won't be reset until the end of |
| 273 | * scan or rescan, but that's OK.) |
| 274 | */ |
| 275 | oldCtx = MemoryContextSwitchTo(so->keyCtx); |
| 276 | |
| 277 | /* if no scan keys provided, allocate extra EVERYTHING GinScanKey */ |
| 278 | so->keys = (GinScanKey) |
| 279 | palloc(Max(scan->numberOfKeys, 1) * sizeof(GinScanKeyData)); |
| 280 | so->nkeys = 0; |
| 281 | |
| 282 | /* initialize expansible array of GinScanEntry pointers */ |
| 283 | so->totalentries = 0; |
| 284 | so->allocentries = 32; |
| 285 | so->entries = (GinScanEntry *) |
| 286 | palloc(so->allocentries * sizeof(GinScanEntry)); |
| 287 | |
| 288 | so->isVoidRes = false; |
| 289 | |
| 290 | for (i = 0; i < scan->numberOfKeys; i++) |
| 291 | { |
| 292 | ScanKey skey = &scankey[i]; |
| 293 | Datum *queryValues; |
| 294 | int32 nQueryValues = 0; |
| 295 | bool *partial_matches = NULL; |
| 296 | Pointer * = NULL; |
| 297 | bool *nullFlags = NULL; |
| 298 | GinNullCategory *categories; |
| 299 | int32 searchMode = GIN_SEARCH_MODE_DEFAULT; |
| 300 | |
| 301 | /* |
| 302 | * We assume that GIN-indexable operators are strict, so a null query |
| 303 | * argument means an unsatisfiable query. |
| 304 | */ |
| 305 | if (skey->sk_flags & SK_ISNULL) |
| 306 | { |
| 307 | so->isVoidRes = true; |
| 308 | break; |
| 309 | } |
| 310 | |
| 311 | /* OK to call the extractQueryFn */ |
| 312 | queryValues = (Datum *) |
| 313 | DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1], |
| 314 | so->ginstate.supportCollation[skey->sk_attno - 1], |
| 315 | skey->sk_argument, |
| 316 | PointerGetDatum(&nQueryValues), |
| 317 | UInt16GetDatum(skey->sk_strategy), |
| 318 | PointerGetDatum(&partial_matches), |
| 319 | PointerGetDatum(&extra_data), |
| 320 | PointerGetDatum(&nullFlags), |
| 321 | PointerGetDatum(&searchMode))); |
| 322 | |
| 323 | /* |
| 324 | * If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note |
| 325 | * in particular we don't allow extractQueryFn to select |
| 326 | * GIN_SEARCH_MODE_EVERYTHING. |
| 327 | */ |
| 328 | if (searchMode < GIN_SEARCH_MODE_DEFAULT || |
| 329 | searchMode > GIN_SEARCH_MODE_ALL) |
| 330 | searchMode = GIN_SEARCH_MODE_ALL; |
| 331 | |
| 332 | /* Non-default modes require the index to have placeholders */ |
| 333 | if (searchMode != GIN_SEARCH_MODE_DEFAULT) |
| 334 | hasNullQuery = true; |
| 335 | |
| 336 | /* |
| 337 | * In default mode, no keys means an unsatisfiable query. |
| 338 | */ |
| 339 | if (queryValues == NULL || nQueryValues <= 0) |
| 340 | { |
| 341 | if (searchMode == GIN_SEARCH_MODE_DEFAULT) |
| 342 | { |
| 343 | so->isVoidRes = true; |
| 344 | break; |
| 345 | } |
| 346 | nQueryValues = 0; /* ensure sane value */ |
| 347 | } |
| 348 | |
| 349 | /* |
| 350 | * Create GinNullCategory representation. If the extractQueryFn |
| 351 | * didn't create a nullFlags array, we assume everything is non-null. |
| 352 | * While at it, detect whether any null keys are present. |
| 353 | */ |
| 354 | categories = (GinNullCategory *) palloc0(nQueryValues * sizeof(GinNullCategory)); |
| 355 | if (nullFlags) |
| 356 | { |
| 357 | int32 j; |
| 358 | |
| 359 | for (j = 0; j < nQueryValues; j++) |
| 360 | { |
| 361 | if (nullFlags[j]) |
| 362 | { |
| 363 | categories[j] = GIN_CAT_NULL_KEY; |
| 364 | hasNullQuery = true; |
| 365 | } |
| 366 | } |
| 367 | } |
| 368 | |
| 369 | ginFillScanKey(so, skey->sk_attno, |
| 370 | skey->sk_strategy, searchMode, |
| 371 | skey->sk_argument, nQueryValues, |
| 372 | queryValues, categories, |
| 373 | partial_matches, extra_data); |
| 374 | } |
| 375 | |
| 376 | /* |
| 377 | * If there are no regular scan keys, generate an EVERYTHING scankey to |
| 378 | * drive a full-index scan. |
| 379 | */ |
| 380 | if (so->nkeys == 0 && !so->isVoidRes) |
| 381 | { |
| 382 | hasNullQuery = true; |
| 383 | ginFillScanKey(so, FirstOffsetNumber, |
| 384 | InvalidStrategy, GIN_SEARCH_MODE_EVERYTHING, |
| 385 | (Datum) 0, 0, |
| 386 | NULL, NULL, NULL, NULL); |
| 387 | } |
| 388 | |
| 389 | /* |
| 390 | * If the index is version 0, it may be missing null and placeholder |
| 391 | * entries, which would render searches for nulls and full-index scans |
| 392 | * unreliable. Throw an error if so. |
| 393 | */ |
| 394 | if (hasNullQuery && !so->isVoidRes) |
| 395 | { |
| 396 | GinStatsData ginStats; |
| 397 | |
| 398 | ginGetStats(scan->indexRelation, &ginStats); |
| 399 | if (ginStats.ginVersion < 1) |
| 400 | ereport(ERROR, |
| 401 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 402 | errmsg("old GIN indexes do not support whole-index scans nor searches for nulls" ), |
| 403 | errhint("To fix this, do REINDEX INDEX \"%s\"." , |
| 404 | RelationGetRelationName(scan->indexRelation)))); |
| 405 | } |
| 406 | |
| 407 | MemoryContextSwitchTo(oldCtx); |
| 408 | |
| 409 | pgstat_count_index_scan(scan->indexRelation); |
| 410 | } |
| 411 | |
| 412 | void |
| 413 | ginrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys, |
| 414 | ScanKey orderbys, int norderbys) |
| 415 | { |
| 416 | GinScanOpaque so = (GinScanOpaque) scan->opaque; |
| 417 | |
| 418 | ginFreeScanKeys(so); |
| 419 | |
| 420 | if (scankey && scan->numberOfKeys > 0) |
| 421 | { |
| 422 | memmove(scan->keyData, scankey, |
| 423 | scan->numberOfKeys * sizeof(ScanKeyData)); |
| 424 | } |
| 425 | } |
| 426 | |
| 427 | |
| 428 | void |
| 429 | ginendscan(IndexScanDesc scan) |
| 430 | { |
| 431 | GinScanOpaque so = (GinScanOpaque) scan->opaque; |
| 432 | |
| 433 | ginFreeScanKeys(so); |
| 434 | |
| 435 | MemoryContextDelete(so->tempCtx); |
| 436 | MemoryContextDelete(so->keyCtx); |
| 437 | |
| 438 | pfree(so); |
| 439 | } |
| 440 | |