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

Commitc12d570

Browse files
committed
Support arrays over domains.
Allowing arrays with a domain type as their element type was left un-donein the original domain patch, but not for any very good reason. Thisomission leads to such surprising results as array_agg() not working ona domain column, because the parser can't identify a suitable output typefor the polymorphic aggregate.In order to fix this, first clean up the APIs of coerce_to_domain() andsome internal functions in parse_coerce.c so that we consistently passaround a CoercionContext along with CoercionForm. Previously, we sometimespassed an "isExplicit" boolean flag instead, which is strictly lessinformation; and coerce_to_domain() didn't even get that, but instead hadto reverse-engineer isExplicit from CoercionForm. That's contrary to thedocumentation in primnodes.h that says that CoercionForm only affectsdisplay and not semantics. I don't think this change fixes any live bugs,but it makes things more consistent. The main reason for doing it thoughis that now build_coercion_expression() receives ccontext, which it needsin order to be able to recursively invoke coerce_to_target_type().Next, reimplement ArrayCoerceExpr so that the node does not directly knowany details of what has to be done to the individual array elements whileperforming the array coercion. Instead, the per-element processing isrepresented by a sub-expression whose input is a source array element andwhose output is a target array element. This simplifies life inparse_coerce.c, because it can build that sub-expression by a recursiveinvocation of coerce_to_target_type(). The executor now handles theper-element processing as a compiled expression instead of hard-wired code.The main advantage of this is that we can use a single ArrayCoerceExpr tohandle as many as three successive steps per element: base type conversion,typmod coercion, and domain constraint checking. The old code used twostacked ArrayCoerceExprs to handle type + typmod coercion, which was prettyinefficient, and adding yet another array deconstruction to do domainconstraint checking seemed very unappetizing.In the case where we just need a single, very simple coercion function,doing this straightforwardly leads to a noticeable increase in theper-array-element runtime cost. Hence, add an additional shortcut evalfuncin execExprInterp.c that skips unnecessary overhead for that specific formof expression. The runtime speed of simple cases is within 1% or so ofwhere it was before, while cases that previously required two levels ofarray processing are significantly faster.Finally, create an implicit array type for every domain type, as we do forbase types, enums, etc. Everything except the array-coercion case seemsto just work without further effort.Tom Lane, reviewed by Andrew DunstanDiscussion:https://postgr.es/m/9852.1499791473@sss.pgh.pa.us
1 parent248e337 commitc12d570

File tree

28 files changed

+492
-264
lines changed

28 files changed

+492
-264
lines changed

‎contrib/pg_stat_statements/pg_stat_statements.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2630,6 +2630,7 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
26302630

26312631
APP_JUMB(acexpr->resulttype);
26322632
JumbleExpr(jstate, (Node*)acexpr->arg);
2633+
JumbleExpr(jstate, (Node*)acexpr->elemexpr);
26332634
}
26342635
break;
26352636
caseT_ConvertRowtypeExpr:

‎doc/src/sgml/array.sgml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@
1010
<para>
1111
<productname>PostgreSQL</productname> allows columns of a table to be
1212
defined as variable-length multidimensional arrays. Arrays of any
13-
built-in or user-defined base type, enum type, or composite type
14-
can be created.
15-
Arrays of domains are not yet supported.
13+
built-in or user-defined base type, enum type, composite type, range type,
14+
or domain can be created.
1615
</para>
1716

1817
<sect2 id="arrays-declaration">

‎src/backend/catalog/dependency.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1738,11 +1738,14 @@ find_expr_references_walker(Node *node,
17381738
{
17391739
ArrayCoerceExpr*acoerce= (ArrayCoerceExpr*)node;
17401740

1741-
if (OidIsValid(acoerce->elemfuncid))
1742-
add_object_address(OCLASS_PROC,acoerce->elemfuncid,0,
1743-
context->addrs);
1741+
/* as above, depend on type */
17441742
add_object_address(OCLASS_TYPE,acoerce->resulttype,0,
17451743
context->addrs);
1744+
/* the collation might not be referenced anywhere else, either */
1745+
if (OidIsValid(acoerce->resultcollid)&&
1746+
acoerce->resultcollid!=DEFAULT_COLLATION_OID)
1747+
add_object_address(OCLASS_COLLATION,acoerce->resultcollid,0,
1748+
context->addrs);
17461749
/* fall through to examine arguments */
17471750
}
17481751
elseif (IsA(node,ConvertRowtypeExpr))

‎src/backend/commands/typecmds.c

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,7 @@ ObjectAddress
729729
DefineDomain(CreateDomainStmt*stmt)
730730
{
731731
char*domainName;
732+
char*domainArrayName;
732733
OiddomainNamespace;
733734
AclResultaclresult;
734735
int16internalLength;
@@ -757,6 +758,7 @@ DefineDomain(CreateDomainStmt *stmt)
757758
Oidbasetypeoid;
758759
Oidold_type_oid;
759760
Oiddomaincoll;
761+
OiddomainArrayOid;
760762
Form_pg_typebaseType;
761763
int32basetypeMod;
762764
OidbaseColl;
@@ -1027,6 +1029,9 @@ DefineDomain(CreateDomainStmt *stmt)
10271029
}
10281030
}
10291031

1032+
/* Allocate OID for array type */
1033+
domainArrayOid=AssignTypeArrayOid();
1034+
10301035
/*
10311036
* Have TypeCreate do all the real work.
10321037
*/
@@ -1051,7 +1056,7 @@ DefineDomain(CreateDomainStmt *stmt)
10511056
analyzeProcedure,/* analyze procedure */
10521057
InvalidOid,/* no array element type */
10531058
false,/* this isn't an array */
1054-
InvalidOid,/*no arrays for domains (yet) */
1059+
domainArrayOid,/*array type we are about to create */
10551060
basetypeoid,/* base type ID */
10561061
defaultValue,/* default type value (text) */
10571062
defaultValueBin,/* default type value (binary) */
@@ -1063,6 +1068,48 @@ DefineDomain(CreateDomainStmt *stmt)
10631068
typNotNull,/* Type NOT NULL */
10641069
domaincoll);/* type's collation */
10651070

1071+
/*
1072+
* Create the array type that goes with it.
1073+
*/
1074+
domainArrayName=makeArrayTypeName(domainName,domainNamespace);
1075+
1076+
/* alignment must be 'i' or 'd' for arrays */
1077+
alignment= (alignment=='d') ?'d' :'i';
1078+
1079+
TypeCreate(domainArrayOid,/* force assignment of this type OID */
1080+
domainArrayName,/* type name */
1081+
domainNamespace,/* namespace */
1082+
InvalidOid,/* relation oid (n/a here) */
1083+
0,/* relation kind (ditto) */
1084+
GetUserId(),/* owner's ID */
1085+
-1,/* internal size (always varlena) */
1086+
TYPTYPE_BASE,/* type-type (base type) */
1087+
TYPCATEGORY_ARRAY,/* type-category (array) */
1088+
false,/* array types are never preferred */
1089+
delimiter,/* array element delimiter */
1090+
F_ARRAY_IN,/* input procedure */
1091+
F_ARRAY_OUT,/* output procedure */
1092+
F_ARRAY_RECV,/* receive procedure */
1093+
F_ARRAY_SEND,/* send procedure */
1094+
InvalidOid,/* typmodin procedure - none */
1095+
InvalidOid,/* typmodout procedure - none */
1096+
F_ARRAY_TYPANALYZE,/* analyze procedure */
1097+
address.objectId,/* element type ID */
1098+
true,/* yes this is an array type */
1099+
InvalidOid,/* no further array type */
1100+
InvalidOid,/* base type ID */
1101+
NULL,/* never a default type value */
1102+
NULL,/* binary default isn't sent either */
1103+
false,/* never passed by value */
1104+
alignment,/* see above */
1105+
'x',/* ARRAY is always toastable */
1106+
-1,/* typMod (Domains only) */
1107+
0,/* Array dimensions of typbasetype */
1108+
false,/* Type NOT NULL */
1109+
domaincoll);/* type's collation */
1110+
1111+
pfree(domainArrayName);
1112+
10661113
/*
10671114
* Process constraints which refer to the domain ID returned by TypeCreate
10681115
*/
@@ -1139,6 +1186,7 @@ DefineEnum(CreateEnumStmt *stmt)
11391186
errmsg("type \"%s\" already exists",enumName)));
11401187
}
11411188

