aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2004-06-05 01:55:05 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2004-06-05 01:55:05 +0000
commitae93e5fd6e8a7e2321e87d23165d9d7660cde598 (patch)
treea8e22c4835283c61e137285ba2dabfe3feead1a9 /src/backend/access
parent8f2ea8b7b53a02078ba0393e6892ac5356a3631e (diff)
downloadpostgresql-ae93e5fd6e8a7e2321e87d23165d9d7660cde598.tar.gz
postgresql-ae93e5fd6e8a7e2321e87d23165d9d7660cde598.zip
Make the world very nearly safe for composite-type columns in tables.
1. Solve the problem of not having TOAST references hiding inside composite values by establishing the rule that toasting only goes one level deep: a tuple can contain toasted fields, but a composite-type datum that is to be inserted into a tuple cannot. Enforcing this in heap_formtuple is relatively cheap and it avoids a large increase in the cost of running the tuptoaster during final storage of a row. 2. Fix some interesting problems in expansion of inherited queries that reference whole-row variables. We never really did this correctly before, but it's now relatively painless to solve by expanding the parent's whole-row Var into a RowExpr() selecting the proper columns from the child. If you dike out the preventive check in CheckAttributeType(), composite-type columns now seem to actually work. However, we surely cannot ship them like this --- without I/O for composite types, you can't get pg_dump to dump tables containing them. So a little more work still to do.
Diffstat (limited to 'src/backend/access')
-rw-r--r--src/backend/access/common/heaptuple.c35
-rw-r--r--src/backend/access/heap/tuptoaster.c133
2 files changed, 157 insertions, 11 deletions
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 266cf3bdcc2..88a9631e8cc 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.91 2004/06/04 20:35:21 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.92 2004/06/05 01:55:04 tgl Exp $
*
* NOTES
* The old interface functions have been converted to macros
@@ -21,6 +21,7 @@
#include "postgres.h"
#include "access/heapam.h"
+#include "access/tuptoaster.h"
#include "catalog/pg_type.h"
@@ -567,8 +568,9 @@ heap_formtuple(TupleDesc tupleDescriptor,
unsigned long len;
int hoff;
bool hasnull = false;
- int i;
+ Form_pg_attribute *att = tupleDescriptor->attrs;
int numberOfAttributes = tupleDescriptor->natts;
+ int i;
if (numberOfAttributes > MaxTupleAttributeNumber)
ereport(ERROR,
@@ -577,17 +579,34 @@ heap_formtuple(TupleDesc tupleDescriptor,
numberOfAttributes, MaxTupleAttributeNumber)));
/*
- * Determine total space needed
+ * Check for nulls and embedded tuples; expand any toasted attributes
+ * in embedded tuples. This preserves the invariant that toasting can
+ * only go one level deep.
+ *
+ * We can skip calling toast_flatten_tuple_attribute() if the attribute
+ * couldn't possibly be of composite type. All composite datums are
+ * varlena and have alignment 'd'; furthermore they aren't arrays.
+ * Also, if an attribute is already toasted, it must have been sent to
+ * disk already and so cannot contain toasted attributes.
*/
for (i = 0; i < numberOfAttributes; i++)
{
if (nulls[i] != ' ')
- {
hasnull = true;
- break;
+ else if (att[i]->attlen == -1 &&
+ att[i]->attalign == 'd' &&
+ att[i]->attndims == 0 &&
+ !VARATT_IS_EXTENDED(values[i]))
+ {
+ values[i] = toast_flatten_tuple_attribute(values[i],
+ att[i]->atttypid,
+ att[i]->atttypmod);
}
}
+ /*
+ * Determine total space needed
+ */
len = offsetof(HeapTupleHeaderData, t_bits);
if (hasnull)
@@ -744,7 +763,11 @@ heap_deformtuple(HeapTuple tuple,
bool slow = false; /* can we use/set attcacheoff? */
natts = tup->t_natts;
- /* This min() operation is pure paranoia */
+ /*
+ * In inheritance situations, it is possible that the given tuple actually
+ * has more fields than the caller is expecting. Don't run off the end
+ * of the caller's arrays.
+ */
natts = Min(natts, tdesc_natts);
tp = (char *) tup + tup->t_hoff;
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index f206a3f28eb..c36cc42309e 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.42 2004/06/04 20:35:21 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.43 2004/06/05 01:55:04 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -35,6 +35,7 @@
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/pg_lzcompress.h"
+#include "utils/typcache.h"
#undef TOAST_DEBUG
@@ -458,10 +459,10 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
* 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(DatumGetPointer(toast_values[i])))
+ if (VARATT_IS_EXTERNAL(new_value))
{
- toast_values[i] = PointerGetDatum(heap_tuple_untoast_attr(
- (varattrib *) DatumGetPointer(toast_values[i])));
+ new_value = heap_tuple_untoast_attr(new_value);
+ toast_values[i] = PointerGetDatum(new_value);
toast_free[i] = true;
need_change = true;
need_free = true;
@@ -470,7 +471,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
/*
* Remember the size of this attribute
*/
- toast_sizes[i] = VARATT_SIZE(DatumGetPointer(toast_values[i]));
+ toast_sizes[i] = VARATT_SIZE(new_value);
}
else
{
@@ -786,6 +787,128 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
/* ----------
+ * toast_flatten_tuple_attribute -
+ *
+ * If a Datum is of composite type, "flatten" it to contain no toasted fields.
+ * This must be invoked on any potentially-composite field that is to be
+ * inserted into a tuple. Doing this preserves the invariant that toasting
+ * goes only one level deep in a tuple.
+ * ----------
+ */
+Datum
+toast_flatten_tuple_attribute(Datum value,
+ Oid typeId, int32 typeMod)
+{
+ TupleDesc tupleDesc;
+ HeapTupleHeader olddata;
+ HeapTupleHeader new_data;
+ int32 new_len;
+ HeapTupleData tmptup;
+ Form_pg_attribute *att;
+ int numAttrs;
+ int i;
+ bool need_change = false;
+ bool has_nulls = false;
+ Datum toast_values[MaxTupleAttributeNumber];
+ char toast_nulls[MaxTupleAttributeNumber];
+ bool toast_free[MaxTupleAttributeNumber];
+
+ /*
+ * See if it's a composite type, and get the tupdesc if so.
+ */
+ tupleDesc = lookup_rowtype_tupdesc_noerror(typeId, typeMod, true);
+ if (tupleDesc == NULL)
+ return value; /* not a composite type */
+
+ att = tupleDesc->attrs;
+ numAttrs = tupleDesc->natts;
+
+ /*
+ * Break down the tuple into fields.
+ */
+ olddata = DatumGetHeapTupleHeader(value);
+ Assert(typeId == HeapTupleHeaderGetTypeId(olddata));
+ Assert(typeMod == HeapTupleHeaderGetTypMod(olddata));
+ /* Build a temporary HeapTuple control structure */
+ tmptup.t_len = HeapTupleHeaderGetDatumLength(olddata);
+ ItemPointerSetInvalid(&(tmptup.t_self));
+ tmptup.t_tableOid = InvalidOid;
+ tmptup.t_data = olddata;
+
+ Assert(numAttrs <= MaxTupleAttributeNumber);
+ heap_deformtuple(&tmptup, tupleDesc, toast_values, toast_nulls);
+
+ memset(toast_free, 0, numAttrs * sizeof(bool));
+
+ for (i = 0; i < numAttrs; i++)
+ {
+ /*
+ * Look at non-null varlena attributes
+ */
+ if (toast_nulls[i] == 'n')
+ has_nulls = true;
+ else if (att[i]->attlen == -1)
+ {
+ varattrib *new_value;
+
+ new_value = (varattrib *) DatumGetPointer(toast_values[i]);
+ if (VARATT_IS_EXTENDED(new_value))
+ {
+ new_value = heap_tuple_untoast_attr(new_value);
+ toast_values[i] = PointerGetDatum(new_value);
+ toast_free[i] = true;
+ need_change = true;
+ }
+ }
+ }
+
+ /*
+ * If nothing to untoast, just return the original tuple.
+ */
+ if (!need_change)
+ return value;
+
+ /*
+ * 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 += ComputeDataSize(tupleDesc, toast_values, toast_nulls);
+
+ new_data = (HeapTupleHeader) palloc0(new_len);
+
+ /*
+ * Put the tuple header and the changed values into place
+ */
+ memcpy(new_data, olddata, olddata->t_hoff);
+
+ HeapTupleHeaderSetDatumLength(new_data, new_len);
+
+ DataFill((char *) new_data + olddata->t_hoff,
+ tupleDesc,
+ toast_values,
+ toast_nulls,
+ &(new_data->t_infomask),
+ has_nulls ? new_data->t_bits : NULL);
+
+ /*
+ * Free allocated temp values
+ */
+ for (i = 0; i < numAttrs; i++)
+ if (toast_free[i])
+ pfree(DatumGetPointer(toast_values[i]));
+
+ return PointerGetDatum(new_data);
+}
+
+
+/* ----------
* toast_compress_datum -
*
* Create a compressed version of a varlena datum