* We must allow for cases where physical attnos of a partition can be
* different from the parent's.
*
+ * If found_whole_row is not NULL, *found_whole_row returns whether a
+ * whole-row variable was found in the input expression.
+ *
* Note: this will work on any node tree, so really the argument and result
* should be declared "Node *". But a substantial majority of the callers
* are working on Lists, so it's less messy to do the casts internally.
*/
List *
map_partition_varattnos(List *expr, int target_varno,
- Relation partrel, Relation parent)
+ Relation partrel, Relation parent,
+ bool *found_whole_row)
{
AttrNumber *part_attnos;
- bool found_whole_row;
+ bool my_found_whole_row;
if (expr == NIL)
return NIL;
target_varno, 0,
part_attnos,
RelationGetDescr(parent)->natts,
- &found_whole_row);
- /* There can never be a whole-row reference here */
+ RelationGetForm(partrel)->reltype,
+ &my_found_whole_row);
if (found_whole_row)
- elog(ERROR, "unexpected whole-row reference found in partition key");
+ *found_whole_row = my_found_whole_row;
return expr;
}
List *my_qual = NIL,
*result = NIL;
Relation parent;
+ bool found_whole_row;
/* Guard against stack overflow due to overly deep partition tree */
check_stack_depth();
* in it to bear this relation's attnos. It's safe to assume varno = 1
* here.
*/
- result = map_partition_varattnos(result, 1, rel, parent);
+ result = map_partition_varattnos(result, 1, rel, parent,
+ &found_whole_row);
+ /* There can never be a whole-row reference here */
+ if (found_whole_row)
+ elog(ERROR, "unexpected whole-row reference found in partition key");
/* Save a copy in the relcache */
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
expr = map_variable_attnos(stringToNode(check[i].ccbin),
1, 0,
newattno, tupleDesc->natts,
- &found_whole_row);
+ InvalidOid, &found_whole_row);
/*
* For the moment we have to reject whole-row variables. We
map_variable_attnos(def->cooked_default,
1, 0,
attmap, RelationGetDescr(rel)->natts,
- &found_whole_row);
+ InvalidOid, &found_whole_row);
if (found_whole_row)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
Oid part_relid = lfirst_oid(lc);
Relation part_rel;
Expr *constr;
+ bool found_whole_row;
/* Lock already taken */
if (part_relid != RelationGetRelid(attachRel))
constr = linitial(partConstraint);
tab->partition_constraint = (Expr *)
map_partition_varattnos((List *) constr, 1,
- part_rel, rel);
+ part_rel, rel,
+ &found_whole_row);
+ /* There can never be a whole-row reference here */
+ if (found_whole_row)
+ elog(ERROR, "unexpected whole-row reference found in partition key");
+
/* keep our lock until commit */
if (part_rel != attachRel)
heap_close(part_rel, NoLock);
/* varno = node->nominalRelation */
mapped_wcoList = map_partition_varattnos(wcoList,
node->nominalRelation,
- partrel, rel);
+ partrel, rel, NULL);
foreach(ll, mapped_wcoList)
{
WithCheckOption *wco = castNode(WithCheckOption, lfirst(ll));
/* varno = node->nominalRelation */
rlist = map_partition_varattnos(returningList,
node->nominalRelation,
- partrel, rel);
+ partrel, rel, NULL);
resultRelInfo->ri_projectReturning =
ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
resultRelInfo->ri_RelationDesc->rd_att);
ccbin_node = map_variable_attnos(stringToNode(ccbin),
1, 0,
attmap, tupleDesc->natts,
- &found_whole_row);
+ InvalidOid, &found_whole_row);
/*
* We reject whole-row variables because the whole point of LIKE
indexkey = map_variable_attnos(indexkey,
1, 0,
attmap, attmap_length,
- &found_whole_row);
+ InvalidOid, &found_whole_row);
/* As in transformTableLikeClause, reject whole-row variables */
if (found_whole_row)
pred_tree = map_variable_attnos(pred_tree,
1, 0,
attmap, attmap_length,
- &found_whole_row);
+ InvalidOid, &found_whole_row);
/* As in transformTableLikeClause, reject whole-row variables */
if (found_whole_row)
* appear in the expression.
*
* If the expression tree contains a whole-row Var for the target RTE,
- * the Var is not changed but *found_whole_row is returned as TRUE.
- * For most callers this is an error condition, but we leave it to the caller
- * to report the error so that useful context can be provided. (In some
- * usages it would be appropriate to modify the Var's vartype and insert a
- * ConvertRowtypeExpr node to map back to the original vartype. We might
- * someday extend this function's API to support that. For now, the only
- * concession to that future need is that this function is a tree mutator
- * not just a walker.)
+ * *found_whole_row is returned as TRUE. In addition, if to_rowtype is
+ * not InvalidOid, we modify the Var's vartype and insert a ConvertRowTypeExpr
+ * to map back to the orignal rowtype. Callers that don't provide to_rowtype
+ * should report an error if *found_row_type is true; we don't do that here
+ * because we don't know exactly what wording for the error message would
+ * be most appropriate. The caller will be aware of the context.
*
* This could be built using replace_rte_variables and a callback function,
* but since we don't ever need to insert sublinks, replace_rte_variables is
int sublevels_up; /* (current) nesting depth */
const AttrNumber *attno_map; /* map array for user attnos */
int map_length; /* number of entries in attno_map[] */
+ /* Target type when converting whole-row vars */
+ Oid to_rowtype;
bool *found_whole_row; /* output flag */
} map_variable_attnos_context;
{
/* whole-row variable, warn caller */
*(context->found_whole_row) = true;
+
+ /* If the callers expects us to convert the same, do so. */
+ if (OidIsValid(context->to_rowtype))
+ {
+ /* No support for RECORDOID. */
+ Assert(var->vartype != RECORDOID);
+
+ /* Don't convert unless necessary. */
+ if (context->to_rowtype != var->vartype)
+ {
+ ConvertRowtypeExpr *r;
+
+ /* Var itself is converted to the requested type. */
+ newvar->vartype = context->to_rowtype;
+
+ /*
+ * And a conversion node on top to convert back to the
+ * original type.
+ */
+ r = makeNode(ConvertRowtypeExpr);
+ r->arg = (Expr *) newvar;
+ r->resulttype = var->vartype;
+ r->convertformat = COERCE_IMPLICIT_CAST;
+ r->location = -1;
+
+ return (Node *) r;
+ }
+ }
}
return (Node *) newvar;
}
map_variable_attnos(Node *node,
int target_varno, int sublevels_up,
const AttrNumber *attno_map, int map_length,
- bool *found_whole_row)
+ Oid to_rowtype, bool *found_whole_row)
{
map_variable_attnos_context context;
context.sublevels_up = sublevels_up;
context.attno_map = attno_map;
context.map_length = map_length;
+ context.to_rowtype = to_rowtype;
context.found_whole_row = found_whole_row;
*found_whole_row = false;
extern List *get_qual_from_partbound(Relation rel, Relation parent,
PartitionBoundSpec *spec);
extern List *map_partition_varattnos(List *expr, int target_varno,
- Relation partrel, Relation parent);
+ Relation partrel, Relation parent,
+ bool *found_whole_row);
extern List *RelationGetPartitionQual(Relation rel);
extern Expr *get_partition_qual_relid(Oid relid);
extern Node *map_variable_attnos(Node *node,
int target_varno, int sublevels_up,
const AttrNumber *attno_map, int map_length,
- bool *found_whole_row);
+ Oid to_rowtype, bool *found_whole_row);
extern Node *ReplaceVarsFromTargetList(Node *node,
int target_varno, int sublevels_up,
(11 rows)
drop table mcrparted;
+-- check that wholerow vars in the RETURNING list work with partitioned tables
+create table returningwrtest (a int) partition by list (a);
+create table returningwrtest1 partition of returningwrtest for values in (1);
+insert into returningwrtest values (1) returning returningwrtest;
+ returningwrtest
+-----------------
+ (1)
+(1 row)
+
+-- check also that the wholerow vars in RETURNING list are converted as needed
+alter table returningwrtest add b text;
+create table returningwrtest2 (b text, c int, a int);
+alter table returningwrtest2 drop c;
+alter table returningwrtest attach partition returningwrtest2 for values in (2);
+insert into returningwrtest values (2, 'foo') returning returningwrtest;
+ returningwrtest
+-----------------
+ (2,foo)
+(1 row)
+
+drop table returningwrtest;
DETAIL: Failing row contains (1, 2, null).
drop view ptv, ptv_wco;
drop table pt, pt1, pt11;
+-- check that wholerow vars appearing in WITH CHECK OPTION constraint expressions
+-- work fine with partitioned tables
+create table wcowrtest (a int) partition by list (a);
+create table wcowrtest1 partition of wcowrtest for values in (1);
+create view wcowrtest_v as select * from wcowrtest where wcowrtest = '(2)'::wcowrtest with check option;
+insert into wcowrtest_v values (1);
+ERROR: new row violates check option for view "wcowrtest_v"
+DETAIL: Failing row contains (1).
+alter table wcowrtest add b text;
+create table wcowrtest2 (b text, c int, a int);
+alter table wcowrtest2 drop c;
+alter table wcowrtest attach partition wcowrtest2 for values in (2);
+create table sometable (a int, b text);
+insert into sometable values (1, 'a'), (2, 'b');
+create view wcowrtest_v2 as
+ select *
+ from wcowrtest r
+ where r in (select s from sometable s where r.a = s.a)
+with check option;
+-- WITH CHECK qual will be processed with wcowrtest2's
+-- rowtype after tuple-routing
+insert into wcowrtest_v2 values (2, 'no such row in sometable');
+ERROR: new row violates check option for view "wcowrtest_v2"
+DETAIL: Failing row contains (2, no such row in sometable).
+drop view wcowrtest_v, wcowrtest_v2;
+drop table wcowrtest, sometable;
('commons', 0), ('d', -10), ('e', 0);
select tableoid::regclass, * from mcrparted order by a, b;
drop table mcrparted;
+
+-- check that wholerow vars in the RETURNING list work with partitioned tables
+create table returningwrtest (a int) partition by list (a);
+create table returningwrtest1 partition of returningwrtest for values in (1);
+insert into returningwrtest values (1) returning returningwrtest;
+
+-- check also that the wholerow vars in RETURNING list are converted as needed
+alter table returningwrtest add b text;
+create table returningwrtest2 (b text, c int, a int);
+alter table returningwrtest2 drop c;
+alter table returningwrtest attach partition returningwrtest2 for values in (2);
+insert into returningwrtest values (2, 'foo') returning returningwrtest;
+drop table returningwrtest;
insert into ptv_wco values (1, 2);
drop view ptv, ptv_wco;
drop table pt, pt1, pt11;
+
+-- check that wholerow vars appearing in WITH CHECK OPTION constraint expressions
+-- work fine with partitioned tables
+create table wcowrtest (a int) partition by list (a);
+create table wcowrtest1 partition of wcowrtest for values in (1);
+create view wcowrtest_v as select * from wcowrtest where wcowrtest = '(2)'::wcowrtest with check option;
+insert into wcowrtest_v values (1);
+
+alter table wcowrtest add b text;
+create table wcowrtest2 (b text, c int, a int);
+alter table wcowrtest2 drop c;
+alter table wcowrtest attach partition wcowrtest2 for values in (2);
+
+create table sometable (a int, b text);
+insert into sometable values (1, 'a'), (2, 'b');
+create view wcowrtest_v2 as
+ select *
+ from wcowrtest r
+ where r in (select s from sometable s where r.a = s.a)
+with check option;
+
+-- WITH CHECK qual will be processed with wcowrtest2's
+-- rowtype after tuple-routing
+insert into wcowrtest_v2 values (2, 'no such row in sometable');
+
+drop view wcowrtest_v, wcowrtest_v2;
+drop table wcowrtest, sometable;