MemTuple ExecCopySlotMemTupleTo(TupleTableSlot *slot, MemoryContext pctxt, char *dest, unsigned int *len) { uint32 dumlen; MemTuple mtup = NULL; Assert(!TupIsNull(slot)); Assert(slot->tts_mt_bind); if(!len) len = &dumlen; if (TupHasMemTuple(slot)) { mtup = memtuple_copy_to(slot->PRIVATE_tts_memtuple, slot->tts_mt_bind, (MemTuple) dest, len); if(mtup || !pctxt) return mtup; mtup = (MemTuple) ctxt_alloc(pctxt, *len); mtup = memtuple_copy_to(slot->PRIVATE_tts_memtuple, slot->tts_mt_bind, mtup, len); Assert(mtup); return mtup; } slot_getallattrs(slot); mtup = memtuple_form_to(slot->tts_mt_bind, slot_get_values(slot), slot_get_isnull(slot), (MemTuple) dest, len, false); if(mtup || !pctxt) return mtup; mtup = (MemTuple) ctxt_alloc(pctxt, *len); mtup = memtuple_form_to(slot->tts_mt_bind, slot_get_values(slot), slot_get_isnull(slot), mtup, len, false); Assert(mtup); return mtup; }
/* XXX * This function is not very efficient. We should detech if we can modify * the memtuple inline so no deform/form is needed */ void ExecModifyMemTuple(TupleTableSlot *slot, Datum *values, bool *isnull, bool *doRepl) { int i; MemTuple mtup; uint32 tuplen; Assert(slot->PRIVATE_tts_memtuple); /* First, get all the attrs. Note we set PRIVATE_tts_nvalid to 0 * so we force the attrs are from memtuple */ slot->PRIVATE_tts_nvalid = 0; slot_getallattrs(slot); /* Next, we construct a new memtuple, on the htup buf to avoid palloc */ slot->PRIVATE_tts_heaptuple = NULL; for(i = 0; i<slot->tts_tupleDescriptor->natts; ++i) { if(doRepl[i]) { slot->PRIVATE_tts_values[i] = values[i]; slot->PRIVATE_tts_isnull[i] = isnull[i]; } } tuplen = slot->PRIVATE_tts_htup_buf_len; mtup = memtuple_form_to(slot->tts_mt_bind, slot->PRIVATE_tts_values, slot->PRIVATE_tts_isnull, slot->PRIVATE_tts_htup_buf, &tuplen, false); if(!mtup) { slot->PRIVATE_tts_htup_buf = MemoryContextAlloc(slot->tts_mcxt, tuplen); slot->PRIVATE_tts_htup_buf_len = tuplen; mtup = memtuple_form_to(slot->tts_mt_bind, slot->PRIVATE_tts_values, slot->PRIVATE_tts_isnull, slot->PRIVATE_tts_htup_buf, &tuplen, false); Assert(mtup); } /* Check if we need to free this mem tuple */ if(TupShouldFree(slot) && slot->PRIVATE_tts_memtuple && slot->PRIVATE_tts_memtuple != slot->PRIVATE_tts_mtup_buf ) pfree(slot->PRIVATE_tts_memtuple); slot->PRIVATE_tts_memtuple = mtup; /* swap mtup_buf and htup_buf stuff */ mtup = (MemTuple) slot->PRIVATE_tts_mtup_buf; tuplen = slot->PRIVATE_tts_mtup_buf_len; slot->PRIVATE_tts_mtup_buf = slot->PRIVATE_tts_htup_buf; slot->PRIVATE_tts_mtup_buf_len = slot->PRIVATE_tts_htup_buf_len; slot->PRIVATE_tts_htup_buf = (void *) mtup; slot->PRIVATE_tts_htup_buf_len = tuplen; /* don't forget to reset PRIVATE_tts_nvalid, because we modified the memtuple */ slot->PRIVATE_tts_nvalid = 0; }
/* -------------------------------- * ExecFetchSlotMinimalTuple * Fetch the slot's minimal physical tuple. * * If the slot contains a virtual tuple, we convert it to minimal * physical form. The slot retains ownership of the physical tuple. * Likewise, if it contains a regular tuple we convert to minimal form. * * As above, the result must be treated as read-only. * -------------------------------- */ MemTuple ExecFetchSlotMemTuple(TupleTableSlot *slot, bool inline_toast) { MemTuple newTuple; MemTuple oldTuple = NULL; uint32 tuplen; Assert(!TupIsNull(slot)); Assert(slot->tts_mt_bind); if(slot->PRIVATE_tts_memtuple) { if(!inline_toast || !memtuple_get_hasext(slot->PRIVATE_tts_memtuple, slot->tts_mt_bind)) return slot->PRIVATE_tts_memtuple; oldTuple = slot->PRIVATE_tts_mtup_buf; slot->PRIVATE_tts_mtup_buf = NULL; slot->PRIVATE_tts_mtup_buf_len = 0; } slot_getallattrs(slot); tuplen = slot->PRIVATE_tts_mtup_buf_len; newTuple = memtuple_form_to(slot->tts_mt_bind, slot_get_values(slot), slot_get_isnull(slot), (MemTuple) slot->PRIVATE_tts_mtup_buf, &tuplen, inline_toast); if(!newTuple) { if(slot->PRIVATE_tts_mtup_buf) pfree(slot->PRIVATE_tts_mtup_buf); slot->PRIVATE_tts_mtup_buf = MemoryContextAlloc(slot->tts_mcxt, tuplen); slot->PRIVATE_tts_mtup_buf_len = tuplen; newTuple = memtuple_form_to(slot->tts_mt_bind, slot_get_values(slot), slot_get_isnull(slot), (MemTuple) slot->PRIVATE_tts_mtup_buf, &tuplen, inline_toast); } Assert(newTuple); slot->PRIVATE_tts_memtuple = newTuple; if(oldTuple) pfree(oldTuple); return newTuple; }
/* * Similar to tuplestore_puttuple(), but work from values + nulls arrays. * This avoids an extra tuple-construction operation. */ void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, Datum *values, bool *isnull) { MemoryContext oldcxt = MemoryContextSwitchTo(state->context); if (!state->mt_bind) { state->mt_bind = create_memtuple_binding(tdesc); Assert(state->mt_bind); } MemTuple tuple = memtuple_form_to(state->mt_bind, values, isnull, NULL, NULL, false); USEMEM(state, GetMemoryChunkSpace(tuple)); tuplestore_puttuple_common(state, (void *) tuple); MemoryContextSwitchTo(oldcxt); }
/* -------------------------------- * ExecCopySlotMinimalTuple * Obtain a copy of a slot's minimal physical tuple. The copy is * palloc'd in the current memory context. * -------------------------------- */ MemTuple ExecCopySlotMemTuple(TupleTableSlot *slot) { /* * sanity checks */ Assert(!TupIsNull(slot)); Assert(slot->tts_mt_bind); /* * If we have a physical tuple then just copy it. */ if (slot->PRIVATE_tts_memtuple) return memtuple_copy_to(slot->PRIVATE_tts_memtuple, slot->tts_mt_bind, NULL, NULL); slot_getallattrs(slot); /* * Otherwise we need to build a tuple from the Datum array. */ return memtuple_form_to(slot->tts_mt_bind, slot_get_values(slot), slot_get_isnull(slot), NULL, 0, false); }
HeapTuple toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, MemTupleBinding *pbind, int toast_tuple_target, bool isFrozen) { HeapTuple result_tuple; TupleDesc tupleDesc; Form_pg_attribute *att; int numAttrs; int i; bool need_change = false; bool need_free = false; bool need_delold = false; bool has_nulls = false; Size maxDataLen; char toast_action[MaxHeapAttributeNumber]; bool toast_isnull[MaxHeapAttributeNumber]; bool toast_oldisnull[MaxHeapAttributeNumber]; Datum toast_values[MaxHeapAttributeNumber]; Datum toast_oldvalues[MaxHeapAttributeNumber]; int32 toast_sizes[MaxHeapAttributeNumber]; bool toast_free[MaxHeapAttributeNumber]; bool toast_delold[MaxHeapAttributeNumber]; bool ismemtuple = is_heaptuple_memtuple(newtup); AssertImply(ismemtuple, pbind); AssertImply(!ismemtuple, !pbind); AssertImply(ismemtuple && oldtup, is_heaptuple_memtuple(oldtup)); Assert(toast_tuple_target > 0); /* * We should only ever be called for tuples of plain relations --- * recursing on a toast rel is bad news. */ //Assert(rel->rd_rel->relkind == RELKIND_RELATION); if (rel->rd_rel->relkind != RELKIND_RELATION) elog(LOG,"Why are we toasting a non-relation! %c ",rel->rd_rel->relkind); /* * Get the tuple descriptor and break down the tuple(s) into fields. */ tupleDesc = rel->rd_att; att = tupleDesc->attrs; numAttrs = tupleDesc->natts; Assert(numAttrs <= MaxHeapAttributeNumber); if(ismemtuple) memtuple_deform((MemTuple) newtup, pbind, toast_values, toast_isnull); else heap_deform_tuple(newtup, tupleDesc, toast_values, toast_isnull); if (oldtup != NULL) { if(ismemtuple) memtuple_deform((MemTuple) oldtup, pbind, toast_oldvalues, toast_oldisnull); else heap_deform_tuple(oldtup, tupleDesc, toast_oldvalues, toast_oldisnull); } /* ---------- * Then collect information about the values given * * NOTE: toast_action[i] can have these values: * ' ' default handling * 'p' already processed --- don't touch it * 'x' incompressible, but OK to move off * * NOTE: toast_sizes[i] is only made valid for varlena attributes with * toast_action[i] different from 'p'. * ---------- */ memset(toast_action, ' ', numAttrs * sizeof(char)); memset(toast_free, 0, numAttrs * sizeof(bool)); memset(toast_delold, 0, numAttrs * sizeof(bool)); for (i = 0; i < numAttrs; i++) { varattrib *old_value; varattrib *new_value; if (oldtup != NULL) { /* * For UPDATE get the old and new values of this attribute */ old_value = (varattrib *) DatumGetPointer(toast_oldvalues[i]); new_value = (varattrib *) DatumGetPointer(toast_values[i]); /* * If the old value is an external stored one, check if it has * changed so we have to delete it later. */ if (att[i]->attlen == -1 && !toast_oldisnull[i] && VARATT_IS_EXTERNAL(old_value)) { if (toast_isnull[i] || !VARATT_IS_EXTERNAL(new_value) || memcmp((char *) old_value, (char *) new_value, VARSIZE_EXTERNAL(old_value)) != 0) { /* * The old external stored value isn't needed any more * after the update */ toast_delold[i] = true; need_delold = true; } else { /* * This attribute isn't changed by this update so we reuse * the original reference to the old value in the new * tuple. */ toast_action[i] = 'p'; continue; } } } else { /* * For INSERT simply get the new value */ new_value = (varattrib *) DatumGetPointer(toast_values[i]); } /* * Handle NULL attributes */ if (toast_isnull[i]) { toast_action[i] = 'p'; has_nulls = true; continue; } /* * Now look at varlena attributes */ if (att[i]->attlen == -1) { /* * If the table's attribute says PLAIN always, force it so. */ if (att[i]->attstorage == 'p') toast_action[i] = 'p'; /* * We took care of UPDATE above, so any external value we find * still in the tuple must be someone else's we cannot reuse. * Fetch it back (without decompression, unless we are forcing * PLAIN storage). If necessary, we'll push it out as a new * external value below. */ if (VARATT_IS_EXTERNAL(new_value)) { if (att[i]->attstorage == 'p') new_value = (varattrib *)heap_tuple_untoast_attr((struct varlena *)new_value); else new_value = (varattrib *)heap_tuple_fetch_attr((struct varlena *)new_value); toast_values[i] = PointerGetDatum(new_value); toast_free[i] = true; need_change = true; need_free = true; } /* * Remember the size of this attribute */ toast_sizes[i] = VARSIZE_ANY(new_value); } else { /* * Not a varlena attribute, plain storage always */ toast_action[i] = 'p'; } } /* ---------- * Compress and/or save external until data fits into target length * * 1: Inline compress attributes with attstorage 'x', and store very * large attributes with attstorage 'x' or 'e' external immediately * 2: Store attributes with attstorage 'x' or 'e' external * 3: Inline compress attributes with attstorage 'm' * 4: Store attributes with attstorage 'm' external * ---------- */ if(!ismemtuple) { /* compute header overhead --- this should match heap_form_tuple() */ maxDataLen = offsetof(HeapTupleHeaderData, t_bits); if (has_nulls) maxDataLen += BITMAPLEN(numAttrs); if (newtup->t_data->t_infomask & HEAP_HASOID) maxDataLen += sizeof(Oid); maxDataLen = MAXALIGN(maxDataLen); Assert(maxDataLen == newtup->t_data->t_hoff); /* now convert to a limit on the tuple data size */ maxDataLen = toast_tuple_target - maxDataLen; } else maxDataLen = toast_tuple_target; /* * Look for attributes with attstorage 'x' to compress. Also find large * attributes with attstorage 'x' or 'e', and store them external. */ while (compute_dest_tuplen(tupleDesc, pbind, has_nulls, toast_values, toast_isnull) > maxDataLen) { int biggest_attno = -1; int32 biggest_size = MAXALIGN(sizeof(varattrib)); Datum old_value; Datum new_value; /* * Search for the biggest yet unprocessed internal attribute */ for (i = 0; i < numAttrs; i++) { if (toast_action[i] != ' ') continue; if (VARATT_IS_EXTERNAL_D(toast_values[i])) continue; if (VARATT_IS_COMPRESSED_D(toast_values[i])) continue; if (att[i]->attstorage != 'x') continue; if (toast_sizes[i] > biggest_size) { biggest_attno = i; biggest_size = toast_sizes[i]; } } if (biggest_attno < 0) break; /* * Attempt to compress it inline, if it has attstorage 'x' */ i = biggest_attno; old_value = toast_values[i]; new_value = toast_compress_datum(old_value); if (DatumGetPointer(new_value) != NULL) { /* successful compression */ if (toast_free[i]) pfree(DatumGetPointer(old_value)); toast_values[i] = new_value; toast_free[i] = true; toast_sizes[i] = VARSIZE_D(toast_values[i]); need_change = true; need_free = true; } else { /* * incompressible data, ignore on subsequent compression passes */ toast_action[i] = 'x'; } } /* * Second we look for attributes of attstorage 'x' or 'e' that are still * inline. */ while (compute_dest_tuplen(tupleDesc, pbind, has_nulls, toast_values, toast_isnull) > maxDataLen && rel->rd_rel->reltoastrelid != InvalidOid) { int biggest_attno = -1; int32 biggest_size = MAXALIGN(sizeof(varattrib)); Datum old_value; /*------ * Search for the biggest yet inlined attribute with * attstorage equals 'x' or 'e' *------ */ for (i = 0; i < numAttrs; i++) { if (toast_action[i] == 'p') continue; if (VARATT_IS_EXTERNAL_D(toast_values[i])) continue; if (att[i]->attstorage != 'x' && att[i]->attstorage != 'e') continue; if (toast_sizes[i] > biggest_size) { biggest_attno = i; biggest_size = toast_sizes[i]; } } if (biggest_attno < 0) break; /* * Store this external */ i = biggest_attno; old_value = toast_values[i]; toast_action[i] = 'p'; toast_values[i] = toast_save_datum(rel, toast_values[i], isFrozen); if (toast_free[i]) pfree(DatumGetPointer(old_value)); toast_free[i] = true; need_change = true; need_free = true; } /* * Round 3 - this time we take attributes with storage 'm' into * compression */ while (compute_dest_tuplen(tupleDesc, pbind, has_nulls, toast_values, toast_isnull) > maxDataLen) { int biggest_attno = -1; int32 biggest_size = MAXALIGN(sizeof(varattrib)); Datum old_value; Datum new_value; /* * Search for the biggest yet uncompressed internal attribute */ for (i = 0; i < numAttrs; i++) { if (toast_action[i] != ' ') continue; if (VARATT_IS_EXTERNAL_D(toast_values[i])) continue; /* can't happen, toast_action would be 'p' */ if (VARATT_IS_COMPRESSED_D(toast_values[i])) continue; if (att[i]->attstorage != 'm') continue; if (toast_sizes[i] > biggest_size) { biggest_attno = i; biggest_size = toast_sizes[i]; } } if (biggest_attno < 0) break; /* * Attempt to compress it inline */ i = biggest_attno; old_value = toast_values[i]; new_value = toast_compress_datum(old_value); if (DatumGetPointer(new_value) != NULL) { /* successful compression */ if (toast_free[i]) pfree(DatumGetPointer(old_value)); toast_values[i] = new_value; toast_free[i] = true; toast_sizes[i] = VARSIZE_D(toast_values[i]); need_change = true; need_free = true; } else { /* incompressible, ignore on subsequent compression passes */ toast_action[i] = 'x'; } } /* * Finally we store attributes of type 'm' external, if possible. */ while (compute_dest_tuplen(tupleDesc, pbind, has_nulls, toast_values, toast_isnull) > maxDataLen && rel->rd_rel->reltoastrelid != InvalidOid) { int biggest_attno = -1; int32 biggest_size = MAXALIGN(sizeof(varattrib)); Datum old_value; /*-------- * Search for the biggest yet inlined attribute with * attstorage = 'm' *-------- */ for (i = 0; i < numAttrs; i++) { if (toast_action[i] == 'p') continue; if (VARATT_IS_EXTERNAL_D(toast_values[i])) continue; /* can't happen, toast_action would be 'p' */ if (att[i]->attstorage != 'm') continue; if (toast_sizes[i] > biggest_size) { biggest_attno = i; biggest_size = toast_sizes[i]; } } if (biggest_attno < 0) break; /* * Store this external */ i = biggest_attno; old_value = toast_values[i]; toast_action[i] = 'p'; toast_values[i] = toast_save_datum(rel, toast_values[i], isFrozen); if (toast_free[i]) pfree(DatumGetPointer(old_value)); toast_free[i] = true; need_change = true; need_free = true; } /* XXX Maybe we should check here for any compressed inline attributes that * didn't save enough to warrant keeping. In particular attributes whose * rawsize is < 128 bytes and didn't save at least 3 bytes... or even maybe * more given alignment issues */ /* * In the case we toasted any values, we need to build a new heap tuple * with the changed values. */ if (need_change) { if(ismemtuple) result_tuple = (HeapTuple) memtuple_form_to(pbind, toast_values, toast_isnull, NULL, NULL, false); else { HeapTupleHeader olddata = newtup->t_data; HeapTupleHeader new_data; int32 new_len; /* * Calculate the new size of the tuple. Header size should not * change, but data size might. */ new_len = offsetof(HeapTupleHeaderData, t_bits); if (has_nulls) new_len += BITMAPLEN(numAttrs); if (olddata->t_infomask & HEAP_HASOID) new_len += sizeof(Oid); new_len = MAXALIGN(new_len); Assert(new_len == olddata->t_hoff); new_len += heap_compute_data_size(tupleDesc, toast_values, toast_isnull); /* * Allocate and zero the space needed, and fill HeapTupleData fields. */ result_tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + new_len); result_tuple->t_len = new_len; result_tuple->t_self = newtup->t_self; new_data = (HeapTupleHeader) ((char *) result_tuple + HEAPTUPLESIZE); result_tuple->t_data = new_data; /* * Put the existing tuple header and the changed values into place */ memcpy(new_data, olddata, olddata->t_hoff); heap_fill_tuple(tupleDesc, toast_values, toast_isnull, (char *) new_data + olddata->t_hoff, &(new_data->t_infomask), has_nulls ? new_data->t_bits : NULL); } } else result_tuple = newtup; /* * Free allocated temp values */ if (need_free) for (i = 0; i < numAttrs; i++) if (toast_free[i]) pfree(DatumGetPointer(toast_values[i])); /* * Delete external values from the old tuple */ if (need_delold) for (i = 0; i < numAttrs; i++) if (toast_delold[i]) toast_delete_datum(rel, toast_oldvalues[i]); return result_tuple; }