Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * gistutil.c
4 : : * utilities routines for the postgres GiST index access method.
5 : : *
6 : : *
7 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
8 : : * Portions Copyright (c) 1994, Regents of the University of California
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/access/gist/gistutil.c
12 : : *-------------------------------------------------------------------------
13 : : */
14 : : #include "postgres.h"
15 : :
16 : : #include <math.h>
17 : :
18 : : #include "access/gist_private.h"
19 : : #include "access/htup_details.h"
20 : : #include "access/reloptions.h"
21 : : #include "common/pg_prng.h"
22 : : #include "storage/indexfsm.h"
23 : : #include "utils/float.h"
24 : : #include "utils/fmgrprotos.h"
25 : : #include "utils/lsyscache.h"
26 : : #include "utils/rel.h"
27 : : #include "utils/snapmgr.h"
28 : : #include "utils/syscache.h"
29 : :
30 : : /*
31 : : * Write itup vector to page, has no control of free space.
32 : : */
33 : : void
5785 heikki.linnakangas@i 34 :CBC 580444 : gistfillbuffer(Page page, IndexTuple *itup, int len, OffsetNumber off)
35 : : {
36 : : int i;
37 : :
6779 bruce@momjian.us 38 [ + + ]: 580444 : if (off == InvalidOffsetNumber)
39 [ + + ]: 1157509 : off = (PageIsEmpty(page)) ? FirstOffsetNumber :
6865 teodor@sigaev.ru 40 : 577980 : OffsetNumberNext(PageGetMaxOffsetNumber(page));
41 : :
6879 42 [ + + ]: 1248602 : for (i = 0; i < len; i++)
43 : : {
5421 bruce@momjian.us 44 : 668158 : Size sz = IndexTupleSize(itup[i]);
45 : : OffsetNumber l;
46 : :
5785 heikki.linnakangas@i 47 : 668158 : l = PageAddItem(page, (Item) itup[i], sz, off, false, false);
6879 teodor@sigaev.ru 48 [ - + ]: 668158 : if (l == InvalidOffsetNumber)
5785 heikki.linnakangas@i 49 [ # # ]:UBC 0 : elog(ERROR, "failed to add item to GiST index page, item %d out of %d, size %d bytes",
50 : : i, len, (int) sz);
6879 teodor@sigaev.ru 51 :CBC 668158 : off++;
52 : : }
53 : 580444 : }
54 : :
55 : : /*
56 : : * Check space for itup vector on page
57 : : */
58 : : bool
6496 bruce@momjian.us 59 : 859502 : gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace)
60 : : {
6402 61 : 859502 : unsigned int size = freespace,
62 : 859502 : deleted = 0;
63 : : int i;
64 : :
6879 teodor@sigaev.ru 65 [ + + ]: 1731489 : for (i = 0; i < len; i++)
66 : 871987 : size += IndexTupleSize(itvec[i]) + sizeof(ItemIdData);
67 : :
6402 bruce@momjian.us 68 [ + + ]: 859502 : if (todelete != InvalidOffsetNumber)
69 : : {
70 : 378626 : IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, todelete));
71 : :
6549 teodor@sigaev.ru 72 : 378626 : deleted = IndexTupleSize(itup) + sizeof(ItemIdData);
73 : : }
74 : :
75 : 859502 : return (PageGetFreeSpace(page) + deleted < size);
76 : : }
77 : :
78 : : bool
6402 bruce@momjian.us 79 : 26752 : gistfitpage(IndexTuple *itvec, int len)
80 : : {
81 : : int i;
82 : 26752 : Size size = 0;
83 : :
84 [ + + ]: 1163508 : for (i = 0; i < len; i++)
6549 teodor@sigaev.ru 85 : 1136756 : size += IndexTupleSize(itvec[i]) + sizeof(ItemIdData);
86 : :
87 : : /* TODO: Consider fillfactor */
88 : 26752 : return (size <= GiSTPageSize);
89 : : }
90 : :
91 : : /*
92 : : * Read buffer into itup vector
93 : : */
94 : : IndexTuple *
6542 95 : 13231 : gistextractpage(Page page, int *len /* out */ )
96 : : {
97 : : OffsetNumber i,
98 : : maxoff;
99 : : IndexTuple *itvec;
100 : :
101 : 13231 : maxoff = PageGetMaxOffsetNumber(page);
6879 102 : 13231 : *len = maxoff;
103 : 13231 : itvec = palloc(sizeof(IndexTuple) * maxoff);
104 [ + + ]: 1010681 : for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
6542 105 : 997450 : itvec[i - FirstOffsetNumber] = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
106 : :
6879 107 : 13231 : return itvec;
108 : : }
109 : :
110 : : /*
111 : : * join two vectors into one
112 : : */
113 : : IndexTuple *
114 : 13089 : gistjoinvector(IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen)
115 : : {
432 peter@eisentraut.org 116 : 13089 : itvec = (IndexTuple *) repalloc(itvec, sizeof(IndexTuple) * ((*len) + addlen));
6879 teodor@sigaev.ru 117 : 13089 : memmove(&itvec[*len], additvec, sizeof(IndexTuple) * addlen);
118 : 13089 : *len += addlen;
119 : 13089 : return itvec;
120 : : }
121 : :
122 : : /*
123 : : * make plain IndexTuple vector
124 : : */
125 : :
126 : : IndexTupleData *
6402 bruce@momjian.us 127 : 26411 : gistfillitupvec(IndexTuple *vec, int veclen, int *memlen)
128 : : {
129 : : char *ptr,
130 : : *ret;
131 : : int i;
132 : :
133 : 26411 : *memlen = 0;
134 : :
6540 teodor@sigaev.ru 135 [ + + ]: 1036881 : for (i = 0; i < veclen; i++)
136 : 1010470 : *memlen += IndexTupleSize(vec[i]);
137 : :
138 : 26411 : ptr = ret = palloc(*memlen);
139 : :
6402 bruce@momjian.us 140 [ + + ]: 1036881 : for (i = 0; i < veclen; i++)
141 : : {
6540 teodor@sigaev.ru 142 : 1010470 : memcpy(ptr, vec[i], IndexTupleSize(vec[i]));
143 : 1010470 : ptr += IndexTupleSize(vec[i]);
144 : : }
145 : :
6402 bruce@momjian.us 146 : 26411 : return (IndexTupleData *) ret;
147 : : }
148 : :
149 : : /*
150 : : * Make unions of keys in IndexTuple vector (one union datum per index column).
151 : : * Union Datums are returned into the attr/isnull arrays.
152 : : * Resulting Datums aren't compressed.
153 : : */
154 : : void
4084 tgl@sss.pgh.pa.us 155 : 2941 : gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
156 : : Datum *attr, bool *isnull)
157 : : {
158 : : int i;
159 : : GistEntryVector *evec;
160 : : int attrsize;
161 : :
6402 bruce@momjian.us 162 : 2941 : evec = (GistEntryVector *) palloc((len + 2) * sizeof(GISTENTRY) + GEVHDRSZ);
163 : :
1862 akorotkov@postgresql 164 [ + + ]: 8450 : for (i = 0; i < giststate->nonLeafTupdesc->natts; i++)
165 : : {
166 : : int j;
167 : :
168 : : /* Collect non-null datums for this column */
6535 teodor@sigaev.ru 169 : 5509 : evec->n = 0;
6402 bruce@momjian.us 170 [ + + ]: 269797 : for (j = 0; j < len; j++)
171 : : {
172 : : Datum datum;
173 : : bool IsNull;
174 : :
1862 akorotkov@postgresql 175 : 264288 : datum = index_getattr(itvec[j], i + 1, giststate->leafTupdesc,
176 : : &IsNull);
6879 teodor@sigaev.ru 177 [ + + ]: 264288 : if (IsNull)
178 : 961 : continue;
179 : :
180 : 263327 : gistdentryinit(giststate, i,
6535 181 : 263327 : evec->vector + evec->n,
182 : : datum,
183 : : NULL, NULL, (OffsetNumber) 0,
184 : : false, IsNull);
185 : 263327 : evec->n++;
186 : : }
187 : :
188 : : /* If this column was all NULLs, the union is NULL */
6402 bruce@momjian.us 189 [ + + ]: 5509 : if (evec->n == 0)
190 : : {
6879 teodor@sigaev.ru 191 : 187 : attr[i] = (Datum) 0;
2433 peter_e@gmx.net 192 : 187 : isnull[i] = true;
193 : : }
194 : : else
195 : : {
6402 bruce@momjian.us 196 [ + + ]: 5322 : if (evec->n == 1)
197 : : {
198 : : /* unionFn may expect at least two inputs */
6879 teodor@sigaev.ru 199 : 4 : evec->n = 2;
6535 200 : 4 : evec->vector[1] = evec->vector[0];
201 : : }
202 : :
203 : : /* Make union and store in attr array */
4741 tgl@sss.pgh.pa.us 204 : 5322 : attr[i] = FunctionCall2Coll(&giststate->unionFn[i],
205 : : giststate->supportCollation[i],
206 : : PointerGetDatum(evec),
207 : : PointerGetDatum(&attrsize));
208 : :
2433 peter_e@gmx.net 209 : 5322 : isnull[i] = false;
210 : : }
211 : : }
6535 teodor@sigaev.ru 212 : 2941 : }
213 : :
214 : : /*
215 : : * Return an IndexTuple containing the result of applying the "union"
216 : : * method to the specified IndexTuple vector.
217 : : */
218 : : IndexTuple
219 : 3 : gistunion(Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate)
220 : : {
221 : : Datum attr[INDEX_MAX_KEYS];
222 : : bool isnull[INDEX_MAX_KEYS];
223 : :
4084 tgl@sss.pgh.pa.us 224 : 3 : gistMakeUnionItVec(giststate, itvec, len, attr, isnull);
225 : :
226 : 3 : return gistFormTuple(giststate, r, attr, isnull, false);
227 : : }
228 : :
229 : : /*
230 : : * makes union of two key
231 : : */
232 : : void
6402 bruce@momjian.us 233 : 730951 : gistMakeUnionKey(GISTSTATE *giststate, int attno,
234 : : GISTENTRY *entry1, bool isnull1,
235 : : GISTENTRY *entry2, bool isnull2,
236 : : Datum *dst, bool *dstisnull)
237 : : {
238 : : /* we need a GistEntryVector with room for exactly 2 elements */
239 : : union
240 : : {
241 : : GistEntryVector gev;
242 : : char padding[2 * sizeof(GISTENTRY) + GEVHDRSZ];
243 : : } storage;
4084 tgl@sss.pgh.pa.us 244 : 730951 : GistEntryVector *evec = &storage.gev;
245 : : int dstsize;
246 : :
6535 teodor@sigaev.ru 247 : 730951 : evec->n = 2;
248 : :
6402 bruce@momjian.us 249 [ + + + - ]: 730951 : if (isnull1 && isnull2)
250 : : {
2433 peter_e@gmx.net 251 : 7804 : *dstisnull = true;
6402 bruce@momjian.us 252 : 7804 : *dst = (Datum) 0;
253 : : }
254 : : else
255 : : {
2433 peter_e@gmx.net 256 [ + - + + ]: 723147 : if (isnull1 == false && isnull2 == false)
257 : : {
6535 teodor@sigaev.ru 258 : 721411 : evec->vector[0] = *entry1;
259 : 721411 : evec->vector[1] = *entry2;
260 : : }
2433 peter_e@gmx.net 261 [ + - ]: 1736 : else if (isnull1 == false)
262 : : {
6535 teodor@sigaev.ru 263 : 1736 : evec->vector[0] = *entry1;
264 : 1736 : evec->vector[1] = *entry1;
265 : : }
266 : : else
267 : : {
6535 teodor@sigaev.ru 268 :UBC 0 : evec->vector[0] = *entry2;
269 : 0 : evec->vector[1] = *entry2;
270 : : }
271 : :
2433 peter_e@gmx.net 272 :CBC 723147 : *dstisnull = false;
4741 tgl@sss.pgh.pa.us 273 : 723147 : *dst = FunctionCall2Coll(&giststate->unionFn[attno],
274 : : giststate->supportCollation[attno],
275 : : PointerGetDatum(evec),
276 : : PointerGetDatum(&dstsize));
277 : : }
6535 teodor@sigaev.ru 278 : 730951 : }
279 : :
280 : : bool
6402 bruce@momjian.us 281 : 631197 : gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
282 : : {
565 peter@eisentraut.org 283 : 631197 : bool result = false; /* silence compiler warning */
284 : :
4741 tgl@sss.pgh.pa.us 285 : 631197 : FunctionCall3Coll(&giststate->equalFn[attno],
286 : : giststate->supportCollation[attno],
287 : : a, b,
288 : : PointerGetDatum(&result));
6530 teodor@sigaev.ru 289 : 631197 : return result;
290 : : }
291 : :
292 : : /*
293 : : * Decompress all keys in tuple
294 : : */
295 : : void
6500 296 : 1926432 : gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p,
297 : : OffsetNumber o, GISTENTRY *attdata, bool *isnull)
298 : : {
299 : : int i;
300 : :
1862 akorotkov@postgresql 301 [ + + ]: 4134159 : for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
302 : : {
303 : : Datum datum;
304 : :
305 : 2207727 : datum = index_getattr(tuple, i + 1, giststate->leafTupdesc, &isnull[i]);
6500 teodor@sigaev.ru 306 : 2207727 : gistdentryinit(giststate, i, &attdata[i],
307 : : datum, r, p, o,
2433 peter_e@gmx.net 308 : 2207727 : false, isnull[i]);
309 : : }
6500 teodor@sigaev.ru 310 : 1926432 : }
311 : :
312 : : /*
313 : : * Forms union of oldtup and addtup, if union == oldtup then return NULL
314 : : */
315 : : IndexTuple
6879 316 : 637184 : gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
317 : : {
2433 peter_e@gmx.net 318 : 637184 : bool neednew = false;
319 : : GISTENTRY oldentries[INDEX_MAX_KEYS],
320 : : addentries[INDEX_MAX_KEYS];
321 : : bool oldisnull[INDEX_MAX_KEYS],
322 : : addisnull[INDEX_MAX_KEYS];
323 : : Datum attr[INDEX_MAX_KEYS];
324 : : bool isnull[INDEX_MAX_KEYS];
6879 teodor@sigaev.ru 325 : 637184 : IndexTuple newtup = NULL;
326 : : int i;
327 : :
328 : 637184 : gistDeCompressAtt(giststate, r, oldtup, NULL,
329 : : (OffsetNumber) 0, oldentries, oldisnull);
330 : :
331 : 637184 : gistDeCompressAtt(giststate, r, addtup, NULL,
332 : : (OffsetNumber) 0, addentries, addisnull);
333 : :
1862 akorotkov@postgresql 334 [ + + ]: 1368133 : for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
335 : : {
6402 bruce@momjian.us 336 : 730949 : gistMakeUnionKey(giststate, i,
337 : 730949 : oldentries + i, oldisnull[i],
338 : 730949 : addentries + i, addisnull[i],
4084 tgl@sss.pgh.pa.us 339 : 730949 : attr + i, isnull + i);
340 : :
6402 bruce@momjian.us 341 [ + + ]: 730949 : if (neednew)
342 : : /* we already need new key, so we can skip check */
6535 teodor@sigaev.ru 343 : 91494 : continue;
344 : :
4084 tgl@sss.pgh.pa.us 345 [ + + ]: 639455 : if (isnull[i])
346 : : /* union of key may be NULL if and only if both keys are NULL */
6535 teodor@sigaev.ru 347 : 7804 : continue;
348 : :
6402 bruce@momjian.us 349 [ + + ]: 631651 : if (!addisnull[i])
350 : : {
4084 tgl@sss.pgh.pa.us 351 [ + - ]: 629915 : if (oldisnull[i] ||
352 [ + + ]: 629915 : !gistKeyIsEQ(giststate, i, oldentries[i].key, attr[i]))
6535 teodor@sigaev.ru 353 : 381009 : neednew = true;
354 : : }
355 : : }
356 : :
6879 357 [ + + ]: 637184 : if (neednew)
358 : : {
359 : : /* need to update key */
4084 tgl@sss.pgh.pa.us 360 : 381009 : newtup = gistFormTuple(giststate, r, attr, isnull, false);
6879 teodor@sigaev.ru 361 : 381009 : newtup->t_tid = oldtup->t_tid;
362 : : }
363 : :
364 : 637184 : return newtup;
365 : : }
366 : :
367 : : /*
368 : : * Search an upper index page for the entry with lowest penalty for insertion
369 : : * of the new index key contained in "it".
370 : : *
371 : : * Returns the index of the page entry to insert into.
372 : : */
373 : : OffsetNumber
374 : 622313 : gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */
375 : : GISTSTATE *giststate)
376 : : {
377 : : OffsetNumber result;
378 : : OffsetNumber maxoff;
379 : : OffsetNumber i;
380 : : float best_penalty[INDEX_MAX_KEYS];
381 : : GISTENTRY entry,
382 : : identry[INDEX_MAX_KEYS];
383 : : bool isnull[INDEX_MAX_KEYS];
384 : : int keep_current_best;
385 : :
4245 tgl@sss.pgh.pa.us 386 [ - + ]: 622313 : Assert(!GistPageIsLeaf(p));
387 : :
6879 teodor@sigaev.ru 388 : 622313 : gistDeCompressAtt(giststate, r,
389 : : it, NULL, (OffsetNumber) 0,
390 : : identry, isnull);
391 : :
392 : : /* we'll return FirstOffsetNumber if page is empty (shouldn't happen) */
4245 tgl@sss.pgh.pa.us 393 : 622313 : result = FirstOffsetNumber;
394 : :
395 : : /*
396 : : * The index may have multiple columns, and there's a penalty value for
397 : : * each column. The penalty associated with a column that appears earlier
398 : : * in the index definition is strictly more important than the penalty of
399 : : * a column that appears later in the index definition.
400 : : *
401 : : * best_penalty[j] is the best penalty we have seen so far for column j,
402 : : * or -1 when we haven't yet examined column j. Array entries to the
403 : : * right of the first -1 are undefined.
404 : : */
405 : 622313 : best_penalty[0] = -1;
406 : :
407 : : /*
408 : : * If we find a tuple that's exactly as good as the currently best one, we
409 : : * could use either one. When inserting a lot of tuples with the same or
410 : : * similar keys, it's preferable to descend down the same path when
411 : : * possible, as that's more cache-friendly. On the other hand, if all
412 : : * inserts land on the same leaf page after a split, we're never going to
413 : : * insert anything to the other half of the split, and will end up using
414 : : * only 50% of the available space. Distributing the inserts evenly would
415 : : * lead to better space usage, but that hurts cache-locality during
416 : : * insertion. To get the best of both worlds, when we find a tuple that's
417 : : * exactly as good as the previous best, choose randomly whether to stick
418 : : * to the old best, or use the new one. Once we decide to stick to the
419 : : * old best, we keep sticking to it for any subsequent equally good tuples
420 : : * we might find. This favors tuples with low offsets, but still allows
421 : : * some inserts to go to other equally-good subtrees.
422 : : *
423 : : * keep_current_best is -1 if we haven't yet had to make a random choice
424 : : * whether to keep the current best tuple. If we have done so, and
425 : : * decided to keep it, keep_current_best is 1; if we've decided to
426 : : * replace, keep_current_best is 0. (This state will be reset to -1 as
427 : : * soon as we've made the replacement, but sometimes we make the choice in
428 : : * advance of actually finding a replacement best tuple.)
429 : : */
4097 heikki.linnakangas@i 430 : 622313 : keep_current_best = -1;
431 : :
432 : : /*
433 : : * Loop over tuples on page.
434 : : */
4245 tgl@sss.pgh.pa.us 435 : 622313 : maxoff = PageGetMaxOffsetNumber(p);
436 [ - + ]: 622313 : Assert(maxoff >= FirstOffsetNumber);
437 : :
438 [ + + ]: 19585866 : for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
439 : : {
6879 teodor@sigaev.ru 440 : 19094447 : IndexTuple itup = (IndexTuple) PageGetItem(p, PageGetItemId(p, i));
441 : : bool zero_penalty;
442 : : int j;
443 : :
4245 tgl@sss.pgh.pa.us 444 : 19094447 : zero_penalty = true;
445 : :
446 : : /* Loop over index attributes. */
1862 akorotkov@postgresql 447 [ + + ]: 38665869 : for (j = 0; j < IndexRelationGetNumberOfKeyAttributes(r); j++)
448 : : {
449 : : Datum datum;
450 : : float usize;
451 : : bool IsNull;
452 : :
453 : : /* Compute penalty for this column. */
454 : 22779008 : datum = index_getattr(itup, j + 1, giststate->leafTupdesc,
455 : : &IsNull);
6879 teodor@sigaev.ru 456 : 22779008 : gistdentryinit(giststate, j, &entry, datum, r, p, i,
457 : : false, IsNull);
6535 458 : 22779008 : usize = gistpenalty(giststate, j, &entry, IsNull,
459 : 22779008 : &identry[j], isnull[j]);
4245 tgl@sss.pgh.pa.us 460 [ + + ]: 22779008 : if (usize > 0)
461 : 22512639 : zero_penalty = false;
462 : :
463 [ + + + + ]: 22779008 : if (best_penalty[j] < 0 || usize < best_penalty[j])
464 : : {
465 : : /*
466 : : * New best penalty for column. Tentatively select this tuple
467 : : * as the target, and record the best penalty. Then reset the
468 : : * next column's penalty to "unknown" (and indirectly, the
469 : : * same for all the ones to its right). This will force us to
470 : : * adopt this tuple's penalty values as the best for all the
471 : : * remaining columns during subsequent loop iterations.
472 : : */
473 : 18246029 : result = i;
474 : 18246029 : best_penalty[j] = usize;
475 : :
1862 akorotkov@postgresql 476 [ + + ]: 18246029 : if (j < IndexRelationGetNumberOfKeyAttributes(r) - 1)
4245 tgl@sss.pgh.pa.us 477 : 3682957 : best_penalty[j + 1] = -1;
478 : :
479 : : /* we have new best, so reset keep-it decision */
4097 heikki.linnakangas@i 480 : 18246029 : keep_current_best = -1;
481 : : }
4245 tgl@sss.pgh.pa.us 482 [ + + ]: 4532979 : else if (best_penalty[j] == usize)
483 : : {
484 : : /*
485 : : * The current tuple is exactly as good for this column as the
486 : : * best tuple seen so far. The next iteration of this loop
487 : : * will compare the next column.
488 : : */
489 : : }
490 : : else
491 : : {
492 : : /*
493 : : * The current tuple is worse for this column than the best
494 : : * tuple seen so far. Skip the remaining columns and move on
495 : : * to the next tuple, if any.
496 : : */
497 : 3207586 : zero_penalty = false; /* so outer loop won't exit */
6879 teodor@sigaev.ru 498 : 3207586 : break;
499 : : }
500 : : }
501 : :
502 : : /*
503 : : * If we looped past the last column, and did not update "result",
504 : : * then this tuple is exactly as good as the prior best tuple.
505 : : */
1862 akorotkov@postgresql 506 [ + + + + ]: 19094447 : if (j == IndexRelationGetNumberOfKeyAttributes(r) && result != i)
507 : : {
4097 heikki.linnakangas@i 508 [ + + ]: 1323789 : if (keep_current_best == -1)
509 : : {
510 : : /* we didn't make the random choice yet for this old best */
868 tgl@sss.pgh.pa.us 511 : 149299 : keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
512 : : }
4097 heikki.linnakangas@i 513 [ + + ]: 1323789 : if (keep_current_best == 0)
514 : : {
515 : : /* we choose to use the new tuple */
516 : 126979 : result = i;
517 : : /* choose again if there are even more exactly-as-good ones */
518 : 126979 : keep_current_best = -1;
519 : : }
520 : : }
521 : :
522 : : /*
523 : : * If we find a tuple with zero penalty for all columns, and we've
524 : : * decided we don't want to search for another tuple with equal
525 : : * penalty, there's no need to examine remaining tuples; just break
526 : : * out of the loop and return it.
527 : : */
4245 tgl@sss.pgh.pa.us 528 [ + + ]: 19094447 : if (zero_penalty)
529 : : {
4097 heikki.linnakangas@i 530 [ + - ]: 262509 : if (keep_current_best == -1)
531 : : {
532 : : /* we didn't make the random choice yet for this old best */
868 tgl@sss.pgh.pa.us 533 : 262509 : keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
534 : : }
4097 heikki.linnakangas@i 535 [ + + ]: 262509 : if (keep_current_best == 1)
536 : 130894 : break;
537 : : }
538 : : }
539 : :
4245 tgl@sss.pgh.pa.us 540 : 622313 : return result;
541 : : }
542 : :
543 : : /*
544 : : * initialize a GiST entry with a decompressed version of key
545 : : */
546 : : void
6879 teodor@sigaev.ru 547 : 27400919 : gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
548 : : Datum k, Relation r, Page pg, OffsetNumber o,
549 : : bool l, bool isNull)
550 : : {
6500 551 [ + + ]: 27400919 : if (!isNull)
552 : : {
553 : : GISTENTRY *dep;
554 : :
555 : 27271946 : gistentryinit(*e, k, r, pg, o, l);
556 : :
557 : : /* there may not be a decompress function in opclass */
2399 tgl@sss.pgh.pa.us 558 [ + + ]: 27271946 : if (!OidIsValid(giststate->decompressFn[nkey].fn_oid))
559 : 24126754 : return;
560 : :
561 : : dep = (GISTENTRY *)
4741 562 : 3145192 : DatumGetPointer(FunctionCall1Coll(&giststate->decompressFn[nkey],
563 : : giststate->supportCollation[nkey],
564 : : PointerGetDatum(e)));
565 : : /* decompressFn may just return the given pointer */
6879 teodor@sigaev.ru 566 [ + + ]: 3145192 : if (dep != e)
567 : 381128 : gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
568 : : dep->leafkey);
569 : : }
570 : : else
6500 571 : 128973 : gistentryinit(*e, (Datum) 0, r, pg, o, l);
572 : : }
573 : :
574 : : IndexTuple
6879 575 : 887978 : gistFormTuple(GISTSTATE *giststate, Relation r,
576 : : const Datum *attdata, const bool *isnull, bool isleaf)
577 : : {
578 : : Datum compatt[INDEX_MAX_KEYS];
579 : : IndexTuple res;
580 : :
1305 heikki.linnakangas@i 581 : 887978 : gistCompressValues(giststate, r, attdata, isnull, isleaf, compatt);
582 : :
583 [ + + ]: 887977 : res = index_form_tuple(isleaf ? giststate->leafTupdesc :
584 : : giststate->nonLeafTupdesc,
585 : : compatt, isnull);
586 : :
587 : : /*
588 : : * The offset number on tuples on internal pages is unused. For historical
589 : : * reasons, it is set to 0xffff.
590 : : */
591 : 887977 : ItemPointerSetOffsetNumber(&(res->t_tid), 0xffff);
592 : 887977 : return res;
593 : : }
594 : :
595 : : void
596 : 986050 : gistCompressValues(GISTSTATE *giststate, Relation r,
597 : : const Datum *attdata, const bool *isnull, bool isleaf, Datum *compatt)
598 : : {
599 : : int i;
600 : :
601 : : /*
602 : : * Call the compress method on each attribute.
603 : : */
1862 akorotkov@postgresql 604 [ + + ]: 2129341 : for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
605 : : {
6879 teodor@sigaev.ru 606 [ + + ]: 1143292 : if (isnull[i])
607 : 7802 : compatt[i] = (Datum) 0;
608 : : else
609 : : {
610 : : GISTENTRY centry;
611 : : GISTENTRY *cep;
612 : :
3307 heikki.linnakangas@i 613 : 1135490 : gistentryinit(centry, attdata[i], r, NULL, (OffsetNumber) 0,
614 : : isleaf);
615 : : /* there may not be a compress function in opclass */
2399 tgl@sss.pgh.pa.us 616 [ + + ]: 1135490 : if (OidIsValid(giststate->compressFn[i].fn_oid))
617 : : cep = (GISTENTRY *)
618 : 890146 : DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i],
619 : : giststate->supportCollation[i],
620 : : PointerGetDatum(¢ry)));
621 : : else
622 : 245344 : cep = ¢ry;
3307 heikki.linnakangas@i 623 : 1135489 : compatt[i] = cep->key;
624 : : }
625 : : }
626 : :
1862 akorotkov@postgresql 627 [ + + ]: 986049 : if (isleaf)
628 : : {
629 : : /*
630 : : * Emplace each included attribute if any.
631 : : */
632 [ + + ]: 725403 : for (; i < r->rd_att->natts; i++)
633 : : {
634 [ + + ]: 146570 : if (isnull[i])
635 : 1000 : compatt[i] = (Datum) 0;
636 : : else
637 : 145570 : compatt[i] = attdata[i];
638 : : }
639 : : }
6879 teodor@sigaev.ru 640 : 986049 : }
641 : :
642 : : /*
643 : : * initialize a GiST entry with fetched value in key field
644 : : */
645 : : static Datum
3307 heikki.linnakangas@i 646 : 52880 : gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
647 : : {
648 : : GISTENTRY fentry;
649 : : GISTENTRY *fep;
650 : :
651 : 52880 : gistentryinit(fentry, k, r, NULL, (OffsetNumber) 0, false);
652 : :
653 : : fep = (GISTENTRY *)
654 : 52880 : DatumGetPointer(FunctionCall1Coll(&giststate->fetchFn[nkey],
655 : : giststate->supportCollation[nkey],
656 : : PointerGetDatum(&fentry)));
657 : :
658 : : /* fetchFn set 'key', return it to the caller */
659 : 52880 : return fep->key;
660 : : }
661 : :
662 : : /*
663 : : * Fetch all keys in tuple.
664 : : * Returns a new HeapTuple containing the originally-indexed data.
665 : : */
666 : : HeapTuple
667 : 265308 : gistFetchTuple(GISTSTATE *giststate, Relation r, IndexTuple tuple)
668 : : {
669 : 265308 : MemoryContext oldcxt = MemoryContextSwitchTo(giststate->tempCxt);
670 : : Datum fetchatt[INDEX_MAX_KEYS];
671 : : bool isnull[INDEX_MAX_KEYS];
672 : : int i;
673 : :
1862 akorotkov@postgresql 674 [ + + ]: 560791 : for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
675 : : {
676 : : Datum datum;
677 : :
678 : 295483 : datum = index_getattr(tuple, i + 1, giststate->leafTupdesc, &isnull[i]);
679 : :
3307 heikki.linnakangas@i 680 [ + + ]: 295483 : if (giststate->fetchFn[i].fn_oid != InvalidOid)
681 : : {
682 [ + + ]: 52886 : if (!isnull[i])
683 : 52880 : fetchatt[i] = gistFetchAtt(giststate, i, datum, r);
684 : : else
685 : 6 : fetchatt[i] = (Datum) 0;
686 : : }
2399 tgl@sss.pgh.pa.us 687 [ + + ]: 242597 : else if (giststate->compressFn[i].fn_oid == InvalidOid)
688 : : {
689 : : /*
690 : : * If opclass does not provide compress method that could change
691 : : * original value, att is necessarily stored in original form.
692 : : */
693 [ + + ]: 212422 : if (!isnull[i])
694 : 210754 : fetchatt[i] = datum;
695 : : else
696 : 1668 : fetchatt[i] = (Datum) 0;
697 : : }
698 : : else
699 : : {
700 : : /*
701 : : * Index-only scans not supported for this column. Since the
702 : : * planner chose an index-only scan anyway, it is not interested
703 : : * in this column, and we can replace it with a NULL.
704 : : */
3307 heikki.linnakangas@i 705 : 30175 : isnull[i] = true;
706 : 30175 : fetchatt[i] = (Datum) 0;
707 : : }
708 : : }
709 : :
710 : : /*
711 : : * Get each included attribute.
712 : : */
1862 akorotkov@postgresql 713 [ - + ]: 265308 : for (; i < r->rd_att->natts; i++)
714 : : {
1862 akorotkov@postgresql 715 :UBC 0 : fetchatt[i] = index_getattr(tuple, i + 1, giststate->leafTupdesc,
716 : : &isnull[i]);
717 : : }
3307 heikki.linnakangas@i 718 :CBC 265308 : MemoryContextSwitchTo(oldcxt);
719 : :
2603 tgl@sss.pgh.pa.us 720 : 265308 : return heap_form_tuple(giststate->fetchTupdesc, fetchatt, isnull);
721 : : }
722 : :
723 : : float
6879 teodor@sigaev.ru 724 : 22931769 : gistpenalty(GISTSTATE *giststate, int attno,
725 : : GISTENTRY *orig, bool isNullOrig,
726 : : GISTENTRY *add, bool isNullAdd)
727 : : {
6402 bruce@momjian.us 728 : 22931769 : float penalty = 0.0;
729 : :
2433 peter_e@gmx.net 730 [ + - ]: 22931769 : if (giststate->penaltyFn[attno].fn_strict == false ||
731 [ + + + + ]: 22931769 : (isNullOrig == false && isNullAdd == false))
732 : : {
4741 tgl@sss.pgh.pa.us 733 : 22786470 : FunctionCall3Coll(&giststate->penaltyFn[attno],
734 : : giststate->supportCollation[attno],
735 : : PointerGetDatum(orig),
736 : : PointerGetDatum(add),
737 : : PointerGetDatum(&penalty));
738 : : /* disallow negative or NaN penalty */
4702 739 [ + + - + ]: 22786470 : if (isnan(penalty) || penalty < 0.0)
740 : 2643 : penalty = 0.0;
741 : : }
6402 bruce@momjian.us 742 [ + + + + ]: 145299 : else if (isNullOrig && isNullAdd)
6535 teodor@sigaev.ru 743 : 7918 : penalty = 0.0;
744 : : else
745 : : {
746 : : /* try to prevent mixing null and non-null values */
4522 tgl@sss.pgh.pa.us 747 : 137381 : penalty = get_float4_infinity();
748 : : }
749 : :
6535 teodor@sigaev.ru 750 : 22931769 : return penalty;
751 : : }
752 : :
753 : : /*
754 : : * Initialize a new index page
755 : : */
756 : : void
1305 heikki.linnakangas@i 757 : 15778 : gistinitpage(Page page, uint32 f)
758 : : {
759 : : GISTPageOpaque opaque;
760 : :
1024 peter@eisentraut.org 761 : 15778 : PageInit(page, BLCKSZ, sizeof(GISTPageOpaqueData));
762 : :
6734 tgl@sss.pgh.pa.us 763 : 15778 : opaque = GistPageGetOpaque(page);
6215 764 : 15778 : opaque->rightlink = InvalidBlockNumber;
765 : 15778 : opaque->flags = f;
766 : 15778 : opaque->gist_page_id = GIST_PAGE_ID;
6734 767 : 15778 : }
768 : :
769 : : /*
770 : : * Initialize a new index buffer
771 : : */
772 : : void
1305 heikki.linnakangas@i 773 : 14454 : GISTInitBuffer(Buffer b, uint32 f)
774 : : {
775 : : Page page;
776 : :
777 : 14454 : page = BufferGetPage(b);
778 : 14454 : gistinitpage(page, f);
779 : 14454 : }
780 : :
781 : : /*
782 : : * Verify that a freshly-read page looks sane.
783 : : */
784 : : void
6734 tgl@sss.pgh.pa.us 785 : 1101764 : gistcheckpage(Relation rel, Buffer buf)
786 : : {
2916 kgrittn@postgresql.o 787 : 1101764 : Page page = BufferGetPage(buf);
788 : :
789 : : /*
790 : : * ReadBuffer verifies that every newly-read page passes
791 : : * PageHeaderIsValid, which means it either contains a reasonably sane
792 : : * page header or is all-zero. We have to defend against the all-zero
793 : : * case, however.
794 : : */
6734 tgl@sss.pgh.pa.us 795 [ - + ]: 1101764 : if (PageIsNew(page))
6734 tgl@sss.pgh.pa.us 796 [ # # ]:UBC 0 : ereport(ERROR,
797 : : (errcode(ERRCODE_INDEX_CORRUPTED),
798 : : errmsg("index \"%s\" contains unexpected zero page at block %u",
799 : : RelationGetRelationName(rel),
800 : : BufferGetBlockNumber(buf)),
801 : : errhint("Please REINDEX it.")));
802 : :
803 : : /*
804 : : * Additionally check that the special area looks sane.
805 : : */
5754 tgl@sss.pgh.pa.us 806 [ - + ]:CBC 1101764 : if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GISTPageOpaqueData)))
6734 tgl@sss.pgh.pa.us 807 [ # # ]:UBC 0 : ereport(ERROR,
808 : : (errcode(ERRCODE_INDEX_CORRUPTED),
809 : : errmsg("index \"%s\" contains corrupted page at block %u",
810 : : RelationGetRelationName(rel),
811 : : BufferGetBlockNumber(buf)),
812 : : errhint("Please REINDEX it.")));
6734 tgl@sss.pgh.pa.us 813 :CBC 1101764 : }
814 : :
815 : :
816 : : /*
817 : : * Allocate a new page (either by recycling, or by extending the index file)
818 : : *
819 : : * The returned buffer is already pinned and exclusive-locked
820 : : *
821 : : * Caller is responsible for initializing the page by calling GISTInitBuffer
822 : : */
823 : : Buffer
379 andres@anarazel.de 824 : 13534 : gistNewBuffer(Relation r, Relation heaprel)
825 : : {
826 : : Buffer buffer;
827 : :
828 : : /* First, try to get a page from FSM */
829 : : for (;;)
6779 bruce@momjian.us 830 :UBC 0 : {
5675 heikki.linnakangas@i 831 :CBC 13534 : BlockNumber blkno = GetFreeIndexPage(r);
832 : :
6866 teodor@sigaev.ru 833 [ + - ]: 13534 : if (blkno == InvalidBlockNumber)
6734 tgl@sss.pgh.pa.us 834 : 13534 : break; /* nothing left in FSM */
835 : :
6866 teodor@sigaev.ru 836 :UBC 0 : buffer = ReadBuffer(r, blkno);
837 : :
838 : : /*
839 : : * We have to guard against the possibility that someone else already
840 : : * recycled this page; the buffer may be locked if so.
841 : : */
6779 bruce@momjian.us 842 [ # # ]: 0 : if (ConditionalLockBuffer(buffer))
843 : : {
2916 kgrittn@postgresql.o 844 : 0 : Page page = BufferGetPage(buffer);
845 : :
846 : : /*
847 : : * If the page was never initialized, it's OK to use.
848 : : */
6734 tgl@sss.pgh.pa.us 849 [ # # ]: 0 : if (PageIsNew(page))
1850 heikki.linnakangas@i 850 : 0 : return buffer;
851 : :
6734 tgl@sss.pgh.pa.us 852 : 0 : gistcheckpage(r, buffer);
853 : :
854 : : /*
855 : : * Otherwise, recycle it if deleted, and too old to have any
856 : : * processes interested in it.
857 : : */
1850 heikki.linnakangas@i 858 [ # # ]: 0 : if (gistPageRecyclable(page))
859 : : {
860 : : /*
861 : : * If we are generating WAL for Hot Standby then create a WAL
862 : : * record that will allow us to conflict with queries running
863 : : * on standby, in case they have snapshots older than the
864 : : * page's deleteXid.
865 : : */
866 [ # # # # : 0 : if (XLogStandbyInfoActive() && RelationNeedsWAL(r))
# # # # #
# ]
379 andres@anarazel.de 867 : 0 : gistXLogPageReuse(r, heaprel, blkno, GistPageGetDeleteXid(page));
868 : :
1850 heikki.linnakangas@i 869 : 0 : return buffer;
870 : : }
871 : :
6734 tgl@sss.pgh.pa.us 872 : 0 : LockBuffer(buffer, GIST_UNLOCK);
873 : : }
874 : :
875 : : /* Can't use it, so release buffer and try again */
6779 bruce@momjian.us 876 : 0 : ReleaseBuffer(buffer);
877 : : }
878 : :
879 : : /* Must extend the file */
235 tmunro@postgresql.or 880 :CBC 13534 : buffer = ExtendBufferedRel(BMR_REL(r), MAIN_FORKNUM, NULL,
881 : : EB_LOCK_FIRST);
882 : :
6873 teodor@sigaev.ru 883 : 13534 : return buffer;
884 : : }
885 : :
886 : : /* Can this page be recycled yet? */
887 : : bool
1850 heikki.linnakangas@i 888 : 1501 : gistPageRecyclable(Page page)
889 : : {
1726 890 [ - + ]: 1501 : if (PageIsNew(page))
1726 heikki.linnakangas@i 891 :UBC 0 : return true;
1726 heikki.linnakangas@i 892 [ - + ]:CBC 1501 : if (GistPageIsDeleted(page))
893 : : {
894 : : /*
895 : : * The page was deleted, but when? If it was just deleted, a scan
896 : : * might have seen the downlink to it, and will read the page later.
897 : : * As long as that can happen, we must keep the deleted page around as
898 : : * a tombstone.
899 : : *
900 : : * For that check if the deletion XID could still be visible to
901 : : * anyone. If not, then no scan that's still in progress could have
902 : : * seen its downlink, and we can recycle it.
903 : : */
1726 heikki.linnakangas@i 904 :UBC 0 : FullTransactionId deletexid_full = GistPageGetDeleteXid(page);
905 : :
1162 pg@bowt.ie 906 : 0 : return GlobalVisCheckRemovableFullXid(NULL, deletexid_full);
907 : : }
1726 heikki.linnakangas@i 908 :CBC 1501 : return false;
909 : : }
910 : :
911 : : bytea *
3010 tgl@sss.pgh.pa.us 912 : 81 : gistoptions(Datum reloptions, bool validate)
913 : : {
914 : : static const relopt_parse_elt tab[] = {
915 : : {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
916 : : {"buffering", RELOPT_TYPE_ENUM, offsetof(GiSTOptions, buffering_mode)}
917 : : };
918 : :
1622 michael@paquier.xyz 919 : 81 : return (bytea *) build_reloptions(reloptions, validate,
920 : : RELOPT_KIND_GIST,
921 : : sizeof(GiSTOptions),
922 : : tab, lengthof(tab));
923 : : }
924 : :
925 : : /*
926 : : * gistproperty() -- Check boolean properties of indexes.
927 : : *
928 : : * This is optional for most AMs, but is required for GiST because the core
929 : : * property code doesn't support AMPROP_DISTANCE_ORDERABLE. We also handle
930 : : * AMPROP_RETURNABLE here to save opening the rel to call gistcanreturn.
931 : : */
932 : : bool
2801 tgl@sss.pgh.pa.us 933 : 234 : gistproperty(Oid index_oid, int attno,
934 : : IndexAMProperty prop, const char *propname,
935 : : bool *res, bool *isnull)
936 : : {
937 : : Oid opclass,
938 : : opfamily,
939 : : opcintype;
940 : : int16 procno;
941 : :
942 : : /* Only answer column-level inquiries */
943 [ + + ]: 234 : if (attno == 0)
944 : 147 : return false;
945 : :
946 : : /*
947 : : * Currently, GiST distance-ordered scans require that there be a distance
948 : : * function in the opclass with the default types (i.e. the one loaded
949 : : * into the relcache entry, see initGISTstate). So we assume that if such
950 : : * a function exists, then there's a reason for it (rather than grubbing
951 : : * through all the opfamily's operators to find an ordered one).
952 : : *
953 : : * Essentially the same code can test whether we support returning the
954 : : * column data, since that's true if the opclass provides a fetch proc.
955 : : */
956 : :
957 [ + + + ]: 87 : switch (prop)
958 : : {
959 : 6 : case AMPROP_DISTANCE_ORDERABLE:
960 : 6 : procno = GIST_DISTANCE_PROC;
961 : 6 : break;
962 : 6 : case AMPROP_RETURNABLE:
963 : 6 : procno = GIST_FETCH_PROC;
964 : 6 : break;
965 : 75 : default:
966 : 75 : return false;
967 : : }
968 : :
969 : : /* First we need to know the column's opclass. */
2034 akorotkov@postgresql 970 : 12 : opclass = get_index_column_opclass(index_oid, attno);
971 [ - + ]: 12 : if (!OidIsValid(opclass))
972 : : {
2801 tgl@sss.pgh.pa.us 973 :UBC 0 : *isnull = true;
974 : 0 : return true;
975 : : }
976 : :
977 : : /* Now look up the opclass family and input datatype. */
2034 akorotkov@postgresql 978 [ - + ]:CBC 12 : if (!get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
979 : : {
2801 tgl@sss.pgh.pa.us 980 :UBC 0 : *isnull = true;
981 : 0 : return true;
982 : : }
983 : :
984 : : /* And now we can check whether the function is provided. */
985 : :
2801 tgl@sss.pgh.pa.us 986 :CBC 12 : *res = SearchSysCacheExists4(AMPROCNUM,
987 : : ObjectIdGetDatum(opfamily),
988 : : ObjectIdGetDatum(opcintype),
989 : : ObjectIdGetDatum(opcintype),
990 : : Int16GetDatum(procno));
991 : :
992 : : /*
993 : : * Special case: even without a fetch function, AMPROP_RETURNABLE is true
994 : : * if the opclass has no compress function.
995 : : */
2399 996 [ + + + - ]: 12 : if (prop == AMPROP_RETURNABLE && !*res)
997 : : {
998 : 6 : *res = !SearchSysCacheExists4(AMPROCNUM,
999 : : ObjectIdGetDatum(opfamily),
1000 : : ObjectIdGetDatum(opcintype),
1001 : : ObjectIdGetDatum(opcintype),
1002 : : Int16GetDatum(GIST_COMPRESS_PROC));
1003 : : }
1004 : :
2034 akorotkov@postgresql 1005 : 12 : *isnull = false;
1006 : :
2801 tgl@sss.pgh.pa.us 1007 : 12 : return true;
1008 : : }
1009 : :
1010 : : /*
1011 : : * Some indexes are not WAL-logged, but we need LSNs to detect concurrent page
1012 : : * splits anyway. This function provides a fake sequence of LSNs for that
1013 : : * purpose.
1014 : : */
1015 : : XLogRecPtr
4080 heikki.linnakangas@i 1016 : 42 : gistGetFakeLSN(Relation rel)
1017 : : {
1018 [ + + ]: 42 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
1019 : : {
1020 : : /*
1021 : : * Temporary relations are only accessible in our session, so a simple
1022 : : * backend-local counter will do.
1023 : : */
1024 : : static XLogRecPtr counter = FirstNormalUnloggedLSN;
1025 : :
1026 : 9 : return counter++;
1027 : : }
1119 bruce@momjian.us 1028 [ - + ]: 33 : else if (RelationIsPermanent(rel))
1029 : : {
1030 : : /*
1031 : : * WAL-logging on this relation will start after commit, so its LSNs
1032 : : * must be distinct numbers smaller than the LSN at the next commit.
1033 : : * Emit a dummy WAL record if insert-LSN hasn't advanced after the
1034 : : * last call.
1035 : : */
1036 : : static XLogRecPtr lastlsn = InvalidXLogRecPtr;
1471 noah@leadboat.com 1037 :UBC 0 : XLogRecPtr currlsn = GetXLogInsertRecPtr();
1038 : :
1039 : : /* Shouldn't be called for WAL-logging relations */
1040 [ # # # # : 0 : Assert(!RelationNeedsWAL(rel));
# # # # ]
1041 : :
1042 : : /* No need for an actual record if we already have a distinct LSN */
1043 [ # # # # ]: 0 : if (!XLogRecPtrIsInvalid(lastlsn) && lastlsn == currlsn)
1044 : 0 : currlsn = gistXLogAssignLSN();
1045 : :
1046 : 0 : lastlsn = currlsn;
1047 : 0 : return currlsn;
1048 : : }
1049 : : else
1050 : : {
1051 : : /*
1052 : : * Unlogged relations are accessible from other backends, and survive
1053 : : * (clean) restarts. GetFakeLSNForUnloggedRel() handles that for us.
1054 : : */
4080 heikki.linnakangas@i 1055 [ - + ]:CBC 33 : Assert(rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED);
1056 : 33 : return GetFakeLSNForUnloggedRel();
1057 : : }
1058 : : }
1059 : :
1060 : : /*
1061 : : * Returns the same number that was received.
1062 : : *
1063 : : * This is for GiST opclasses that use the RT*StrategyNumber constants.
1064 : : */
1065 : : Datum
86 peter@eisentraut.org 1066 :GNC 997 : gist_stratnum_identity(PG_FUNCTION_ARGS)
1067 : : {
1068 : 997 : StrategyNumber strat = PG_GETARG_UINT16(0);
1069 : :
1070 : 997 : PG_RETURN_UINT16(strat);
1071 : : }
1072 : :
1073 : : /*
1074 : : * Returns the opclass's private stratnum used for the given strategy.
1075 : : *
1076 : : * Calls the opclass's GIST_STRATNUM_PROC support function, if any,
1077 : : * and returns the result.
1078 : : * Returns InvalidStrategy if the function is not defined.
1079 : : */
1080 : : StrategyNumber
81 1081 : 994 : GistTranslateStratnum(Oid opclass, StrategyNumber strat)
1082 : : {
1083 : : Oid opfamily;
1084 : : Oid opcintype;
1085 : : Oid funcid;
1086 : : Datum result;
1087 : :
1088 : : /* Look up the opclass family and input datatype. */
1089 [ - + ]: 994 : if (!get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
81 peter@eisentraut.org 1090 :UNC 0 : return InvalidStrategy;
1091 : :
1092 : : /* Check whether the function is provided. */
81 peter@eisentraut.org 1093 :GNC 994 : funcid = get_opfamily_proc(opfamily, opcintype, opcintype, GIST_STRATNUM_PROC);
1094 [ - + ]: 994 : if (!OidIsValid(funcid))
81 peter@eisentraut.org 1095 :UNC 0 : return InvalidStrategy;
1096 : :
1097 : : /* Ask the translation function */
81 peter@eisentraut.org 1098 :GNC 994 : result = OidFunctionCall1Coll(funcid, InvalidOid, UInt16GetDatum(strat));
1099 : 994 : return DatumGetUInt16(result);
1100 : : }
|