Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit4b93f57

Browse files
committed
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 forsome purposes but much less flexible. In particular, the ROW code pathsare entirely incapable of dealing with DDL-caused changes to the numberor data types of the columns of a row variable, once a particular plpgsqlfunction has been parsed for the first time in a session. And, since thestored representation of a ROW isn't a tuple, there wasn't any easy wayto deal with variables of domain-over-composite types, since the domainconstraint checking code would expect the value to be checked to be atuple. A lesser, but still real, annoyance is that ROW format cannotrepresent a true NULL composite value, only a row of per-field NULLvalues, 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 compositetype. DTYPE_ROW remains but is used only for its native purpose, torepresent a fixed-at-compile-time list of variables, for instance thetargets of an INTO clause.To accomplish this without taking significant performance losses, introduceinfrastructure that allows storing composite-type variables as "expandedobjects", similar to the "expanded array" infrastructure introduced incommit1dc5ebc. A composite variable's value is thereby kept (most ofthe time) in the form of separate Datums, so that field accesses andupdates are not much more expensive than they were in the ROW format.This holds the line, more or less, on performance of variables of namedcomposite types in field-access-intensive microbenchmarks, and makesvariables declared "record" perform much better than before in similartests. In addition, the logic involved with enforcing composite-domainconstraints against updates of individual fields is in the expandedrecord infrastructure not plpgsql proper, so that it might be reusablefor other purposes.In further support of this, introduce a typcache feature for assigning aunique-within-process identifier to each distinct tuple descriptor ofinterest; in particular, DDL alterations on composite types result in a newidentifier for that type. This allows very cheap detection of the need torefresh tupdesc-dependent data. This improves on the "tupDescSeqNo" ideaI had in commit687f096: that assigned identifying sequence numbers tosuccessive versions of individual composite types, but the numbers were notunique across different types, nor was there support for assigning numbersto 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 itwas out of step with most of the other PLs.Tom Lane, reviewed by Pavel StehuleDiscussion:https://postgr.es/m/8962.1514399547@sss.pgh.pa.us
1 parent2ac3e6a commit4b93f57

File tree

20 files changed

+4589
-906
lines changed

20 files changed

+4589
-906
lines changed

‎doc/src/sgml/plpgsql.sgml

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,9 @@
123123
and they can return a result of any of these types. They can also
124124
accept or return any composite type (row type) specified by name.
125125
It is also possible to declare a <application>PL/pgSQL</application>
126-
function as returning <type>record</type>, which means that the result
126+
function as accepting <type>record</type>, which means that any
127+
composite type will do as input, or
128+
as returning <type>record</type>, which means that the result
127129
is a row type whose columns are determined by specification in the
128130
calling query, as discussed in <xref linkend="queries-tablefunctions"/>.
129131
</para>
@@ -671,14 +673,6 @@ user_id users.user_id%TYPE;
671673
be selected from it, for example <literal>$1.user_id</literal>.
672674
</para>
673675

674-
<para>
675-
Only the user-defined columns of a table row are accessible in a
676-
row-type variable, not the OID or other system columns (because the
677-
row could be from a view). The fields of the row type inherit the
678-
table's field size or precision for data types such as
679-
<type>char(<replaceable>n</replaceable>)</type>.
680-
</para>
681-
682676
<para>
683677
Here is an example of using composite types. <structname>table1</structname>
684678
and <structname>table2</structname> are existing tables having at least the

‎src/backend/executor/execExprInterp.c

Lines changed: 92 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
#include"utils/builtins.h"
7171
#include"utils/date.h"
7272
#include"utils/datum.h"
73+
#include"utils/expandedrecord.h"
7374
#include"utils/lsyscache.h"
7475
#include"utils/timestamp.h"
7576
#include"utils/typcache.h"
@@ -2820,57 +2821,105 @@ ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
28202821
if (*op->resnull)
28212822
return;
28222823

2823-
/* Get the composite datum and extract its type fields */
28242824
tupDatum=*op->resvalue;
2825-
tuple=DatumGetHeapTupleHeader(tupDatum);
28262825