1189+
/* Allocate OID for array type */
11421190
enumArrayOid=AssignTypeArrayOid();
11431191

11441192
/* Create the pg_type entry */

‎src/backend/executor/execExpr.c

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,6 +1225,7 @@ ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
12251225
{
12261226
ArrayCoerceExpr*acoerce= (ArrayCoerceExpr*)node;
12271227
Oidresultelemtype;
1228+
ExprState*elemstate;
12281229

12291230
/* evaluate argument into step's result area */
12301231
ExecInitExprRec(acoerce->arg,parent,state,resv,resnull);
@@ -1234,42 +1235,49 @@ ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
12341235
ereport(ERROR,
12351236
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
12361237
errmsg("target type is not an array")));
1237-
/* Arrays over domains aren't supported yet */
1238-
Assert(getBaseType(resultelemtype)==resultelemtype);
12391238

1240-
scratch.opcode=EEOP_ARRAYCOERCE;
1241-
scratch.d.arraycoerce.coerceexpr=acoerce;
1242-
scratch.d.arraycoerce.resultelemtype=resultelemtype;
1239+
/*
1240+
* Construct a sub-expression for the per-element expression;
1241+
* but don't ready it until after we check it for triviality.
1242+
* We assume it hasn't any Var references, but does have a
1243+
* CaseTestExpr representing the source array element values.
1244+
*/
1245+
elemstate=makeNode(ExprState);
1246+
elemstate->expr=acoerce->elemexpr;
1247+
elemstate->innermost_caseval= (Datum*)palloc(sizeof(Datum));
1248+
elemstate->innermost_casenull= (bool*)palloc(sizeof(bool));
12431249

