Age Owner TLA Line data Source code
1 : /*
2 : * brin_tuple.c
3 : * Method implementations for tuples in BRIN indexes.
4 : *
5 : * Intended usage is that code outside this file only deals with
6 : * BrinMemTuples, and convert to and from the on-disk representation through
7 : * functions in this file.
8 : *
9 : * NOTES
10 : *
11 : * A BRIN tuple is similar to a heap tuple, with a few key differences. The
12 : * first interesting difference is that the tuple header is much simpler, only
13 : * containing its total length and a small area for flags. Also, the stored
14 : * data does not match the relation tuple descriptor exactly: for each
15 : * attribute in the descriptor, the index tuple carries an arbitrary number
16 : * of values, depending on the opclass.
17 : *
18 : * Also, for each column of the index relation there are two null bits: one
19 : * (hasnulls) stores whether any tuple within the page range has that column
20 : * set to null; the other one (allnulls) stores whether the column values are
21 : * all null. If allnulls is true, then the tuple data area does not contain
22 : * values for that column at all; whereas it does if the hasnulls is set.
23 : * Note the size of the null bitmask may not be the same as that of the
24 : * datum array.
25 : *
26 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
27 : * Portions Copyright (c) 1994, Regents of the University of California
28 : *
29 : * IDENTIFICATION
30 : * src/backend/access/brin/brin_tuple.c
31 : */
32 : #include "postgres.h"
33 :
34 : #include "access/brin_tuple.h"
35 : #include "access/detoast.h"
36 : #include "access/heaptoast.h"
37 : #include "access/htup_details.h"
38 : #include "access/toast_internals.h"
39 : #include "access/tupdesc.h"
40 : #include "access/tupmacs.h"
41 : #include "utils/datum.h"
42 : #include "utils/memutils.h"
43 :
44 :
45 : /*
46 : * This enables de-toasting of index entries. Needed until VACUUM is
47 : * smart enough to rebuild indexes from scratch.
48 : */
49 : #define TOAST_INDEX_HACK
50 :
51 :
52 : static inline void brin_deconstruct_tuple(BrinDesc *brdesc,
53 : char *tp, bits8 *nullbits, bool nulls,
54 : Datum *values, bool *allnulls, bool *hasnulls);
55 :
56 :
57 : /*
58 : * Return a tuple descriptor used for on-disk storage of BRIN tuples.
59 : */
60 : static TupleDesc
3075 alvherre 61 CBC 110572 : brtuple_disk_tupdesc(BrinDesc *brdesc)
62 : {
63 : /* We cache these in the BrinDesc */
64 110572 : if (brdesc->bd_disktdesc == NULL)
65 : {
66 : int i;
67 : int j;
68 1966 : AttrNumber attno = 1;
69 : TupleDesc tupdesc;
70 : MemoryContext oldcxt;
71 :
72 : /* make sure it's in the bdesc's context */
73 1966 : oldcxt = MemoryContextSwitchTo(brdesc->bd_context);
74 :
1601 andres 75 1966 : tupdesc = CreateTemplateTupleDesc(brdesc->bd_totalstored);
76 :
3075 alvherre 77 37092 : for (i = 0; i < brdesc->bd_tupdesc->natts; i++)
78 : {
79 96744 : for (j = 0; j < brdesc->bd_info[i]->oi_nstored; j++)
80 61618 : TupleDescInitEntry(tupdesc, attno++, NULL,
2118 tgl 81 61618 : brdesc->bd_info[i]->oi_typcache[j]->type_id,
82 : -1, 0);
83 : }
84 :
3075 alvherre 85 1966 : MemoryContextSwitchTo(oldcxt);
86 :
87 1966 : brdesc->bd_disktdesc = tupdesc;
88 : }
89 :
90 110572 : return brdesc->bd_disktdesc;
91 : }
92 :
93 : /*
94 : * Generate a new on-disk tuple to be inserted in a BRIN index.
95 : *
96 : * See brin_form_placeholder_tuple if you touch this.
97 : */
98 : BrinTuple *
99 4269 : brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
100 : Size *size)
101 : {
102 : Datum *values;
103 : bool *nulls;
104 4269 : bool anynulls = false;
105 : BrinTuple *rettuple;
106 : int keyno;
107 : int idxattno;
3072 108 4269 : uint16 phony_infomask = 0;
109 : bits8 *phony_nullbitmap;
110 : Size len,
111 : hoff,
112 : data_len;
113 : int i;
114 :
115 : #ifdef TOAST_INDEX_HACK
116 : Datum *untoasted_values;
883 tomas.vondra 117 4269 : int nuntoasted = 0;
118 : #endif
119 :
3075 alvherre 120 4269 : Assert(brdesc->bd_totalstored > 0);
121 :
2876 tgl 122 4269 : values = (Datum *) palloc(sizeof(Datum) * brdesc->bd_totalstored);
123 4269 : nulls = (bool *) palloc0(sizeof(bool) * brdesc->bd_totalstored);
124 : phony_nullbitmap = (bits8 *)
125 4269 : palloc(sizeof(bits8) * BITMAPLEN(brdesc->bd_totalstored));
126 :
127 : #ifdef TOAST_INDEX_HACK
883 tomas.vondra 128 4269 : untoasted_values = (Datum *) palloc(sizeof(Datum) * brdesc->bd_totalstored);
129 : #endif
130 :
131 : /*
132 : * Set up the values/nulls arrays for heap_fill_tuple
133 : */
3075 alvherre 134 4269 : idxattno = 0;
135 58855 : for (keyno = 0; keyno < brdesc->bd_tupdesc->natts; keyno++)
136 : {
137 : int datumno;
138 :
139 : /*
140 : * "allnulls" is set when there's no nonnull value in any row in the
141 : * column; when this happens, there is no data to store. Thus set the
142 : * nullable bits for all data elements of this column and we're done.
143 : */
144 54586 : if (tuple->bt_columns[keyno].bv_allnulls)
145 : {
146 297 : for (datumno = 0;
147 666 : datumno < brdesc->bd_info[keyno]->oi_nstored;
148 369 : datumno++)
149 369 : nulls[idxattno++] = true;
150 297 : anynulls = true;
151 297 : continue;
152 : }
153 :
154 : /*
155 : * The "hasnulls" bit is set when there are some null values in the
156 : * data. We still need to store a real value, but the presence of
157 : * this means we need a null bitmap.
158 : */
159 54289 : if (tuple->bt_columns[keyno].bv_hasnulls)
160 4779 : anynulls = true;
161 :
162 : /* If needed, serialize the values before forming the on-disk tuple. */
744 tomas.vondra 163 54289 : if (tuple->bt_columns[keyno].bv_serialize)
164 : {
165 8535 : tuple->bt_columns[keyno].bv_serialize(brdesc,
166 : tuple->bt_columns[keyno].bv_mem_value,
167 : tuple->bt_columns[keyno].bv_values);
168 : }
169 :
170 : /*
171 : * Now obtain the values of each stored datum. Note that some values
172 : * might be toasted, and we cannot rely on the original heap values
173 : * sticking around forever, so we must detoast them. Also try to
174 : * compress them.
175 : */
3075 alvherre 176 54289 : for (datumno = 0;
177 143112 : datumno < brdesc->bd_info[keyno]->oi_nstored;
178 88823 : datumno++)
179 : {
697 tgl 180 88823 : Datum value = tuple->bt_columns[keyno].bv_values[datumno];
181 :
182 : #ifdef TOAST_INDEX_HACK
183 :
184 : /* We must look at the stored type, not at the index descriptor. */
185 88823 : TypeCacheEntry *atttype = brdesc->bd_info[keyno]->oi_typcache[datumno];
186 :
187 : /* Do we need to free the value at the end? */
188 88823 : bool free_value = false;
189 :
190 : /* For non-varlena types we don't need to do anything special */
883 tomas.vondra 191 88823 : if (atttype->typlen != -1)
192 : {
193 47147 : values[idxattno++] = value;
194 47147 : continue;
195 : }
196 :
197 : /*
198 : * Do nothing if value is not of varlena type. We don't need to
199 : * care about NULL values here, thanks to bv_allnulls above.
200 : *
201 : * If value is stored EXTERNAL, must fetch it so we are not
202 : * depending on outside storage.
203 : *
204 : * XXX Is this actually true? Could it be that the summary is NULL
205 : * even for range with non-NULL data? E.g. degenerate bloom filter
206 : * may be thrown away, etc.
207 : */
208 41676 : if (VARATT_IS_EXTERNAL(DatumGetPointer(value)))
209 : {
210 12 : value = PointerGetDatum(detoast_external_attr((struct varlena *)
211 12 : DatumGetPointer(value)));
212 12 : free_value = true;
213 : }
214 :
215 : /*
216 : * If value is above size target, and is of a compressible
217 : * datatype, try to compress it in-line.
218 : */
219 41676 : if (!VARATT_IS_EXTENDED(DatumGetPointer(value)) &&
220 23661 : VARSIZE(DatumGetPointer(value)) > TOAST_INDEX_TARGET &&
221 30 : (atttype->typstorage == TYPSTORAGE_EXTENDED ||
883 tomas.vondra 222 UBC 0 : atttype->typstorage == TYPSTORAGE_MAIN))
223 : {
224 : Datum cvalue;
225 : char compression;
751 rhaas 226 CBC 30 : Form_pg_attribute att = TupleDescAttr(brdesc->bd_tupdesc,
227 : keyno);
228 :
229 : /*
230 : * If the BRIN summary and indexed attribute use the same data
231 : * type and it has a valid compression method, we can use the
232 : * same compression method. Otherwise we have to use the
233 : * default method.
234 : */
682 tgl 235 30 : if (att->atttypid == atttype->type_id)
749 tomas.vondra 236 24 : compression = att->attcompression;
237 : else
682 tgl 238 6 : compression = InvalidCompressionMethod;
239 :
749 tomas.vondra 240 30 : cvalue = toast_compress_datum(value, compression);
241 :
883 242 30 : if (DatumGetPointer(cvalue) != NULL)
243 : {
244 : /* successful compression */
245 6 : if (free_value)
883 tomas.vondra 246 UBC 0 : pfree(DatumGetPointer(value));
247 :
883 tomas.vondra 248 CBC 6 : value = cvalue;
249 6 : free_value = true;
250 : }
251 : }
252 :
253 : /*
254 : * If we untoasted / compressed the value, we need to free it
255 : * after forming the index tuple.
256 : */
257 41676 : if (free_value)
258 18 : untoasted_values[nuntoasted++] = value;
259 :
260 : #endif
261 :
262 41676 : values[idxattno++] = value;
263 : }
264 : }
265 :
266 : /* Assert we did not overrun temp arrays */
2876 tgl 267 4269 : Assert(idxattno <= brdesc->bd_totalstored);
268 :
269 : /* compute total space needed */
3075 alvherre 270 4269 : len = SizeOfBrinTuple;
271 4269 : if (anynulls)
272 : {
273 : /*
274 : * We need a double-length bitmap on an on-disk BRIN index tuple; the
275 : * first half stores the "allnulls" bits, the second stores
276 : * "hasnulls".
277 : */
278 284 : len += BITMAPLEN(brdesc->bd_tupdesc->natts * 2);
279 : }
280 :
281 4269 : len = hoff = MAXALIGN(len);
282 :
283 4269 : data_len = heap_compute_data_size(brtuple_disk_tupdesc(brdesc),
284 : values, nulls);
285 4269 : len += data_len;
286 :
2876 tgl 287 4269 : len = MAXALIGN(len);
288 :
3075 alvherre 289 4269 : rettuple = palloc0(len);
290 4269 : rettuple->bt_blkno = blkno;
291 4269 : rettuple->bt_info = hoff;
292 :
293 : /* Assert that hoff fits in the space available */
294 4269 : Assert((rettuple->bt_info & BRIN_OFFSET_MASK) == hoff);
295 :
296 : /*
297 : * The infomask and null bitmap as computed by heap_fill_tuple are useless
298 : * to us. However, that function will not accept a null infomask; and we
299 : * need to pass a valid null bitmap so that it will correctly skip
300 : * outputting null attributes in the data area.
301 : */
302 4269 : heap_fill_tuple(brtuple_disk_tupdesc(brdesc),
303 : values,
304 : nulls,
305 : (char *) rettuple + hoff,
306 : data_len,
307 : &phony_infomask,
308 : phony_nullbitmap);
309 :
310 : /* done with these */
311 4269 : pfree(values);
312 4269 : pfree(nulls);
313 4269 : pfree(phony_nullbitmap);
314 :
315 : #ifdef TOAST_INDEX_HACK
883 tomas.vondra 316 4287 : for (i = 0; i < nuntoasted; i++)
317 18 : pfree(DatumGetPointer(untoasted_values[i]));
318 : #endif
319 :
320 : /*
321 : * Now fill in the real null bitmasks. allnulls first.
322 : */
3075 alvherre 323 4269 : if (anynulls)
324 : {
325 : bits8 *bitP;
326 : int bitmask;
327 :
328 284 : rettuple->bt_info |= BRIN_NULLS_MASK;
329 :
330 : /*
331 : * Note that we reverse the sense of null bits in this module: we
332 : * store a 1 for a null attribute rather than a 0. So we must reverse
333 : * the sense of the att_isnull test in brin_deconstruct_tuple as well.
334 : */
335 284 : bitP = ((bits8 *) ((char *) rettuple + SizeOfBrinTuple)) - 1;
336 284 : bitmask = HIGHBIT;
337 6029 : for (keyno = 0; keyno < brdesc->bd_tupdesc->natts; keyno++)
338 : {
339 5745 : if (bitmask != HIGHBIT)
340 4893 : bitmask <<= 1;
341 : else
342 : {
343 852 : bitP += 1;
344 852 : *bitP = 0x0;
345 852 : bitmask = 1;
346 : }
347 :
348 5745 : if (!tuple->bt_columns[keyno].bv_allnulls)
349 5448 : continue;
350 :
351 297 : *bitP |= bitmask;
352 : }
353 : /* hasnulls bits follow */
354 6029 : for (keyno = 0; keyno < brdesc->bd_tupdesc->natts; keyno++)
355 : {
356 5745 : if (bitmask != HIGHBIT)
357 5097 : bitmask <<= 1;
358 : else
359 : {
360 648 : bitP += 1;
361 648 : *bitP = 0x0;
362 648 : bitmask = 1;
363 : }
364 :
365 5745 : if (!tuple->bt_columns[keyno].bv_hasnulls)
366 777 : continue;
367 :
368 4968 : *bitP |= bitmask;
369 : }
370 : }
371 :
372 4269 : if (tuple->bt_placeholder)
3075 alvherre 373 UBC 0 : rettuple->bt_info |= BRIN_PLACEHOLDER_MASK;
374 :
3075 alvherre 375 CBC 4269 : *size = len;
376 4269 : return rettuple;
377 : }
378 :
379 : /*
380 : * Generate a new on-disk tuple with no data values, marked as placeholder.
381 : *
382 : * This is a cut-down version of brin_form_tuple.
383 : */
384 : BrinTuple *
385 1467 : brin_form_placeholder_tuple(BrinDesc *brdesc, BlockNumber blkno, Size *size)
386 : {
387 : Size len;
388 : Size hoff;
389 : BrinTuple *rettuple;
390 : int keyno;
391 : bits8 *bitP;
392 : int bitmask;
393 :
394 : /* compute total space needed: always add nulls */
395 1467 : len = SizeOfBrinTuple;
396 1467 : len += BITMAPLEN(brdesc->bd_tupdesc->natts * 2);
397 1467 : len = hoff = MAXALIGN(len);
398 :
399 1467 : rettuple = palloc0(len);
400 1467 : rettuple->bt_blkno = blkno;
401 1467 : rettuple->bt_info = hoff;
402 1467 : rettuple->bt_info |= BRIN_NULLS_MASK | BRIN_PLACEHOLDER_MASK;
403 :
404 1467 : bitP = ((bits8 *) ((char *) rettuple + SizeOfBrinTuple)) - 1;
405 1467 : bitmask = HIGHBIT;
406 : /* set allnulls true for all attributes */
407 4110 : for (keyno = 0; keyno < brdesc->bd_tupdesc->natts; keyno++)
408 : {
409 2643 : if (bitmask != HIGHBIT)
410 1059 : bitmask <<= 1;
411 : else
412 : {
413 1584 : bitP += 1;
414 1584 : *bitP = 0x0;
415 1584 : bitmask = 1;
416 : }
417 :
418 2643 : *bitP |= bitmask;
419 : }
420 : /* no need to set hasnulls */
421 :
422 1467 : *size = len;
423 1467 : return rettuple;
424 : }
425 :
426 : /*
427 : * Free a tuple created by brin_form_tuple
428 : */
429 : void
430 2934 : brin_free_tuple(BrinTuple *tuple)
431 : {
432 2934 : pfree(tuple);
433 2934 : }
434 :
435 : /*
436 : * Given a brin tuple of size len, create a copy of it. If 'dest' is not
437 : * NULL, its size is destsz, and can be used as output buffer; if the tuple
438 : * to be copied does not fit, it is enlarged by repalloc, and the size is
439 : * updated to match. This avoids palloc/free cycles when many brin tuples
440 : * are being processed in loops.
441 : */
442 : BrinTuple *
2193 443 95896 : brin_copy_tuple(BrinTuple *tuple, Size len, BrinTuple *dest, Size *destsz)
444 : {
445 95896 : if (!destsz || *destsz == 0)
446 95896 : dest = palloc(len);
2193 alvherre 447 UBC 0 : else if (len > *destsz)
448 : {
449 0 : dest = repalloc(dest, len);
450 0 : *destsz = len;
451 : }
452 :
2193 alvherre 453 CBC 95896 : memcpy(dest, tuple, len);
454 :
455 95896 : return dest;
456 : }
457 :
458 : /*
459 : * Return whether two BrinTuples are bitwise identical.
460 : */
461 : bool
3075 462 3436 : brin_tuples_equal(const BrinTuple *a, Size alen, const BrinTuple *b, Size blen)
463 : {
464 3436 : if (alen != blen)
3075 alvherre 465 UBC 0 : return false;
3075 alvherre 466 CBC 3436 : if (memcmp(a, b, alen) != 0)
3075 alvherre 467 UBC 0 : return false;
3075 alvherre 468 CBC 3436 : return true;
469 : }
470 :
471 : /*
472 : * Create a new BrinMemTuple from scratch, and initialize it to an empty
473 : * state.
474 : *
475 : * Note: we don't provide any means to free a deformed tuple, so make sure to
476 : * use a temporary memory context.
477 : */
478 : BrinMemTuple *
479 9555 : brin_new_memtuple(BrinDesc *brdesc)
480 : {
481 : BrinMemTuple *dtup;
482 : long basesize;
483 :
484 9555 : basesize = MAXALIGN(sizeof(BrinMemTuple) +
485 : sizeof(BrinValues) * brdesc->bd_tupdesc->natts);
486 9555 : dtup = palloc0(basesize + sizeof(Datum) * brdesc->bd_totalstored);
487 :
2193 488 9555 : dtup->bt_values = palloc(sizeof(Datum) * brdesc->bd_totalstored);
489 9555 : dtup->bt_allnulls = palloc(sizeof(bool) * brdesc->bd_tupdesc->natts);
490 9555 : dtup->bt_hasnulls = palloc(sizeof(bool) * brdesc->bd_tupdesc->natts);
491 :
3075 492 9555 : dtup->bt_context = AllocSetContextCreate(CurrentMemoryContext,
493 : "brin dtuple",
494 : ALLOCSET_DEFAULT_SIZES);
495 :
2193 496 9555 : brin_memtuple_initialize(dtup, brdesc);
497 :
3075 498 9555 : return dtup;
499 : }
500 :
501 : /*
502 : * Reset a BrinMemTuple to initial state. We return the same tuple, for
503 : * notational convenience.
504 : */
505 : BrinMemTuple *
506 105954 : brin_memtuple_initialize(BrinMemTuple *dtuple, BrinDesc *brdesc)
507 : {
508 : int i;
509 : char *currdatum;
510 :
511 105954 : MemoryContextReset(dtuple->bt_context);
512 :
2193 513 105954 : currdatum = (char *) dtuple +
514 105954 : MAXALIGN(sizeof(BrinMemTuple) +
515 : sizeof(BrinValues) * brdesc->bd_tupdesc->natts);
3075 516 2839060 : for (i = 0; i < brdesc->bd_tupdesc->natts; i++)
517 : {
2193 518 2733106 : dtuple->bt_columns[i].bv_attno = i + 1;
519 2733106 : dtuple->bt_columns[i].bv_allnulls = true;
520 2733106 : dtuple->bt_columns[i].bv_hasnulls = false;
521 2733106 : dtuple->bt_columns[i].bv_values = (Datum *) currdatum;
522 :
744 tomas.vondra 523 2733106 : dtuple->bt_columns[i].bv_mem_value = PointerGetDatum(NULL);
524 2733106 : dtuple->bt_columns[i].bv_serialize = NULL;
525 2733106 : dtuple->bt_columns[i].bv_context = dtuple->bt_context;
526 :
2193 alvherre 527 2733106 : currdatum += sizeof(Datum) * brdesc->bd_info[i]->oi_nstored;
528 : }
529 :
530 105954 : return dtuple;
531 : }
532 :
533 : /*
534 : * Convert a BrinTuple back to a BrinMemTuple. This is the reverse of
535 : * brin_form_tuple.
536 : *
537 : * As an optimization, the caller can pass a previously allocated 'dMemtuple'.
538 : * This avoids having to allocate it here, which can be useful when this
539 : * function is called many times in a loop. It is caller's responsibility
540 : * that the given BrinMemTuple matches what we need here.
541 : *
542 : * Note we don't need the "on disk tupdesc" here; we rely on our own routine to
543 : * deconstruct the tuple from the on-disk format.
544 : */
545 : BrinMemTuple *
546 102034 : brin_deform_tuple(BrinDesc *brdesc, BrinTuple *tuple, BrinMemTuple *dMemtuple)
547 : {
548 : BrinMemTuple *dtup;
549 : Datum *values;
550 : bool *allnulls;
551 : bool *hasnulls;
552 : char *tp;
553 : bits8 *nullbits;
554 : int keyno;
555 : int valueno;
556 : MemoryContext oldcxt;
557 :
558 102034 : dtup = dMemtuple ? brin_memtuple_initialize(dMemtuple, brdesc) :
559 8107 : brin_new_memtuple(brdesc);
560 :
3075 561 102034 : if (BrinTupleIsPlaceholder(tuple))
3075 alvherre 562 UBC 0 : dtup->bt_placeholder = true;
3075 alvherre 563 CBC 102034 : dtup->bt_blkno = tuple->bt_blkno;
564 :
2193 565 102034 : values = dtup->bt_values;
566 102034 : allnulls = dtup->bt_allnulls;
567 102034 : hasnulls = dtup->bt_hasnulls;
568 :
3075 569 102034 : tp = (char *) tuple + BrinTupleDataOffset(tuple);
570 :
571 102034 : if (BrinTupleHasNulls(tuple))
572 11511 : nullbits = (bits8 *) ((char *) tuple + SizeOfBrinTuple);
573 : else
574 90523 : nullbits = NULL;
575 102034 : brin_deconstruct_tuple(brdesc,
576 102034 : tp, nullbits, BrinTupleHasNulls(tuple),
577 : values, allnulls, hasnulls);
578 :
579 : /*
580 : * Iterate to assign each of the values to the corresponding item in the
581 : * values array of each column. The copies occur in the tuple's context.
582 : */
583 102034 : oldcxt = MemoryContextSwitchTo(dtup->bt_context);
584 2783495 : for (valueno = 0, keyno = 0; keyno < brdesc->bd_tupdesc->natts; keyno++)
585 : {
586 : int i;
587 :
588 2681461 : if (allnulls[keyno])
589 : {
590 5508 : valueno += brdesc->bd_info[keyno]->oi_nstored;
591 5508 : continue;
592 : }
593 :
594 : /*
595 : * We would like to skip datumCopy'ing the values datum in some cases,
596 : * caller permitting ...
597 : */
598 7909500 : for (i = 0; i < brdesc->bd_info[keyno]->oi_nstored; i++)
599 5233547 : dtup->bt_columns[keyno].bv_values[i] =
600 5233547 : datumCopy(values[valueno++],
2894 601 5233547 : brdesc->bd_info[keyno]->oi_typcache[i]->typbyval,
602 5233547 : brdesc->bd_info[keyno]->oi_typcache[i]->typlen);
603 :
3075 604 2675953 : dtup->bt_columns[keyno].bv_hasnulls = hasnulls[keyno];
605 2675953 : dtup->bt_columns[keyno].bv_allnulls = false;
606 :
744 tomas.vondra 607 2675953 : dtup->bt_columns[keyno].bv_mem_value = PointerGetDatum(NULL);
608 2675953 : dtup->bt_columns[keyno].bv_serialize = NULL;
609 2675953 : dtup->bt_columns[keyno].bv_context = dtup->bt_context;
610 : }
611 :
3075 alvherre 612 102034 : MemoryContextSwitchTo(oldcxt);
613 :
614 102034 : return dtup;
615 : }
616 :
617 : /*
618 : * brin_deconstruct_tuple
619 : * Guts of attribute extraction from an on-disk BRIN tuple.
620 : *
621 : * Its arguments are:
622 : * brdesc BRIN descriptor for the stored tuple
623 : * tp pointer to the tuple data area
624 : * nullbits pointer to the tuple nulls bitmask
625 : * nulls "has nulls" bit in tuple infomask
626 : * values output values, array of size brdesc->bd_totalstored
627 : * allnulls output "allnulls", size brdesc->bd_tupdesc->natts
628 : * hasnulls output "hasnulls", size brdesc->bd_tupdesc->natts
629 : *
630 : * Output arrays must have been allocated by caller.
631 : */
632 : static inline void
633 102034 : brin_deconstruct_tuple(BrinDesc *brdesc,
634 : char *tp, bits8 *nullbits, bool nulls,
635 : Datum *values, bool *allnulls, bool *hasnulls)
636 : {
637 : int attnum;
638 : int stored;
639 : TupleDesc diskdsc;
640 : long off;
641 :
642 : /*
643 : * First iterate to natts to obtain both null flags for each attribute.
644 : * Note that we reverse the sense of the att_isnull test, because we store
645 : * 1 for a null value (rather than a 1 for a not null value as is the
646 : * att_isnull convention used elsewhere.) See brin_form_tuple.
647 : */
648 2783495 : for (attnum = 0; attnum < brdesc->bd_tupdesc->natts; attnum++)
649 : {
650 : /*
651 : * the "all nulls" bit means that all values in the page range for
652 : * this column are nulls. Therefore there are no values in the tuple
653 : * data area.
654 : */
655 2681461 : allnulls[attnum] = nulls && !att_isnull(attnum, nullbits);
656 :
657 : /*
658 : * the "has nulls" bit means that some tuples have nulls, but others
659 : * have not-null values. Therefore we know the tuple contains data
660 : * for this column.
661 : *
662 : * The hasnulls bits follow the allnulls bits in the same bitmask.
663 : */
664 2681461 : hasnulls[attnum] =
665 2681461 : nulls && !att_isnull(brdesc->bd_tupdesc->natts + attnum, nullbits);
666 : }
667 :
668 : /*
669 : * Iterate to obtain each attribute's stored values. Note that since we
670 : * may reuse attribute entries for more than one column, we cannot cache
671 : * offsets here.
672 : */
673 102034 : diskdsc = brtuple_disk_tupdesc(brdesc);
674 102034 : stored = 0;
675 102034 : off = 0;
676 2783495 : for (attnum = 0; attnum < brdesc->bd_tupdesc->natts; attnum++)
677 : {
678 : int datumno;
679 :
680 2681461 : if (allnulls[attnum])
681 : {
682 5508 : stored += brdesc->bd_info[attnum]->oi_nstored;
683 5508 : continue;
684 : }
685 :
686 2675953 : for (datumno = 0;
687 7909500 : datumno < brdesc->bd_info[attnum]->oi_nstored;
688 5233547 : datumno++)
689 : {
2058 andres 690 5233547 : Form_pg_attribute thisatt = TupleDescAttr(diskdsc, stored);
691 :
3075 alvherre 692 5233547 : if (thisatt->attlen == -1)
693 : {
694 1844085 : off = att_align_pointer(off, thisatt->attalign, -1,
695 : tp + off);
696 : }
697 : else
698 : {
699 : /* not varlena, so safe to use att_align_nominal */
700 3389462 : off = att_align_nominal(off, thisatt->attalign);
701 : }
702 :
703 5233547 : values[stored++] = fetchatt(thisatt, tp + off);
704 :
705 5233547 : off = att_addlength_pointer(off, thisatt->attlen, tp + off);
706 : }
707 : }
708 102034 : }
|