2827-
tupType=HeapTupleHeaderGetTypeId(tuple);
2828-
tupTypmod=HeapTupleHeaderGetTypMod(tuple);
2826+
/* We can special-case expanded records for speed */
2827+
if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(tupDatum)))
2828+
{
2829+
ExpandedRecordHeader*erh= (ExpandedRecordHeader*)DatumGetEOHP(tupDatum);
28292830

2830-
/* Lookup tupdesc if first time through or if type changes */
2831-
tupDesc=get_cached_rowtype(tupType,tupTypmod,
2832-
&op->d.fieldselect.argdesc,
2833-
econtext);
2831+
Assert(erh->er_magic==ER_MAGIC);
28342832

2835-
/*
2836-
* Find field's attr record. Note we don't support system columns here: a
2837-
* datum tuple doesn't have valid values for most of the interesting
2838-
* system columns anyway.
2839-
*/
2840-
if (fieldnum <=0)/* should never happen */
2841-
elog(ERROR,"unsupported reference to system column %d in FieldSelect",
2842-
fieldnum);
2843-
if (fieldnum>tupDesc->natts)/* should never happen */
2844-
elog(ERROR,"attribute number %d exceeds number of columns %d",
2845-
fieldnum,tupDesc->natts);
2846-
attr=TupleDescAttr(tupDesc,fieldnum-1);
2847-
2848-
/* Check for dropped column, and force a NULL result if so */
2849-
if (attr->attisdropped)
2850-
{
2851-
*op->resnull= true;
2852-
return;
2833+
/* Extract record's TupleDesc */
2834+
tupDesc=expanded_record_get_tupdesc(erh);
2835+
2836+
/*
2837+
* Find field's attr record. Note we don't support system columns
2838+
* here: a datum tuple doesn't have valid values for most of the
2839+
* interesting system columns anyway.
2840+
*/
2841+
if (fieldnum <=0)/* should never happen */
2842+
elog(ERROR,"unsupported reference to system column %d in FieldSelect",
2843+
fieldnum);
2844+
if (fieldnum>tupDesc->natts)/* should never happen */
2845+
elog(ERROR,"attribute number %d exceeds number of columns %d",
2846+
fieldnum,tupDesc->natts);
2847+
attr=TupleDescAttr(tupDesc,fieldnum-1);
2848+
2849+
/* Check for dropped column, and force a NULL result if so */
2850+
if (attr->attisdropped)
2851+
{
2852+
*op->resnull= true;
2853+
return;
2854+
}
2855+
2856+
/* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
2857+
/* As in CheckVarSlotCompatibility, we should but can't check typmod */
2858+
if (op->d.fieldselect.resulttype!=attr->atttypid)
2859+
ereport(ERROR,
2860+
(errcode(ERRCODE_DATATYPE_MISMATCH),
2861+
errmsg("attribute %d has wrong type",fieldnum),
2862+
errdetail("Table has type %s, but query expects %s.",
2863+
format_type_be(attr->atttypid),
2864+
format_type_be(op->d.fieldselect.resulttype))));
2865+
2866+
/* extract the field */
2867+
*op->resvalue=expanded_record_get_field(erh,fieldnum,
2868+
op->resnull);
28532869
}
2870+
else
2871+
{
2872+
/* Get the composite datum and extract its type fields */
2873+
tuple=DatumGetHeapTupleHeader(tupDatum);
28542874

2855-
/* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
2856-
/* As in CheckVarSlotCompatibility, we should but can't check typmod */
2857-
if (op->d.fieldselect.resulttype!=attr->atttypid)
2858-
ereport(ERROR,
2859-
(errcode(ERRCODE_DATATYPE_MISMATCH),
2860-
errmsg("attribute %d has wrong type",fieldnum),
2861-
errdetail("Table has type %s, but query expects %s.",
2862-
format_type_be(attr->atttypid),
2863-
format_type_be(op->d.fieldselect.resulttype))));
2875+
tupType=HeapTupleHeaderGetTypeId(tuple);
2876+
tupTypmod=HeapTupleHeaderGetTypMod(tuple);
28642877

2865-
/* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */
2866-
tmptup.t_len=HeapTupleHeaderGetDatumLength(tuple);
2867-
tmptup.t_data=tuple;
2878+
/* Lookup tupdesc if first time through or if type changes */
2879+
tupDesc=get_cached_rowtype(tupType,tupTypmod,
2880+
&op->d.fieldselect.argdesc,
2881+
econtext);
2882+
2883+
/*
2884+
* Find field's attr record. Note we don't support system columns
2885+
* here: a datum tuple doesn't have valid values for most of the
2886+
* interesting system columns anyway.
2887+
*/
2888+
if (fieldnum <=0)/* should never happen */
2889+
elog(ERROR,"unsupported reference to system column %d in FieldSelect",
2890+
fieldnum);
2891+
if (fieldnum>tupDesc->natts)/* should never happen */
2892+
elog(ERROR,"attribute number %d exceeds number of columns %d",
2893+
fieldnum,tupDesc->natts);
2894+
attr=TupleDescAttr(tupDesc,fieldnum-1);
28682895

2869-
/* extract the field */
2870-
*op->resvalue=heap_getattr(&tmptup,
2871-
fieldnum,
2872-
tupDesc,
2873-
op->resnull);
2896+
/* Check for dropped column, and force a NULL result if so */
2897+
if (attr->attisdropped)
2898+
{
2899+
*op->resnull= true;
2900+
return;
2901+
}
2902+
2903+
/* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
2904+
/* As in CheckVarSlotCompatibility, we should but can't check typmod */
2905+
if (op->d.fieldselect.resulttype!=attr->atttypid)
2906+
ereport(ERROR,
2907+
(errcode(ERRCODE_DATATYPE_MISMATCH),
2908+
errmsg("attribute %d has wrong type",fieldnum),
2909+
errdetail("Table has type %s, but query expects %s.",
2910+
format_type_be(attr->atttypid),
2911+
format_type_be(op->d.fieldselect.resulttype))));
2912+
2913+
/* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */
2914+
tmptup.t_len=HeapTupleHeaderGetDatumLength(tuple);
2915+
tmptup.t_data=tuple;
2916+
2917+
/* extract the field */
2918+
*op->resvalue=heap_getattr(&tmptup,
2919+
fieldnum,
2920+
tupDesc,
2921+
op->resnull);
2922+
}
28742923
}
28752924

28762925
/*

‎src/backend/utils/adt/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ include $(top_builddir)/src/Makefile.global
1212
OBJS = acl.o amutils.o arrayfuncs.o array_expanded.o array_selfuncs.o\
1313
array_typanalyze.o array_userfuncs.o arrayutils.o ascii.o\
1414
bool.o cash.o char.o date.o datetime.o datum.o dbsize.o domains.o\
15-
encode.o enum.o expandeddatum.o\
15+
encode.o enum.o expandeddatum.oexpandedrecord.o\
1616
float.o format_type.o formatting.o genfile.o\
1717
geo_ops.o geo_selfuncs.o geo_spgist.o inet_cidr_ntop.o inet_net_pton.o\
1818
int.o int8.o json.o jsonb.o jsonb_gin.o jsonb_op.o jsonb_util.o\

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp