mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	Disallow whole-row variables in GENERATED expressions.
This was previously allowed, but I think that was just an oversight. It's a clear violation of the rule that a generated column cannot depend on itself or other generated columns. Moreover, because the code was relying on the assumption that no such cross-references exist, it was pretty easy to crash ALTER TABLE and perhaps other places. Even if you managed not to crash, you got quite unstable, implementation-dependent results. Per report from Vitaly Ustinov. Back-patch to v12 where GENERATED came in. Discussion: https://postgr.es/m/CAM_DEiWR2DPT6U4xb-Ehigozzd3n3G37ZB1+867zbsEVtYoJww@mail.gmail.com
This commit is contained in:
		@@ -3020,15 +3020,26 @@ check_nested_generated_walker(Node *node, void *context)
 | 
				
			|||||||
		AttrNumber	attnum;
 | 
							AttrNumber	attnum;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		relid = rt_fetch(var->varno, pstate->p_rtable)->relid;
 | 
							relid = rt_fetch(var->varno, pstate->p_rtable)->relid;
 | 
				
			||||||
 | 
							if (!OidIsValid(relid))
 | 
				
			||||||
 | 
								return false;		/* XXX shouldn't we raise an error? */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		attnum = var->varattno;
 | 
							attnum = var->varattno;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (OidIsValid(relid) && AttributeNumberIsValid(attnum) && get_attgenerated(relid, attnum))
 | 
							if (attnum > 0 && get_attgenerated(relid, attnum))
 | 
				
			||||||
			ereport(ERROR,
 | 
								ereport(ERROR,
 | 
				
			||||||
					(errcode(ERRCODE_SYNTAX_ERROR),
 | 
										(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 | 
				
			||||||
					 errmsg("cannot use generated column \"%s\" in column generation expression",
 | 
										 errmsg("cannot use generated column \"%s\" in column generation expression",
 | 
				
			||||||
							get_attname(relid, attnum, false)),
 | 
												get_attname(relid, attnum, false)),
 | 
				
			||||||
					 errdetail("A generated column cannot reference another generated column."),
 | 
										 errdetail("A generated column cannot reference another generated column."),
 | 
				
			||||||
					 parser_errposition(pstate, var->location)));
 | 
										 parser_errposition(pstate, var->location)));
 | 
				
			||||||
 | 
							/* A whole-row Var is necessarily self-referential, so forbid it */
 | 
				
			||||||
 | 
							if (attnum == 0)
 | 
				
			||||||
 | 
								ereport(ERROR,
 | 
				
			||||||
 | 
										(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 | 
				
			||||||
 | 
										 errmsg("cannot use whole-row variable in column generation expression"),
 | 
				
			||||||
 | 
										 errdetail("This would cause the generated column to depend on its own value."),
 | 
				
			||||||
 | 
										 parser_errposition(pstate, var->location)));
 | 
				
			||||||
 | 
							/* System columns were already checked in the parser */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,6 +46,13 @@ ERROR:  cannot use generated column "b" in column generation expression
 | 
				
			|||||||
LINE 1: ...AYS AS (a * 2) STORED, c int GENERATED ALWAYS AS (b * 3) STO...
 | 
					LINE 1: ...AYS AS (a * 2) STORED, c int GENERATED ALWAYS AS (b * 3) STO...
 | 
				
			||||||
                                                             ^
 | 
					                                                             ^
 | 
				
			||||||
DETAIL:  A generated column cannot reference another generated column.
 | 
					DETAIL:  A generated column cannot reference another generated column.
 | 
				
			||||||
 | 
					-- a whole-row var is a self-reference on steroids, so disallow that too
 | 
				
			||||||
 | 
					CREATE TABLE gtest_err_2c (a int PRIMARY KEY,
 | 
				
			||||||
 | 
					    b int GENERATED ALWAYS AS (num_nulls(gtest_err_2c)) STORED);
 | 
				
			||||||
 | 
					ERROR:  cannot use whole-row variable in column generation expression
 | 
				
			||||||
 | 
					LINE 2:     b int GENERATED ALWAYS AS (num_nulls(gtest_err_2c)) STOR...
 | 
				
			||||||
 | 
					                                                 ^
 | 
				
			||||||
 | 
					DETAIL:  This would cause the generated column to depend on its own value.
 | 
				
			||||||
-- invalid reference
 | 
					-- invalid reference
 | 
				
			||||||
CREATE TABLE gtest_err_3 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (c * 2) STORED);
 | 
					CREATE TABLE gtest_err_3 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (c * 2) STORED);
 | 
				
			||||||
ERROR:  column "c" does not exist
 | 
					ERROR:  column "c" does not exist
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,9 @@ CREATE TABLE gtest_err_1 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) S
 | 
				
			|||||||
-- references to other generated columns, including self-references
 | 
					-- references to other generated columns, including self-references
 | 
				
			||||||
CREATE TABLE gtest_err_2a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (b * 2) STORED);
 | 
					CREATE TABLE gtest_err_2a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (b * 2) STORED);
 | 
				
			||||||
CREATE TABLE gtest_err_2b (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED, c int GENERATED ALWAYS AS (b * 3) STORED);
 | 
					CREATE TABLE gtest_err_2b (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED, c int GENERATED ALWAYS AS (b * 3) STORED);
 | 
				
			||||||
 | 
					-- a whole-row var is a self-reference on steroids, so disallow that too
 | 
				
			||||||
 | 
					CREATE TABLE gtest_err_2c (a int PRIMARY KEY,
 | 
				
			||||||
 | 
					    b int GENERATED ALWAYS AS (num_nulls(gtest_err_2c)) STORED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- invalid reference
 | 
					-- invalid reference
 | 
				
			||||||
CREATE TABLE gtest_err_3 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (c * 2) STORED);
 | 
					CREATE TABLE gtest_err_3 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (c * 2) STORED);
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user