Fix possible crash with GENERATED ALWAYS columns
authorDavid Rowley <drowley@postgresql.org>
Sat, 18 Apr 2020 02:10:37 +0000 (14:10 +1200)
committerDavid Rowley <drowley@postgresql.org>
Sat, 18 Apr 2020 02:10:37 +0000 (14:10 +1200)
In some corner cases, this could also lead to corrupted values being
included in the tuple.

Users who are concerned that they are affected by this should first
upgrade and then perform a base backup of their database and restore onto
an off-line server. They should then query each table with generated
columns to ensure there are no rows where the generated expression does
not match a newly calculated version of the GENERATED ALWAYS expression.
If no crashes occur and no rows are returned then you're not affected.

Fixes bug #16369.

Reported-by: Cameron Ezell
Discussion: https://postgr.es/m/16369-5845a6f1bef59884@postgresql.org
Backpatch-through: 12 (where GENERATED ALWAYS columns were added.)

src/backend/executor/nodeModifyTable.c
src/test/regress/expected/generated.out
src/test/regress/sql/generated.sql

index d71c0a432207c41976244344d792308ad329c29b..20a4c474cc47f5e1ee8ffba4444d2e19f1e255e0 100644 (file)
@@ -336,6 +336,13 @@ ExecComputeStoredGenerated(EState *estate, TupleTableSlot *slot, CmdType cmdtype
 
                        val = ExecEvalExpr(resultRelInfo->ri_GeneratedExprs[i], econtext, &isnull);
 
+                       /*
+                        * We must make a copy of val as we have no guarantees about where
+                        * memory for a pass-by-reference Datum is located.
+                        */
+                       if (!isnull)
+                               val = datumCopy(val, attr->attbyval, attr->attlen);
+
                        values[i] = val;
                        nulls[i] = isnull;
                }
index 620579a6fd62a72e488075369f8097981b32227f..30fa320a3959cda3a5d6d1ad19e37b194a45a240 100644 (file)
@@ -320,6 +320,18 @@ SELECT * FROM gtest2;
  1 |  
 (1 row)
 
+-- simple column reference for varlena types
+CREATE TABLE gtest_varlena (a varchar, b varchar GENERATED ALWAYS AS (a) STORED);
+INSERT INTO gtest_varlena (a) VALUES('01234567890123456789');
+INSERT INTO gtest_varlena (a) VALUES(NULL);
+SELECT * FROM gtest_varlena ORDER BY a;
+          a           |          b           
+----------------------+----------------------
+ 01234567890123456789 | 01234567890123456789
+                      | 
+(2 rows)
+
+DROP TABLE gtest_varlena;
 -- composite types
 CREATE TYPE double_int as (a int, b int);
 CREATE TABLE gtest4 (
index f0e6a22dac5110ed0b167c233dc6a6ab134b2c61..c13ede4107579b89f4505af5916a1e2975dcab3f 100644 (file)
@@ -145,6 +145,13 @@ CREATE TABLE gtest2 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (NULL) STORED)
 INSERT INTO gtest2 VALUES (1);
 SELECT * FROM gtest2;
 
+-- simple column reference for varlena types
+CREATE TABLE gtest_varlena (a varchar, b varchar GENERATED ALWAYS AS (a) STORED);
+INSERT INTO gtest_varlena (a) VALUES('01234567890123456789');
+INSERT INTO gtest_varlena (a) VALUES(NULL);
+SELECT * FROM gtest_varlena ORDER BY a;
+DROP TABLE gtest_varlena;
+
 -- composite types
 CREATE TYPE double_int as (a int, b int);
 CREATE TABLE gtest4 (