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