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:
@@ -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);
|
||||
|
||||
@@ -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))));
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -13383,12 +13384,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,
|
||||
|
||||
@@ -57,6 +57,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 202607271
|
||||
#define CATALOG_VERSION_NO 202607272
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -2436,7 +2436,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 */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user