Skip to content

Commit 610e8eb

Browse files
committed
Teach map_partition_varattnos to handle whole-row expressions.
Otherwise, partitioned tables with RETURNING expressions or subject to a WITH CHECK OPTION do not work properly. Amit Langote, reviewed by Amit Khandekar and Etsuro Fujita. A few comment changes by me. Discussion: http://postgr.es/m/9a39df80-871e-6212-0684-f93c83be4097@lab.ntt.co.jp
1 parent 5ff3d73 commit 610e8eb

File tree

11 files changed

+157
-25
lines changed

11 files changed

+157
-25
lines changed

src/backend/catalog/partition.c

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -898,16 +898,20 @@ get_qual_from_partbound(Relation rel, Relation parent,
898898
* We must allow for cases where physical attnos of a partition can be
899899
* different from the parent's.
900900
*
901+
* If found_whole_row is not NULL, *found_whole_row returns whether a
902+
* whole-row variable was found in the input expression.
903+
*
901904
* Note: this will work on any node tree, so really the argument and result
902905
* should be declared "Node *". But a substantial majority of the callers
903906
* are working on Lists, so it's less messy to do the casts internally.
904907
*/
905908
List *
906909
map_partition_varattnos(List *expr, int target_varno,
907-
Relation partrel, Relation parent)
910+
Relation partrel, Relation parent,
911+
bool *found_whole_row)
908912
{
909913
AttrNumber *part_attnos;
910-
bool found_whole_row;
914+
bool my_found_whole_row;
911915

912916
if (expr == NIL)
913917
return NIL;
@@ -919,10 +923,10 @@ map_partition_varattnos(List *expr, int target_varno,
919923
target_varno, 0,
920924
part_attnos,
921925
RelationGetDescr(parent)->natts,
922-
&found_whole_row);
923-
/* There can never be a whole-row reference here */
926+
RelationGetForm(partrel)->reltype,
927+
&my_found_whole_row);
924928
if (found_whole_row)
925-
elog(ERROR, "unexpected whole-row reference found in partition key");
929+
*found_whole_row = my_found_whole_row;
926930

927931
return expr;
928932
}
@@ -1783,6 +1787,7 @@ generate_partition_qual(Relation rel)
17831787
List *my_qual = NIL,
17841788
*result = NIL;
17851789
Relation parent;
1790+
bool found_whole_row;
17861791

17871792
/* Guard against stack overflow due to overly deep partition tree */
17881793
check_stack_depth();
@@ -1825,7 +1830,11 @@ generate_partition_qual(Relation rel)
18251830
* in it to bear this relation's attnos. It's safe to assume varno = 1
18261831
* here.
18271832
*/
1828-
result = map_partition_varattnos(result, 1, rel, parent);
1833+
result = map_partition_varattnos(result, 1, rel, parent,
1834+
&found_whole_row);
1835+
/* There can never be a whole-row reference here */
1836+
if (found_whole_row)
1837+
elog(ERROR, "unexpected whole-row reference found in partition key");
18291838

18301839
/* Save a copy in the relcache */
18311840
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);

src/backend/commands/tablecmds.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1989,7 +1989,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
19891989
expr = map_variable_attnos(stringToNode(check[i].ccbin),
19901990
1, 0,
19911991
newattno, tupleDesc->natts,
1992-
&found_whole_row);
1992+
InvalidOid, &found_whole_row);
19931993

