aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2018-02-13 18:52:21 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2018-02-13 18:52:21 -0500
commit4b93f57999a2ca9b9c9e573ea32ab1aeaa8bf496 (patch)
tree643c8944b9847e37674fc5c2357b2ede81692767 /src/backend/executor
parent2ac3e6acc228e4b99022019379c6d5c4b61b231c (diff)
downloadpostgresql-4b93f57999a2ca9b9c9e573ea32ab1aeaa8bf496.tar.gz
postgresql-4b93f57999a2ca9b9c9e573ea32ab1aeaa8bf496.zip
Make plpgsql use its DTYPE_REC code paths for composite-type variables.
Formerly, DTYPE_REC was used only for variables declared as "record"; variables of named composite types used DTYPE_ROW, which is faster for some purposes but much less flexible. In particular, the ROW code paths are entirely incapable of dealing with DDL-caused changes to the number or data types of the columns of a row variable, once a particular plpgsql function has been parsed for the first time in a session. And, since the stored representation of a ROW isn't a tuple, there wasn't any easy way to deal with variables of domain-over-composite types, since the domain constraint checking code would expect the value to be checked to be a tuple. A lesser, but still real, annoyance is that ROW format cannot represent a true NULL composite value, only a row of per-field NULL values, which is not exactly the same thing. Hence, switch to using DTYPE_REC for all composite-typed variables, whether "record", named composite type, or domain over named composite type. DTYPE_ROW remains but is used only for its native purpose, to represent a fixed-at-compile-time list of variables, for instance the targets of an INTO clause. To accomplish this without taking significant performance losses, introduce infrastructure that allows storing composite-type variables as "expanded objects", similar to the "expanded array" infrastructure introduced in commit 1dc5ebc90. A composite variable's value is thereby kept (most of the time) in the form of separate Datums, so that field accesses and updates are not much more expensive than they were in the ROW format. This holds the line, more or less, on performance of variables of named composite types in field-access-intensive microbenchmarks, and makes variables declared "record" perform much better than before in similar tests. In addition, the logic involved with enforcing composite-domain constraints against updates of individual fields is in the expanded record infrastructure not plpgsql proper, so that it might be reusable for other purposes. In further support of this, introduce a typcache feature for assigning a unique-within-process identifier to each distinct tuple descriptor of interest; in particular, DDL alterations on composite types result in a new identifier for that type. This allows very cheap detection of the need to refresh tupdesc-dependent data. This improves on the "tupDescSeqNo" idea I had in commit 687f096ea: that assigned identifying sequence numbers to successive versions of individual composite types, but the numbers were not unique across different types, nor was there support for assigning numbers to registered record types. In passing, allow plpgsql functions to accept as well as return type "record". There was no good reason for the old restriction, and it was out of step with most of the other PLs. Tom Lane, reviewed by Pavel Stehule Discussion: https://postgr.es/m/8962.1514399547@sss.pgh.pa.us
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/execExprInterp.c135
1 files changed, 92 insertions, 43 deletions
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index f646fd9c51e..9c6c2b02e98 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -70,6 +70,7 @@
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/datum.h"
+#include "utils/expandedrecord.h"
#include "utils/lsyscache.h"
#include "utils/timestamp.h"
#include "utils/typcache.h"
@@ -2820,57 +2821,105 @@ ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
if (*op->resnull)
return;
- /* Get the composite datum and extract its type fields */
tupDatum = *op->resvalue;
- tuple = DatumGetHeapTupleHeader(tupDatum);
- tupType = HeapTupleHeaderGetTypeId(tuple);
- tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+ /* We can special-case expanded records for speed */
+ if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(tupDatum)))
+ {
+ ExpandedRecordHeader *erh = (ExpandedRecordHeader *) DatumGetEOHP(tupDatum);
- /* Lookup tupdesc if first time through or if type changes */
- tupDesc = get_cached_rowtype(tupType, tupTypmod,
- &op->d.fieldselect.argdesc,
- econtext);
+ Assert(erh->er_magic == ER_MAGIC);
- /*
- * Find field's attr record. Note we don't support system columns here: a
- * datum tuple doesn't have valid values for most of the interesting
- * system columns anyway.
- */
- if (fieldnum <= 0) /* should never happen */
- elog(ERROR, "unsupported reference to system column %d in FieldSelect",
- fieldnum);
- if (fieldnum > tupDesc->natts) /* should never happen */
- elog(ERROR, "attribute number %d exceeds number of columns %d",
- fieldnum, tupDesc->natts);
- attr = TupleDescAttr(tupDesc, fieldnum - 1);
-
- /* Check for dropped column, and force a NULL result if so */
- if (attr->attisdropped)
- {
- *op->resnull = true;
- return;
+ /* Extract record's TupleDesc */
+ tupDesc = expanded_record_get_tupdesc(erh);
+
+ /*
+ * Find field's attr record. Note we don't support system columns
+ * here: a datum tuple doesn't have valid values for most of the
+ * interesting system columns anyway.
+ */
+ if (fieldnum <= 0) /* should never happen */
+ elog(ERROR, "unsupported reference to system column %d in FieldSelect",
+ fieldnum);
+ if (fieldnum > tupDesc->natts) /* should never happen */
+ elog(ERROR, "attribute number %d exceeds number of columns %d",
+ fieldnum, tupDesc->natts);
+ attr = TupleDescAttr(tupDesc, fieldnum - 1);
+
+ /* Check for dropped column, and force a NULL result if so */
+ if (attr->attisdropped)
+ {
+ *op->resnull = true;
+ return;
+ }
+
+ /* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
+ /* As in CheckVarSlotCompatibility, we should but can't check typmod */
+ if (op->d.fieldselect.resulttype != attr->atttypid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("attribute %d has wrong type", fieldnum),
+ errdetail("Table has type %s, but query expects %s.",
+ format_type_be(attr->atttypid),
+ format_type_be(op->d.fieldselect.resulttype))));
+
+ /* extract the field */
+ *op->resvalue = expanded_record_get_field(erh, fieldnum,
+ op->resnull);
}
+ else
+ {
+ /* Get the composite datum and extract its type fields */
+ tuple = DatumGetHeapTupleHeader(tupDatum);
- /* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
- /* As in CheckVarSlotCompatibility, we should but can't check typmod */
- if (op->d.fieldselect.resulttype != attr->atttypid)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("attribute %d has wrong type", fieldnum),
- errdetail("Table has type %s, but query expects %s.",
- format_type_be(attr->atttypid),
- format_type_be(op->d.fieldselect.resulttype))));
+ tupType = HeapTupleHeaderGetTypeId(tuple);
+ tupTypmod = HeapTupleHeaderGetTypMod(tuple);
- /* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */
- tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
- tmptup.t_data = tuple;
+ /* Lookup tupdesc if first time through or if type changes */
+ tupDesc = get_cached_rowtype(tupType, tupTypmod,
+ &op->d.fieldselect.argdesc,
+ econtext);
+
+ /*
+ * Find field's attr record. Note we don't support system columns
+ * here: a datum tuple doesn't have valid values for most of the
+ * interesting system columns anyway.
+ */
+ if (fieldnum <= 0) /* should never happen */
+ elog(ERROR, "unsupported reference to system column %d in FieldSelect",
+ fieldnum);
+ if (fieldnum > tupDesc->natts) /* should never happen */
+ elog(ERROR, "attribute number %d exceeds number of columns %d",
+ fieldnum, tupDesc->natts);
+ attr = TupleDescAttr(tupDesc, fieldnum - 1);
- /* extract the field */
- *op->resvalue = heap_getattr(&tmptup,
- fieldnum,
- tupDesc,
- op->resnull);
+ /* Check for dropped column, and force a NULL result if so */
+ if (attr->attisdropped)
+ {
+ *op->resnull = true;
+ return;
+ }
+
+ /* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
+ /* As in CheckVarSlotCompatibility, we should but can't check typmod */
+ if (op->d.fieldselect.resulttype != attr->atttypid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("attribute %d has wrong type", fieldnum),
+ errdetail("Table has type %s, but query expects %s.",
+ format_type_be(attr->atttypid),
+ format_type_be(op->d.fieldselect.resulttype))));
+
+ /* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */
+ tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+ tmptup.t_data = tuple;
+
+ /* extract the field */
+ *op->resvalue = heap_getattr(&tmptup,
+ fieldnum,
+ tupDesc,
+ op->resnull);
+ }
}
/*