postgres.git / summary / log / commit / refs

commit    3cb02e307e350caf3c9099c6f661a95fd00e7e4c
Author:   David Rowley <drowley@postgresql.org>
Date:     Sat Apr 18 02:10:37 2020 +0000
First released in: 13.0, 12.3

    Fix possible crash with GENERATED ALWAYS columns
    
    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 | 7 +++++++ src/test/regress/expected/generated.out | 12 ++++++++++++ src/test/regress/sql/generated.sql | 7 +++++++ 3 files changed, 26 insertions(+) diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index d71c0a43220..20a4c474cc4 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -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; } diff --git a/src/test/regress/expected/generated.out b/src/test/regress/expected/generated.out index 620579a6fd6..30fa320a395 100644 --- a/src/test/regress/expected/generated.out +++ b/src/test/regress/expected/generated.out @@ -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 ( diff --git a/src/test/regress/sql/generated.sql b/src/test/regress/sql/generated.sql index f0e6a22dac5..c13ede41075 100644 --- a/src/test/regress/sql/generated.sql +++ b/src/test/regress/sql/generated.sql @@ -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 ( [parent: 737d69ffc3cf]