19941994
/*
19951995
* For the moment we have to reject whole-row variables. We
@@ -8874,7 +8874,7 @@ ATPrepAlterColumnType(List **wqueue,
88748874
map_variable_attnos(def->cooked_default,
88758875
1, 0,
88768876
attmap, RelationGetDescr(rel)->natts,
8877-
&found_whole_row);
8877+
InvalidOid, &found_whole_row);
88788878
if (found_whole_row)
88798879
ereport(ERROR,
88808880
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -13713,6 +13713,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
1371313713
Oid part_relid = lfirst_oid(lc);
1371413714
Relation part_rel;
1371513715
Expr *constr;
13716+
bool found_whole_row;
1371613717

1371713718
/* Lock already taken */
1371813719
if (part_relid != RelationGetRelid(attachRel))
@@ -13738,7 +13739,12 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
1373813739
constr = linitial(partConstraint);
1373913740
tab->partition_constraint = (Expr *)
1374013741
map_partition_varattnos((List *) constr, 1,
13741-
part_rel, rel);
13742+
part_rel, rel,
13743+
&found_whole_row);
13744+
/* There can never be a whole-row reference here */
13745+
if (found_whole_row)
13746+
elog(ERROR, "unexpected whole-row reference found in partition key");
13747+
1374213748
/* keep our lock until commit */
1374313749
if (part_rel != attachRel)
1374413750
heap_close(part_rel, NoLock);

src/backend/executor/nodeModifyTable.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1996,7 +1996,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
19961996
/* varno = node->nominalRelation */
19971997
mapped_wcoList = map_partition_varattnos(wcoList,
19981998
node->nominalRelation,
1999-
partrel, rel);
1999+
partrel, rel, NULL);
20002000
foreach(ll, mapped_wcoList)
20012001
{
20022002
WithCheckOption *wco = castNode(WithCheckOption, lfirst(ll));
@@ -2069,7 +2069,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
20692069
/* varno = node->nominalRelation */
20702070
rlist = map_partition_varattnos(returningList,
20712071
node->nominalRelation,
2072-
partrel, rel);
2072+
partrel, rel, NULL);
20732073
resultRelInfo->ri_projectReturning =
20742074
ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
20752075
resultRelInfo->ri_RelationDesc->rd_att);

src/backend/parser/parse_utilcmd.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,7 +1107,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
11071107
ccbin_node = map_variable_attnos(stringToNode(ccbin),
11081108
1, 0,
11091109
attmap, tupleDesc->natts,
1110-
&found_whole_row);
1110+
InvalidOid, &found_whole_row);
11111111

11121112
/*
11131113
* We reject whole-row variables because the whole point of LIKE
@@ -1463,7 +1463,7 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
14631463
indexkey = map_variable_attnos(indexkey,
14641464
1, 0,
14651465
attmap, attmap_length,
1466-
&found_whole_row);
1466+
InvalidOid, &found_whole_row);
14671467

14681468
/* As in transformTableLikeClause, reject whole-row variables */
14691469
if (found_whole_row)
@@ -1539,7 +1539,7 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
15391539
pred_tree = map_variable_attnos(pred_tree,
15401540
1, 0,
15411541
attmap, attmap_length,
1542-
&found_whole_row);
1542+
InvalidOid, &found_whole_row);
15431543

15441544
/* As in transformTableLikeClause, reject whole-row variables */
15451545
if (found_whole_row)

src/backend/rewrite/rewriteManip.c

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,14 +1203,12 @@ replace_rte_variables_mutator(Node *node,
12031203
* appear in the expression.
12041204
*
12051205
* If the expression tree contains a whole-row Var for the target RTE,
1206-
* the Var is not changed but *found_whole_row is returned as TRUE.
1207-
* For most callers this is an error condition, but we leave it to the caller
1208-
* to report the error so that useful context can be provided. (In some
1209-
* usages it would be appropriate to modify the Var's vartype and insert a
1210-
* ConvertRowtypeExpr node to map back to the original vartype. We might
1211-
* someday extend this function's API to support that. For now, the only
1212-
* concession to that future need is that this function is a tree mutator
1213-
* not just a walker.)
1206+
* *found_whole_row is returned as TRUE. In addition, if to_rowtype is
1207+
* not InvalidOid, we modify the Var's vartype and insert a ConvertRowTypeExpr
1208+
* to map back to the orignal rowtype. Callers that don't provide to_rowtype
1209+
* should report an error if *found_row_type is true; we don't do that here
1210+
* because we don't know exactly what wording for the error message would
1211+
* be most appropriate. The caller will be aware of the context.
12141212
*
12151213
* This could be built using replace_rte_variables and a callback function,
12161214
* but since we don't ever need to insert sublinks, replace_rte_variables is
@@ -1223,6 +1221,8 @@ typedef struct
12231221
int sublevels_up; /* (current) nesting depth */
12241222
const AttrNumber *attno_map; /* map array for user attnos */
12251223
int map_length; /* number of entries in attno_map[] */
1224+
/* Target type when converting whole-row vars */
1225+
Oid to_rowtype;
12261226
bool *found_whole_row; /* output flag */
12271227
} map_variable_attnos_context;
12281228

@@ -1257,6 +1257,34 @@ map_variable_attnos_mutator(Node *node,
12571257
{
12581258
/* whole-row variable, warn caller */
12591259
*(context->found_whole_row) = true;
1260+
1261+
/* If the callers expects us to convert the same, do so. */
1262+
if (OidIsValid(context->to_rowtype))
1263+
{
1264+
/* No support for RECORDOID. */
1265+
Assert(var->vartype != RECORDOID);
1266+
1267+
/* Don't convert unless necessary. */
1268+
if (context->to_rowtype != var->vartype)
1269+
{
1270+
ConvertRowtypeExpr *r;
1271+
1272+
/* Var itself is converted to the requested type. */
1273+
newvar->vartype = context->to_rowtype;
1274+
1275+
/*
1276+
* And a conversion node on top to convert back to the
1277+
* original type.
1278+
*/
1279+
r = makeNode(ConvertRowtypeExpr);
1280+
r->arg = (Expr *) newvar;
1281+
r->resulttype = var->vartype;
1282+
r->convertformat = COERCE_IMPLICIT_CAST;
1283+
r->location = -1;
1284+
1285+
return (Node *) r;
1286+
}
1287+
}
12601288
}
12611289
return (Node *) newvar;
12621290
}
@@ -1283,14 +1311,15 @@ Node *
12831311
map_variable_attnos(Node *node,
12841312
int target_varno, int sublevels_up,
12851313
const AttrNumber *attno_map, int map_length,
1286-
bool *found_whole_row)
1314+
Oid to_rowtype, bool *found_whole_row)
12871315
{
12881316
map_variable_attnos_context context;
12891317

12901318
context.target_varno = target_varno;
12911319
context.sublevels_up = sublevels_up;
12921320
context.attno_map = attno_map;
12931321
context.map_length = map_length;
1322+
context.to_rowtype = to_rowtype;
12941323
context.found_whole_row = found_whole_row;
12951324

12961325
*found_whole_row = false;

src/include/catalog/partition.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ extern Oid get_partition_parent(Oid relid);
8080
extern List *get_qual_from_partbound(Relation rel, Relation parent,
8181
PartitionBoundSpec *spec);
8282
extern List *map_partition_varattnos(List *expr, int target_varno,
83-
Relation partrel, Relation parent);
83+
Relation partrel, Relation parent,
84+
bool *found_whole_row);
8485
extern List *RelationGetPartitionQual(Relation rel);
8586
extern Expr *get_partition_qual_relid(Oid relid);
8687

src/include/rewrite/rewriteManip.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ extern Node *replace_rte_variables_mutator(Node *node,
7272
extern Node *map_variable_attnos(Node *node,
7373
int target_varno, int sublevels_up,
7474
const AttrNumber *attno_map, int map_length,
75-
bool *found_whole_row);
75+
Oid to_rowtype, bool *found_whole_row);
7676