1244-
if (OidIsValid(acoerce->elemfuncid))
1245-
{
1246-
AclResultaclresult;
1250+
ExecInitExprRec(acoerce->elemexpr,parent,elemstate,
1251+
&elemstate->resvalue,&elemstate->resnull);
12471252

1248-
/* Check permission to call function */
1249-
aclresult=pg_proc_aclcheck(acoerce->elemfuncid,
1250-
GetUserId(),
1251-
ACL_EXECUTE);
1252-
if (aclresult!=ACLCHECK_OK)
1253-
aclcheck_error(aclresult,ACL_KIND_PROC,
1254-
get_func_name(acoerce->elemfuncid));
1255-
InvokeFunctionExecuteHook(acoerce->elemfuncid);
1253+
if (elemstate->steps_len==1&&
1254+
elemstate->steps[0].opcode==EEOP_CASE_TESTVAL)
1255+
{
1256+
/* Trivial, so we need no per-element work at runtime */
1257+
elemstate=NULL;
1258+
}
1259+
else
1260+
{
1261+
/* Not trivial, so append a DONE step */
1262+
scratch.opcode=EEOP_DONE;
1263+
ExprEvalPushStep(elemstate,&scratch);
1264+
/* and ready the subexpression */
1265+
ExecReadyExpr(elemstate);
1266+
}
12561267

1257-
/* Set up the primary fmgr lookup information */
1258-
scratch.d.arraycoerce.elemfunc=
1259-
(FmgrInfo*)palloc0(sizeof(FmgrInfo));
1260-
fmgr_info(acoerce->elemfuncid,
1261-
scratch.d.arraycoerce.elemfunc);
1262-
fmgr_info_set_expr((Node*)acoerce,
1263-
scratch.d.arraycoerce.elemfunc);
1268+
scratch.opcode=EEOP_ARRAYCOERCE;
1269+
scratch.d.arraycoerce.elemexprstate=elemstate;
1270+
scratch.d.arraycoerce.resultelemtype=resultelemtype;
12641271

1272+
if (elemstate)
1273+
{
12651274
/* Set up workspace for array_map */
12661275
scratch.d.arraycoerce.amstate=
12671276
(ArrayMapState*)palloc0(sizeof(ArrayMapState));
12681277
}
12691278
else
12701279
{
1271-
/* Don't need workspace if there's no conversion func */
1272-
scratch.d.arraycoerce.elemfunc=NULL;
1280+
/* Don't need workspace if there's no subexpression */
12731281
scratch.d.arraycoerce.amstate=NULL;
12741282
}
12751283

‎src/backend/executor/execExprInterp.c

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,8 @@
3434
*
3535
* For very simple instructions the overhead of the full interpreter
3636
* "startup", as minimal as it is, is noticeable. Therefore
37-
* ExecReadyInterpretedExpr will choose to implement simple scalar Var
38-
* and Const expressions using special fast-path routines (ExecJust*).
39-
* Benchmarking shows anything more complex than those may as well use the
40-
* "full interpreter".
37+
* ExecReadyInterpretedExpr will choose to implement certain simple
38+
* opcode patterns using special fast-path routines (ExecJust*).
4139
*
4240
* Complex or uncommon instructions are not implemented in-line in
4341
* ExecInterpExpr(), rather we call out to a helper function appearing later
@@ -149,6 +147,7 @@ static Datum ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull
149147
staticDatumExecJustAssignInnerVar(ExprState*state,ExprContext*econtext,bool*isnull);
150148
staticDatumExecJustAssignOuterVar(ExprState*state,ExprContext*econtext,bool*isnull);
151149
staticDatumExecJustAssignScanVar(ExprState*state,ExprContext*econtext,bool*isnull);
150+
staticDatumExecJustApplyFuncToCase(ExprState*state,ExprContext*econtext,bool*isnull);
152151

153152

154153
/*
@@ -184,10 +183,8 @@ ExecReadyInterpretedExpr(ExprState *state)
184183

185184
/*
186185
* Select fast-path evalfuncs for very simple expressions. "Starting up"
187-
* the full interpreter is a measurable overhead for these. Plain Vars
188-
* and Const seem to be the only ones where the intrinsic cost is small
189-
* enough that the overhead of ExecInterpExpr matters. For more complex
190-
* expressions it's cheaper to use ExecInterpExpr always.
186+
* the full interpreter is a measurable overhead for these, and these
187+
* patterns occur often enough to be worth optimizing.
191188
*/
192189
if (state->steps_len==3)
193190
{
@@ -230,6 +227,13 @@ ExecReadyInterpretedExpr(ExprState *state)
230227
state->evalfunc=ExecJustAssignScanVar;
231228
return;
232229
}
230+
elseif (step0==EEOP_CASE_TESTVAL&&
231+
step1==EEOP_FUNCEXPR_STRICT&&
232+
state->steps[0].d.casetest.value)
233+
{
234+
state->evalfunc=ExecJustApplyFuncToCase;
235+
return;
236+
}
233237
}
234238
elseif (state->steps_len==2&&
235239
state->steps[0].opcode==EEOP_CONST)
@@ -1275,7 +1279,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
12751279
EEO_CASE(EEOP_ARRAYCOERCE)
12761280
{
12771281
/* too complex for an inline implementation */
1278-
ExecEvalArrayCoerce(state,op);
1282+
ExecEvalArrayCoerce(state,op,econtext);
12791283

12801284
EEO_NEXT();
12811285
}
@@ -1811,6 +1815,43 @@ ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
18111815
return0;
18121816
}
18131817

1818+
/* Evaluate CASE_TESTVAL and apply a strict function to it */
1819+
staticDatum
1820+
ExecJustApplyFuncToCase(ExprState*state,ExprContext*econtext,bool*isnull)
1821+
{
1822+
ExprEvalStep*op=&state->steps[0];
1823+
FunctionCallInfofcinfo;
1824+
bool*argnull;
1825+
intargno;
1826+
Datumd;
1827+
1828+
/*
1829+
* XXX with some redesign of the CaseTestExpr mechanism, maybe we could
1830+
* get rid of this data shuffling?
1831+
*/
1832+
*op->resvalue=*op->d.casetest.value;
1833+
*op->resnull=*op->d.casetest.isnull;
1834+
1835+
op++;
1836+
1837+
fcinfo=op->d.func.fcinfo_data;
1838+
argnull=fcinfo->argnull;
1839+
1840+
/* strict function, so check for NULL args */
1841+
for (argno=0;argno<op->d.func.nargs;argno++)
1842+
{
1843+
if (argnull[argno])
1844+
{
1845+
*isnull= true;
1846+
return (Datum)0;
1847+
}
1848+
}
1849+
fcinfo->isnull= false;
1850+
d=op->d.func.fn_addr(fcinfo);
1851+
*isnull=fcinfo->isnull;
1852+
returnd;
1853+
}
1854+
18141855

