1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

Deparse FOR PORTION OF using the range column's current name.

Commit 8e72d914c recorded the range column's name in ForPortionOfExpr
and used that for deparsing FOR PORTION OF.  This gives the wrong
answer if the ForPortionOfExpr is saved in a rule or SQL function and
then the column gets renamed.  Drop the ForPortionOfExpr.range_name
field; instead fetch the current column name from the catalogs when
needed.

Also drop ForPortionOfState.fp_rangeName, which wasn't being used
anywhere.

Full disclosure: an earlier draft of this patch was made with
Claude Opus 4.8.

Reported-by: John Naylor <johncnaylorls@gmail.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/CANWCAZYFEpJ5Oi45gi4q9Y6LYa4_oiAXxuNNWe-1ym-i0fF8Pw@mail.gmail.com
Backpatch-through: 19
This commit is contained in:
Tom Lane
2026-07-27 09:37:04 -04:00
parent 2cad308cb8
commit 7090c696cc
9 changed files with 64 additions and 11 deletions

View File

@@ -5641,7 +5641,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
/* Create state for FOR PORTION OF operation */
fpoState = makeNode(ForPortionOfState);
fpoState->fp_rangeName = forPortionOf->range_name;
fpoState->fp_rangeType = forPortionOf->rangeType;
fpoState->fp_rangeAttno = forPortionOf->rangeVar->varattno;
fpoState->fp_targetRange = targetRange;
@@ -5928,7 +5927,6 @@ ExecInitForPortionOf(ModifyTableState *mtstate, EState *estate,
leafState = makeNode(ForPortionOfState);
leafState->fp_rangeName = fpoState->fp_rangeName;
leafState->fp_rangeType = fpoState->fp_rangeType;
leafState->fp_targetRange = fpoState->fp_targetRange;
map = ExecGetChildToRootMap(resultRelInfo);

View File

@@ -871,7 +871,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse, char *plan_name,
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot use generated column \"%s\" in FOR PORTION OF",
forPortionOf->range_name)));
get_attname(rte->relid,
forPortionOf->rangeVar->varattno,
false))));
}
/*

View File

@@ -1606,7 +1606,6 @@ transformForPortionOfClause(ParseState *pstate,
else
result->rangeTargetList = NIL;
result->range_name = forPortionOf->range_name;
result->location = forPortionOf->location;
result->targetLocation = forPortionOf->target_location;

View File

@@ -527,6 +527,7 @@ static void get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
static void get_column_alias_list(deparse_columns *colinfo,
deparse_context *context);
static void get_for_portion_of(ForPortionOfExpr *forPortionOf,
RangeTblEntry *rte,
deparse_context *context);
static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
deparse_columns *colinfo,
@@ -7556,7 +7557,7 @@ get_update_query_def(Query *query, deparse_context *context)
generate_relation_name(rte->relid, NIL));
/* Print the FOR PORTION OF, if needed */
get_for_portion_of(query->forPortionOf, context);
get_for_portion_of(query->forPortionOf, rte, context);
/* Print the relation alias, if needed */
get_rte_alias(rte, query->resultRelation, false, context);
@@ -7763,7 +7764,7 @@ get_delete_query_def(Query *query, deparse_context *context)
generate_relation_name(rte->relid, NIL));
/* Print the FOR PORTION OF, if needed */
get_for_portion_of(query->forPortionOf, context);
get_for_portion_of(query->forPortionOf, rte, context);
/* Print the relation alias, if needed */
get_rte_alias(rte, query->resultRelation, false, context);
@@ -13479,12 +13480,18 @@ get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
* alias and SET will be on their own line with a leading space.
*/
static void
get_for_portion_of(ForPortionOfExpr *forPortionOf, deparse_context *context)
get_for_portion_of(ForPortionOfExpr *forPortionOf, RangeTblEntry *rte,
deparse_context *context)
{
if (forPortionOf)
{
char *range_name;
range_name = get_attname(rte->relid,
forPortionOf->rangeVar->varattno,
false);
appendStringInfo(context->buf, " FOR PORTION OF %s",
quote_identifier(forPortionOf->range_name));
quote_identifier(range_name));
/*
* Try to write it as FROM ... TO ... if we received it that way,

View File

@@ -57,6 +57,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 202607271
#define CATALOG_VERSION_NO 202607273
#endif

View File

@@ -476,7 +476,6 @@ typedef struct ForPortionOfState
{
NodeTag type;
char *fp_rangeName; /* the column named in FOR PORTION OF */
Oid fp_rangeType; /* the base type (not domain) of the FOR
* PORTION OF expression */
int fp_rangeAttno; /* the attno of the range column */

View File

@@ -2438,7 +2438,6 @@ typedef struct ForPortionOfExpr
{
NodeTag type;
Var *rangeVar; /* Range column */
char *range_name; /* Range name */
Node *targetFrom; /* FOR PORTION OF FROM bound, if given */
Node *targetTo; /* FOR PORTION OF TO bound, if given */
Node *targetRange; /* FOR PORTION OF bounds as a range/multirange */

View File

@@ -2255,6 +2255,39 @@ SELECT * FROM fpo_rule ORDER BY f1;
(2 rows)
DROP TABLE fpo_rule;
-- Deparsing FOR PORTION OF must use the range column's current name,
-- not the name it had when the rule was created.
CREATE TABLE fpo_rename (f1 bigint, f2 int4range);
CREATE TABLE fpo_rename_src (x int);
CREATE RULE fpo_rename_rule1 AS ON UPDATE TO fpo_rename_src
DO INSTEAD UPDATE fpo_rename FOR PORTION OF f2 FROM 3 TO 6 SET f1 = 2;
CREATE RULE fpo_rename_rule2 AS ON DELETE TO fpo_rename_src
DO INSTEAD DELETE FROM fpo_rename FOR PORTION OF f2 (int4range(3, 6));
\d+ fpo_rename_src
Table "public.fpo_rename_src"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------+-----------+----------+---------+---------+--------------+-------------
x | integer | | | | plain | |
Rules:
fpo_rename_rule1 AS
ON UPDATE TO fpo_rename_src DO INSTEAD UPDATE fpo_rename FOR PORTION OF f2 FROM 3 TO 6 SET f1 = 2
fpo_rename_rule2 AS
ON DELETE TO fpo_rename_src DO INSTEAD DELETE FROM fpo_rename FOR PORTION OF f2 (int4range(3, 6))
ALTER TABLE fpo_rename RENAME COLUMN f1 TO ff1;
ALTER TABLE fpo_rename RENAME COLUMN f2 TO ff2;
\d+ fpo_rename_src
Table "public.fpo_rename_src"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------+-----------+----------+---------+---------+--------------+-------------
x | integer | | | | plain | |
Rules:
fpo_rename_rule1 AS
ON UPDATE TO fpo_rename_src DO INSTEAD UPDATE fpo_rename FOR PORTION OF ff2 FROM 3 TO 6 SET ff1 = 2
fpo_rename_rule2 AS
ON DELETE TO fpo_rename_src DO INSTEAD DELETE FROM fpo_rename FOR PORTION OF ff2 (int4range(3, 6))
DROP TABLE fpo_rename, fpo_rename_src;
-- UPDATE/DELETE FOR PORTION OF on a GENERATED VIRTUAL range column:
CREATE TABLE fpo_gen_virtual (
a int,

View File

@@ -1495,6 +1495,22 @@ SELECT * FROM fpo_rule ORDER BY f1;
DROP TABLE fpo_rule;
-- Deparsing FOR PORTION OF must use the range column's current name,
-- not the name it had when the rule was created.
CREATE TABLE fpo_rename (f1 bigint, f2 int4range);
CREATE TABLE fpo_rename_src (x int);
CREATE RULE fpo_rename_rule1 AS ON UPDATE TO fpo_rename_src
DO INSTEAD UPDATE fpo_rename FOR PORTION OF f2 FROM 3 TO 6 SET f1 = 2;
CREATE RULE fpo_rename_rule2 AS ON DELETE TO fpo_rename_src
DO INSTEAD DELETE FROM fpo_rename FOR PORTION OF f2 (int4range(3, 6));
\d+ fpo_rename_src
ALTER TABLE fpo_rename RENAME COLUMN f1 TO ff1;
ALTER TABLE fpo_rename RENAME COLUMN f2 TO ff2;
\d+ fpo_rename_src
DROP TABLE fpo_rename, fpo_rename_src;
-- UPDATE/DELETE FOR PORTION OF on a GENERATED VIRTUAL range column:
CREATE TABLE fpo_gen_virtual (
a int,