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