18151856
/*
18161857
* Do one-time initialization of interpretation machinery.
@@ -2345,11 +2386,9 @@ ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op)
23452386
* Source array is in step's result variable.
23462387
*/
23472388
void
2348-
ExecEvalArrayCoerce(ExprState*state,ExprEvalStep*op)
2389+
ExecEvalArrayCoerce(ExprState*state,ExprEvalStep*op,ExprContext*econtext)
23492390
{
2350-
ArrayCoerceExpr*acoerce=op->d.arraycoerce.coerceexpr;
23512391
Datumarraydatum;
2352-
FunctionCallInfoDatalocfcinfo;
23532392

23542393
/* NULL array -> NULL result */
23552394
if (*op->resnull)
@@ -2361,7 +2400,7 @@ ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op)
23612400
* If it's binary-compatible, modify the element type in the array header,
23622401
* but otherwise leave the array as we received it.
23632402
*/
2364-
if (!OidIsValid(acoerce->elemfuncid))
2403+
if (op->d.arraycoerce.elemexprstate==NULL)
23652404
{
23662405
/* Detoast input array if necessary, and copy in any case */
23672406
ArrayType*array=DatumGetArrayTypePCopy(arraydatum);
@@ -2372,23 +2411,12 @@ ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op)
23722411
}
23732412

23742413
/*
2375-
* Use array_map to apply the function to each array element.
2376-
*
2377-
* We pass on the desttypmod and isExplicit flags whether or not the
2378-
* function wants them.
2379-
*
2380-
* Note: coercion functions are assumed to not use collation.
2414+
* Use array_map to apply the sub-expression to each array element.
23812415
*/
2382-
InitFunctionCallInfoData(locfcinfo,op->d.arraycoerce.elemfunc,3,
2383-
InvalidOid,NULL,NULL);
2384-
locfcinfo.arg[0]=arraydatum;
2385-
locfcinfo.arg[1]=Int32GetDatum(acoerce->resulttypmod);
2386-
locfcinfo.arg[2]=BoolGetDatum(acoerce->isExplicit);
2387-
locfcinfo.argnull[0]= false;
2388-
locfcinfo.argnull[1]= false;
2389-
locfcinfo.argnull[2]= false;
2390-
2391-
*op->resvalue=array_map(&locfcinfo,op->d.arraycoerce.resultelemtype,
2416+
*op->resvalue=array_map(arraydatum,
2417+
op->d.arraycoerce.elemexprstate,
2418+
econtext,
2419+
op->d.arraycoerce.resultelemtype,
23922420
op->d.arraycoerce.amstate);
23932421
}
23942422

‎src/backend/nodes/copyfuncs.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1698,11 +1698,10 @@ _copyArrayCoerceExpr(const ArrayCoerceExpr *from)
16981698
ArrayCoerceExpr*newnode=makeNode(ArrayCoerceExpr);
16991699

17001700
COPY_NODE_FIELD(arg);
1701-
COPY_SCALAR_FIELD(elemfuncid);
1701+
COPY_NODE_FIELD(elemexpr);
17021702
COPY_SCALAR_FIELD(resulttype);
17031703
COPY_SCALAR_FIELD(resulttypmod);
17041704
COPY_SCALAR_FIELD(resultcollid);
1705-
COPY_SCALAR_FIELD(isExplicit);
17061705
COPY_SCALAR_FIELD(coerceformat);
17071706
COPY_LOCATION_FIELD(location);
17081707

‎src/backend/nodes/equalfuncs.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -513,11 +513,10 @@ static bool
513513
_equalArrayCoerceExpr(constArrayCoerceExpr*a,constArrayCoerceExpr*b)
514514
{
515515
COMPARE_NODE_FIELD(arg);
516-
COMPARE_SCALAR_FIELD(elemfuncid);
516+
COMPARE_NODE_FIELD(elemexpr);
517517
COMPARE_SCALAR_FIELD(resulttype);
518518
COMPARE_SCALAR_FIELD(resulttypmod);
519519
COMPARE_SCALAR_FIELD(resultcollid);
520-
COMPARE_SCALAR_FIELD(isExplicit);
521520
COMPARE_COERCIONFORM_FIELD(coerceformat);
522521
COMPARE_LOCATION_FIELD(location);
523522

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp