Fujita-san, Thanks for the review.
On 2017/08/03 16:01, Etsuro Fujita wrote: > On 2017/08/02 15:21, Amit Langote wrote: >> On 2017/08/02 1:33, Amit Khandekar wrote: >>> ------- >>> >>> Few more comments : >>> >>> @@ -1240,7 +1247,7 @@ map_variable_attnos_mutator(Node *node, >>> var->varlevelsup == context->sublevels_up) >>> { >>> /* Found a matching variable, make the substitution */ >>> >>> - Var *newvar = (Var *) palloc(sizeof(Var)); >>> + Var *newvar = copyObject(var); >>> int attno = var->varattno; >>> >>> *newvar = *var; >>> >>> Here, "*newvar = *var" should be removed. >> >> Done. > > I'm not sure this change is a good idea, because the copy by "*newvar = > *var" would be more efficient than the copyObject(). (We have this > optimization in other places as well. See eg, copyVar() in setrefs.c.) OK, done. > Here are some other comments: > > + /* If the callers expects us to convert the same, do so. */ > + if (OidIsValid(context->to_rowtype)) > + { > + ConvertRowtypeExpr *r; > + > + /* Var itself is converted to the requested rowtype. */ > + newvar->vartype = context->to_rowtype; > + > + /* > + * And a conversion step 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; > + } > > Why not do this conversion if to_rowtype is valid and it's different from > the rowtype of the original whole-row Var like the previous patch? Also, I > think it's better to add an assertion that the rowtype of the original > whole-row Var is a named one. So, what I have in mind is: > > if (OidIsValid(context->to_rowtype)) > { > Assert(var->vartype != RECORDOID) > if (var->vartype != context->to_rowtype) > { > ConvertRowtypeExpr *r; > > /* Var itself is converted to the requested rowtype. */ > ... > /* And a conversion step on top to convert back to the ... */ > ... > return (Node *) r; > } > } Sounds good, so done. > Here is the modification to the map_variable_attnos()'s API: > > map_variable_attnos(Node *node, > int target_varno, int sublevels_up, > const AttrNumber *attno_map, int > map_length, > - bool *found_whole_row) > + bool *found_whole_row, Oid > to_rowtype) > > This is nitpicking, but I think it would be better to put the new argument > to_rowtype right before the output argument found_whole_row. I consider this a good suggestion. I guess we tend to list all the input arguments before any output arguments. So done as you suggest. > + * RelationGetRelType > + * Returns the rel's pg_type OID. > + */ > +#define RelationGetRelType(relation) \ > + ((relation)->rd_rel->reltype) > > This macro is used in only one place. Do we really need that? (This > macro would be useful for other places such as expand_inherited_rtentry, > but I think it's better to introduce this in a separate patch, maybe for > PG11.) Alright, dropped RelationGetRelType from the patch. > +-- check that wholerow vars in the RETUNING list work with partitioned > tables > > Typo: s/RETUNING/RETURNING/ Fixed. Attached updated patches. Thanks, Amit
From 9b2d16ec4c8eadd7261849d5aa0f34ee2577b405 Mon Sep 17 00:00:00 2001 From: amit <amitlangot...@gmail.com> Date: Wed, 26 Jul 2017 16:45:46 +0900 Subject: [PATCH 1/2] Fix map_partition_varattnos to not error on found_whole_row It was designed assuming that the expressions passed to it can never contain whole-row vars, but it's wrong since it's called from places that pass it WCO constraint expressions and RETURNING target list expressions, which can very well contain whole-row vars. Move the responsibility of error'ing out to the callers, because they are in position to know that finding whole-row vars is an error condition. Adds test in insert.sql and updatable_views.sql. Reported by: Rajkumar Raghuwanshi Report: https://postgr.es/m/CAKcux6%3Dz38gH4K6YAFi%2BYvo5tHTwBL4tam4VM33CAPZ5dDMk1Q%40mail.gmail.com --- src/backend/catalog/partition.c | 20 ++++++++++++++------ src/backend/commands/tablecmds.c | 8 +++++++- src/backend/executor/nodeModifyTable.c | 18 ++++++++++++++---- src/include/catalog/partition.h | 3 ++- src/test/regress/expected/insert.out | 10 ++++++++++ src/test/regress/expected/updatable_views.out | 10 ++++++++++ src/test/regress/sql/insert.sql | 6 ++++++ src/test/regress/sql/updatable_views.sql | 9 +++++++++ 8 files changed, 72 insertions(+), 12 deletions(-) diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 9d50efb6a0..80ca928a9c 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -898,16 +898,20 @@ get_qual_from_partbound(Relation rel, Relation parent, * 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; @@ -919,10 +923,9 @@ map_partition_varattnos(List *expr, int target_varno, target_varno, 0, part_attnos, RelationGetDescr(parent)->natts, - &found_whole_row); - /* There can never be a whole-row reference here */ + &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; } @@ -1783,6 +1786,7 @@ generate_partition_qual(Relation rel) 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(); @@ -1825,7 +1829,11 @@ generate_partition_qual(Relation rel) * 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); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index bb00858ad1..cc5d3d6faf 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -13713,6 +13713,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd) Oid part_relid = lfirst_oid(lc); Relation part_rel; Expr *constr; + bool found_whole_row; /* Lock already taken */ if (part_relid != RelationGetRelid(attachRel)) @@ -13738,7 +13739,12 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd) 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); diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 0dde0ed6eb..c60a9f7952 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1993,10 +1993,15 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) List *wcoExprs = NIL; ListCell *ll; - /* varno = node->nominalRelation */ + /* + * We are passing node->nominalRelation as the varno to match + * Vars to be changed. We don't care to know whether wcoList + * contains a whole-row Var, so passing NULL for the last + * argument. + */ mapped_wcoList = map_partition_varattnos(wcoList, node->nominalRelation, - partrel, rel); + partrel, rel, NULL); foreach(ll, mapped_wcoList) { WithCheckOption *wco = castNode(WithCheckOption, lfirst(ll)); @@ -2066,10 +2071,15 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) Relation partrel = resultRelInfo->ri_RelationDesc; List *rlist; - /* varno = node->nominalRelation */ + /* + * We are passing node->nominalRelation as the varno to match + * Vars to be changed. We don't care to know whether + * returningList contains a whole-row Var, so passing NULL for + * the last argument. + */ 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); diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h index f10879a162..434ded37d7 100644 --- a/src/include/catalog/partition.h +++ b/src/include/catalog/partition.h @@ -80,7 +80,8 @@ extern Oid get_partition_parent(Oid relid); 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); diff --git a/src/test/regress/expected/insert.out b/src/test/regress/expected/insert.out index 0dcc86fef4..da08062b4a 100644 --- a/src/test/regress/expected/insert.out +++ b/src/test/regress/expected/insert.out @@ -659,3 +659,13 @@ select tableoid::regclass, * from mcrparted order by a, b; (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) + +drop table returningwrtest; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index eab5c0334c..51a21f10c2 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -2428,3 +2428,13 @@ ERROR: new row violates check option for view "ptv_wco" 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). +drop view wcowrtest_v; +drop table wcowrtest; diff --git a/src/test/regress/sql/insert.sql b/src/test/regress/sql/insert.sql index 6adf25da40..de7d54ee5e 100644 --- a/src/test/regress/sql/insert.sql +++ b/src/test/regress/sql/insert.sql @@ -399,3 +399,9 @@ insert into mcrparted values ('aaa', 0), ('b', 0), ('bz', 10), ('c', -10), ('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; +drop table returningwrtest; diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql index 2ede44c02b..af8499a019 100644 --- a/src/test/regress/sql/updatable_views.sql +++ b/src/test/regress/sql/updatable_views.sql @@ -1141,3 +1141,12 @@ create view ptv_wco as select * from pt where a = 0 with check option; 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); +drop view wcowrtest_v; +drop table wcowrtest; -- 2.11.0
From a6e3c8d8cd99e513bc0ab417adc9cd0ea8527249 Mon Sep 17 00:00:00 2001 From: amit <amitlangot...@gmail.com> Date: Mon, 31 Jul 2017 17:48:07 +0900 Subject: [PATCH 2/2] Teach map_variable_attnos_mutator to convert whole-row Vars Partitioning code that uses it requires it. --- src/backend/catalog/partition.c | 1 + src/backend/commands/tablecmds.c | 4 +- src/backend/parser/parse_utilcmd.c | 6 +-- src/backend/rewrite/rewriteManip.c | 57 ++++++++++++++++++++++----- src/include/rewrite/rewriteManip.h | 2 +- src/test/regress/expected/insert.out | 11 ++++++ src/test/regress/expected/updatable_views.out | 20 +++++++++- src/test/regress/sql/insert.sql | 7 ++++ src/test/regress/sql/updatable_views.sql | 22 ++++++++++- 9 files changed, 110 insertions(+), 20 deletions(-) diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 80ca928a9c..dcc7f8af27 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -923,6 +923,7 @@ map_partition_varattnos(List *expr, int target_varno, target_varno, 0, part_attnos, RelationGetDescr(parent)->natts, + RelationGetForm(partrel)->reltype, &my_found_whole_row); if (found_whole_row) *found_whole_row = my_found_whole_row; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cc5d3d6faf..b58c92d846 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1989,7 +1989,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence, 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 @@ -8874,7 +8874,7 @@ ATPrepAlterColumnType(List **wqueue, 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), diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 9f37f1b920..a86c2341f5 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1107,7 +1107,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla 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 @@ -1463,7 +1463,7 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx, 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) @@ -1539,7 +1539,7 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx, 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) diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index b89b435da0..297473f409 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -1202,15 +1202,18 @@ replace_rte_variables_mutator(Node *node, * A zero in the mapping array represents a dropped column, which should not * 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.) + * Depending on the caller, whole-row Vars in the expression tree are + * converted if the necessary information is provided (see below.) If the + * caller cannot provide the information, it might mean that finding a + * whole-row Var in the first place is an error. In that case, we let the + * caller know that a whole-row Var was found by returning *found_whole_row + * as TRUE, which the caller then can report as an error by providing useful + * context information. + * + * When the information necessary to convert whole-row Vars is present, we + * substitute the Var's vartype (replace by to_rowtype) and add a + * ConvertRowtypeExpr node to map back to the original vartype. Currently, + * only map_partition_varattnos() requests conversion of whole-row Vars. * * 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 @@ -1223,6 +1226,8 @@ typedef struct 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; @@ -1257,6 +1262,37 @@ map_variable_attnos_mutator(Node *node, { /* 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)) + { + /* + * Considering that the only caller who wants to get + * whole-row variables converted is concerned with + * partitioning, we can be sure that var is a named + * whole-row variable. + */ + Assert(var->vartype != RECORDOID); + 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; } @@ -1283,7 +1319,7 @@ 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) { map_variable_attnos_context context; @@ -1291,6 +1327,7 @@ map_variable_attnos(Node *node, 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; diff --git a/src/include/rewrite/rewriteManip.h b/src/include/rewrite/rewriteManip.h index 282ff9967f..f0a7a8b2cd 100644 --- a/src/include/rewrite/rewriteManip.h +++ b/src/include/rewrite/rewriteManip.h @@ -72,7 +72,7 @@ extern Node *replace_rte_variables_mutator(Node *node, 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, diff --git a/src/test/regress/expected/insert.out b/src/test/regress/expected/insert.out index da08062b4a..a2d9469592 100644 --- a/src/test/regress/expected/insert.out +++ b/src/test/regress/expected/insert.out @@ -668,4 +668,15 @@ insert into returningwrtest values (1) returning 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; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index 51a21f10c2..2090a411fe 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -2436,5 +2436,21 @@ create view wcowrtest_v as select * from wcowrtest where wcowrtest = '(2)'::wcow insert into wcowrtest_v values (1); ERROR: new row violates check option for view "wcowrtest_v" DETAIL: Failing row contains (1). -drop view wcowrtest_v; -drop table wcowrtest; +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; diff --git a/src/test/regress/sql/insert.sql b/src/test/regress/sql/insert.sql index de7d54ee5e..6f17872087 100644 --- a/src/test/regress/sql/insert.sql +++ b/src/test/regress/sql/insert.sql @@ -404,4 +404,11 @@ drop table mcrparted; 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; diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql index af8499a019..a6ba5aad9e 100644 --- a/src/test/regress/sql/updatable_views.sql +++ b/src/test/regress/sql/updatable_views.sql @@ -1148,5 +1148,23 @@ 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); -drop view wcowrtest_v; -drop table wcowrtest; + +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; -- 2.11.0
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers