📄 tuptoaster.c
字号:
/*------------------------------------------------------------------------- * * tuptoaster.c * Support routines for external and compressed storage of * variable size attributes. * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.53.2.3 2006/01/17 17:33:20 tgl Exp $ * * * INTERFACE ROUTINES * toast_insert_or_update - * Try to make a given tuple fit into one page by compressing * or moving off attributes * * toast_delete - * Reclaim toast storage when a tuple is deleted * * heap_tuple_untoast_attr - * Fetch back a given value from the "secondary" relation * *------------------------------------------------------------------------- */#include "postgres.h"#include <unistd.h>#include <fcntl.h>#include "access/heapam.h"#include "access/genam.h"#include "access/tuptoaster.h"#include "catalog/catalog.h"#include "utils/rel.h"#include "utils/builtins.h"#include "utils/fmgroids.h"#include "utils/pg_lzcompress.h"#include "utils/typcache.h"#undef TOAST_DEBUGstatic void toast_delete_datum(Relation rel, Datum value);static Datum toast_save_datum(Relation rel, Datum value);static varattrib *toast_fetch_datum(varattrib *attr);static varattrib *toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length);/* ---------- * heap_tuple_fetch_attr - * * Public entry point to get back a toasted value * external storage (possibly still in compressed format). * ---------- */varattrib *heap_tuple_fetch_attr(varattrib *attr){ varattrib *result; if (VARATT_IS_EXTERNAL(attr)) { /* * This is an external stored plain value */ result = toast_fetch_datum(attr); } else { /* * This is a plain value inside of the main tuple - why am I called? */ result = attr; } return result;}/* ---------- * heap_tuple_untoast_attr - * * Public entry point to get back a toasted value from compression * or external storage. * ---------- */varattrib *heap_tuple_untoast_attr(varattrib *attr){ varattrib *result; if (VARATT_IS_EXTERNAL(attr)) { if (VARATT_IS_COMPRESSED(attr)) { /* ---------- * This is an external stored compressed value * Fetch it from the toast heap and decompress. * ---------- */ varattrib *tmp; tmp = toast_fetch_datum(attr); result = (varattrib *) palloc(attr->va_content.va_external.va_rawsize + VARHDRSZ); VARATT_SIZEP(result) = attr->va_content.va_external.va_rawsize + VARHDRSZ; pglz_decompress((PGLZ_Header *) tmp, VARATT_DATA(result)); pfree(tmp); } else { /* * This is an external stored plain value */ result = toast_fetch_datum(attr); } } else if (VARATT_IS_COMPRESSED(attr)) { /* * This is a compressed value inside of the main tuple */ result = (varattrib *) palloc(attr->va_content.va_compressed.va_rawsize + VARHDRSZ); VARATT_SIZEP(result) = attr->va_content.va_compressed.va_rawsize + VARHDRSZ; pglz_decompress((PGLZ_Header *) attr, VARATT_DATA(result)); } else /* * This is a plain value inside of the main tuple - why am I called? */ return attr; return result;}/* ---------- * heap_tuple_untoast_attr_slice - * * Public entry point to get back part of a toasted value * from compression or external storage. * ---------- */varattrib *heap_tuple_untoast_attr_slice(varattrib *attr, int32 sliceoffset, int32 slicelength){ varattrib *preslice; varattrib *result; int32 attrsize; if (VARATT_IS_COMPRESSED(attr)) { varattrib *tmp; if (VARATT_IS_EXTERNAL(attr)) tmp = toast_fetch_datum(attr); else { tmp = attr; /* compressed in main tuple */ } preslice = (varattrib *) palloc(attr->va_content.va_external.va_rawsize + VARHDRSZ); VARATT_SIZEP(preslice) = attr->va_content.va_external.va_rawsize + VARHDRSZ; pglz_decompress((PGLZ_Header *) tmp, VARATT_DATA(preslice)); if (tmp != attr) pfree(tmp); } else { /* Plain value */ if (VARATT_IS_EXTERNAL(attr)) { /* fast path */ return (toast_fetch_datum_slice(attr, sliceoffset, slicelength)); } else preslice = attr; } /* slicing of datum for compressed cases and plain value */ attrsize = VARSIZE(preslice) - VARHDRSZ; if (sliceoffset >= attrsize) { sliceoffset = 0; slicelength = 0; } if (((sliceoffset + slicelength) > attrsize) || slicelength < 0) slicelength = attrsize - sliceoffset; result = (varattrib *) palloc(slicelength + VARHDRSZ); VARATT_SIZEP(result) = slicelength + VARHDRSZ; memcpy(VARDATA(result), VARDATA(preslice) + sliceoffset, slicelength); if (preslice != attr) pfree(preslice); return result;}/* ---------- * toast_raw_datum_size - * * Return the raw (detoasted) size of a varlena datum * ---------- */Sizetoast_raw_datum_size(Datum value){ varattrib *attr = (varattrib *) DatumGetPointer(value); Size result; if (VARATT_IS_COMPRESSED(attr)) { /* * va_rawsize shows the original data size, whether the datum is * external or not. */ result = attr->va_content.va_compressed.va_rawsize + VARHDRSZ; } else if (VARATT_IS_EXTERNAL(attr)) { /* * an uncompressed external attribute has rawsize including the header * (not too consistent!) */ result = attr->va_content.va_external.va_rawsize; } else { /* plain untoasted datum */ result = VARSIZE(attr); } return result;}/* ---------- * toast_datum_size * * Return the physical storage size (possibly compressed) of a varlena datum * ---------- */Sizetoast_datum_size(Datum value){ varattrib *attr = (varattrib *) DatumGetPointer(value); Size result; if (VARATT_IS_EXTERNAL(attr)) { /* * Attribute is stored externally - return the extsize whether * compressed or not. We do not count the size of the toast pointer * ... should we? */ result = attr->va_content.va_external.va_extsize; } else { /* * Attribute is stored inline either compressed or not, just calculate * the size of the datum in either case. */ result = VARSIZE(attr); } return result;}/* ---------- * toast_delete - * * Cascaded delete toast-entries on DELETE * ---------- */voidtoast_delete(Relation rel, HeapTuple oldtup){ TupleDesc tupleDesc; Form_pg_attribute *att; int numAttrs; int i; Datum toast_values[MaxHeapAttributeNumber]; bool toast_isnull[MaxHeapAttributeNumber]; /* * Get the tuple descriptor and break down the tuple into fields. * * NOTE: it's debatable whether to use heap_deformtuple() here or just * heap_getattr() only the varlena columns. The latter could win if there * are few varlena columns and many non-varlena ones. However, * heap_deformtuple costs only O(N) while the heap_getattr way would cost * O(N^2) if there are many varlena columns, so it seems better to err on * the side of linear cost. (We won't even be here unless there's at * least one varlena column, by the way.) */ tupleDesc = rel->rd_att; att = tupleDesc->attrs; numAttrs = tupleDesc->natts; Assert(numAttrs <= MaxHeapAttributeNumber); heap_deform_tuple(oldtup, tupleDesc, toast_values, toast_isnull); /* * Check for external stored attributes and delete them from the secondary * relation. */ for (i = 0; i < numAttrs; i++) { if (att[i]->attlen == -1) { Datum value = toast_values[i]; if (!toast_isnull[i] && VARATT_IS_EXTERNAL(value)) toast_delete_datum(rel, value); } }}/* ---------- * toast_insert_or_update - * * Delete no-longer-used toast-entries and create new ones to * make the new tuple fit on INSERT or UPDATE * * Inputs: * newtup: the candidate new tuple to be inserted * oldtup: the old row version for UPDATE, or NULL for INSERT * Result: * either newtup if no toasting is needed, or a palloc'd modified tuple * that is what should actually get stored * * NOTE: neither newtup nor oldtup will be modified. This is a change * from the pre-8.1 API of this routine. * ---------- */HeapTupletoast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup){ 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]; /* * 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); heap_deform_tuple(newtup, tupleDesc, toast_values, toast_isnull); if (oldtup != NULL) 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) || old_value->va_content.va_external.va_valueid != new_value->va_content.va_external.va_valueid || old_value->va_content.va_external.va_toastrelid != new_value->va_content.va_external.va_toastrelid) { /* * 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'; toast_sizes[i] = VARATT_SIZE(toast_values[i]); 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. * Expand it to plain (and, probably, toast it again below). */ if (VARATT_IS_EXTERNAL(new_value)) { new_value = heap_tuple_untoast_attr(new_value); toast_values[i] = PointerGetDatum(new_value); toast_free[i] = true; need_change = true; need_free = true;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -