#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/memutils.h"
#include "utils/rel.h"
MemoryContext oldContext;
Datum *values;
bool *nulls;
- bool *replaces;
- HeapTuple oldtuple, newtuple;
- bool should_free;
Assert(tupdesc->constr && tupdesc->constr->has_generated_stored);
values = palloc(sizeof(*values) * natts);
nulls = palloc(sizeof(*nulls) * natts);
- replaces = palloc0(sizeof(*replaces) * natts);
+
+ slot_getallattrs(slot);
+ memcpy(nulls, slot->tts_isnull, sizeof(*nulls) * natts);
for (int i = 0; i < natts; i++)
{
- if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED)
+ Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+
+ if (attr->attgenerated == ATTRIBUTE_GENERATED_STORED)
{
ExprContext *econtext;
Datum val;
values[i] = val;
nulls[i] = isnull;
- replaces[i] = true;
+ }
+ else
+ {
+ if (!nulls[i])
+ values[i] = datumCopy(slot->tts_values[i], attr->attbyval, attr->attlen);
}
}
- oldtuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
- newtuple = heap_modify_tuple(oldtuple, tupdesc, values, nulls, replaces);
- /*
- * The tuple will be freed by way of the memory context - the slot might
- * only be cleared after the context is reset, and we'd thus potentially
- * double free.
- */
- ExecForceStoreHeapTuple(newtuple, slot, false);
- if (should_free)
- heap_freetuple(oldtuple);
+ ExecClearTuple(slot);
+ memcpy(slot->tts_values, values, sizeof(*values) * natts);
+ memcpy(slot->tts_isnull, nulls, sizeof(*nulls) * natts);
+ ExecStoreVirtualTuple(slot);
+ ExecMaterializeSlot(slot);
MemoryContextSwitchTo(oldContext);
}
ERROR: inherited column "b" has a generation conflict
DROP TABLE gtesty;
-- test stored update
-CREATE TABLE gtest3 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 3) STORED);
-INSERT INTO gtest3 (a) VALUES (1), (2), (3);
+CREATE TABLE gtest3 (a int, b int GENERATED ALWAYS AS (a * 3) STORED);
+INSERT INTO gtest3 (a) VALUES (1), (2), (3), (NULL);
SELECT * FROM gtest3 ORDER BY a;
a | b
---+---
1 | 3
2 | 6
3 | 9
-(3 rows)
+ |
+(4 rows)
UPDATE gtest3 SET a = 22 WHERE a = 2;
SELECT * FROM gtest3 ORDER BY a;
1 | 3
3 | 9
22 | 66
-(3 rows)
+ |
+(4 rows)
+
+CREATE TABLE gtest3a (a text, b text GENERATED ALWAYS AS (a || '+' || a) STORED);
+INSERT INTO gtest3a (a) VALUES ('a'), ('b'), ('c'), (NULL);
+SELECT * FROM gtest3a ORDER BY a;
+ a | b
+---+-----
+ a | a+a
+ b | b+b
+ c | c+c
+ |
+(4 rows)
+
+UPDATE gtest3a SET a = 'bb' WHERE a = 'b';
+SELECT * FROM gtest3a ORDER BY a;
+ a | b
+----+-------
+ a | a+a
+ bb | bb+bb
+ c | c+c
+ |
+(4 rows)
-- COPY
TRUNCATE gtest1;
DROP TABLE gtesty;
-- test stored update
-CREATE TABLE gtest3 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 3) STORED);
-INSERT INTO gtest3 (a) VALUES (1), (2), (3);
+CREATE TABLE gtest3 (a int, b int GENERATED ALWAYS AS (a * 3) STORED);
+INSERT INTO gtest3 (a) VALUES (1), (2), (3), (NULL);
SELECT * FROM gtest3 ORDER BY a;
UPDATE gtest3 SET a = 22 WHERE a = 2;
SELECT * FROM gtest3 ORDER BY a;
+CREATE TABLE gtest3a (a text, b text GENERATED ALWAYS AS (a || '+' || a) STORED);
+INSERT INTO gtest3a (a) VALUES ('a'), ('b'), ('c'), (NULL);
+SELECT * FROM gtest3a ORDER BY a;
+UPDATE gtest3a SET a = 'bb' WHERE a = 'b';
+SELECT * FROM gtest3a ORDER BY a;
+
-- COPY
TRUNCATE gtest1;
INSERT INTO gtest1 (a) VALUES (1), (2);