7777
extern Node *ReplaceVarsFromTargetList(Node *node,
7878
int target_varno, int sublevels_up,

src/test/regress/expected/insert.out

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,3 +659,24 @@ select tableoid::regclass, * from mcrparted order by a, b;
659659
(11 rows)
660660

661661
drop table mcrparted;
662+
-- check that wholerow vars in the RETURNING list work with partitioned tables
663+
create table returningwrtest (a int) partition by list (a);
664+
create table returningwrtest1 partition of returningwrtest for values in (1);
665+
insert into returningwrtest values (1) returning returningwrtest;
666+
returningwrtest
667+
-----------------
668+
(1)
669+
(1 row)
670+
671+
-- check also that the wholerow vars in RETURNING list are converted as needed
672+
alter table returningwrtest add b text;
673+
create table returningwrtest2 (b text, c int, a int);
674+
alter table returningwrtest2 drop c;
675+
alter table returningwrtest attach partition returningwrtest2 for values in (2);
676+
insert into returningwrtest values (2, 'foo') returning returningwrtest;
677+
returningwrtest
678+
-----------------
679+
(2,foo)
680+
(1 row)
681+
682+
drop table returningwrtest;

src/test/regress/expected/updatable_views.out

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2428,3 +2428,29 @@ ERROR: new row violates check option for view "ptv_wco"
24282428
DETAIL: Failing row contains (1, 2, null).
24292429
drop view ptv, ptv_wco;
24302430
drop table pt, pt1, pt11;
2431+
-- check that wholerow vars appearing in WITH CHECK OPTION constraint expressions
2432+
-- work fine with partitioned tables
2433+
create table wcowrtest (a int) partition by list (a);
2434+
create table wcowrtest1 partition of wcowrtest for values in (1);
2435+
create view wcowrtest_v as select * from wcowrtest where wcowrtest = '(2)'::wcowrtest with check option;
2436+
insert into wcowrtest_v values (1);
2437+
ERROR: new row violates check option for view "wcowrtest_v"
2438+
DETAIL: Failing row contains (1).
2439+
alter table wcowrtest add b text;
2440+
create table wcowrtest2 (b text, c int, a int);
2441+
alter table wcowrtest2 drop c;
2442+
alter table wcowrtest attach partition wcowrtest2 for values in (2);
2443+
create table sometable (a int, b text);
2444+
insert into sometable values (1, 'a'), (2, 'b');
2445+
create view wcowrtest_v2 as
2446+
select *
2447+
from wcowrtest r
2448+
where r in (select s from sometable s where r.a = s.a)
2449+
with check option;
2450+
-- WITH CHECK qual will be processed with wcowrtest2's
2451+
-- rowtype after tuple-routing
2452+
insert into wcowrtest_v2 values (2, 'no such row in sometable');
2453+
ERROR: new row violates check option for view "wcowrtest_v2"
2454+
DETAIL: Failing row contains (2, no such row in sometable).
2455+
drop view wcowrtest_v, wcowrtest_v2;
2456+
drop table wcowrtest, sometable;

src/test/regress/sql/insert.sql

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,3 +399,16 @@ insert into mcrparted values ('aaa', 0), ('b', 0), ('bz', 10), ('c', -10),
399399
('commons', 0), ('d', -10), ('e', 0);
400400
select tableoid::regclass, * from mcrparted order by a, b;
401401
drop table mcrparted;
402+
403+
-- check that wholerow vars in the RETURNING list work with partitioned tables
404+
create table returningwrtest (a int) partition by list (a);
405+
create table returningwrtest1 partition of returningwrtest for values in (1);
406+
insert into returningwrtest values (1) returning returningwrtest;
407+
408+
-- check also that the wholerow vars in RETURNING list are converted as needed
409+
alter table returningwrtest add b text;
410+
create table returningwrtest2 (b text, c int, a int);
411+
alter table returningwrtest2 drop c;
412+
alter table returningwrtest attach partition returningwrtest2 for values in (2);
413+
insert into returningwrtest values (2, 'foo') returning returningwrtest;
414+
drop table returningwrtest;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy