summaryrefslogtreecommitdiff
path: root/src/test/regress
diff options
context:
space:
mode:
authorRobert Haas2013-07-02 17:35:14 +0000
committerRobert Haas2013-07-02 17:38:55 +0000
commit3682025015390a8e802e0752589162db7bd70b5d (patch)
treeb72433fb328d27b2289204e2c19282de29633f22 /src/test/regress
parent148326b9940c6f3aa554df83a70c7d4563f67d86 (diff)
Add support for multiple kinds of external toast datums.
To that end, support tags rather than lengths for external datums. As an example of how this can be used, add support or "indirect" tuples which point to some externally allocated memory containing a toast tuple. Similar infrastructure could be used for other purposes, including, perhaps, support for alternative compression algorithms. Andres Freund, reviewed by Hitoshi Harada and myself
Diffstat (limited to 'src/test/regress')
-rw-r--r--src/test/regress/expected/indirect_toast.out151
-rw-r--r--src/test/regress/input/create_function_1.source5
-rw-r--r--src/test/regress/output/create_function_1.source4
-rw-r--r--src/test/regress/parallel_schedule2
-rw-r--r--src/test/regress/regress.c92
-rw-r--r--src/test/regress/serial_schedule1
-rw-r--r--src/test/regress/sql/indirect_toast.sql61
7 files changed, 315 insertions, 1 deletions
diff --git a/src/test/regress/expected/indirect_toast.out b/src/test/regress/expected/indirect_toast.out
new file mode 100644
index 00000000000..4f4bf41973a
--- /dev/null
+++ b/src/test/regress/expected/indirect_toast.out
@@ -0,0 +1,151 @@
+CREATE TABLE toasttest(descr text, cnt int DEFAULT 0, f1 text, f2 text);
+INSERT INTO toasttest(descr, f1, f2) VALUES('two-compressed', repeat('1234567890',1000), repeat('1234567890',1000));
+INSERT INTO toasttest(descr, f1, f2) VALUES('two-toasted', repeat('1234567890',30000), repeat('1234567890',50000));
+INSERT INTO toasttest(descr, f1, f2) VALUES('one-compressed,one-null', NULL, repeat('1234567890',1000));
+INSERT INTO toasttest(descr, f1, f2) VALUES('one-toasted,one-null', NULL, repeat('1234567890',50000));
+-- check whether indirect tuples works on the most basic level
+SELECT descr, substring(make_tuple_indirect(toasttest)::text, 1, 200) FROM toasttest;
+ descr | substring
+-------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ two-compressed | (two-compressed,0,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012
+ two-toasted | (two-toasted,0,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345
+ one-compressed,one-null | ("one-compressed,one-null",0,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+ one-toasted,one-null | ("one-toasted,one-null",0,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
+(4 rows)
+
+-- modification without changing varlenas
+UPDATE toasttest SET cnt = cnt +1 RETURNING substring(toasttest::text, 1, 200);
+ substring
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ (two-compressed,1,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012
+ (two-toasted,1,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345
+ ("one-compressed,one-null",1,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+ ("one-toasted,one-null",1,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
+(4 rows)
+
+-- modification without modifying asigned value
+UPDATE toasttest SET cnt = cnt +1, f1 = f1 RETURNING substring(toasttest::text, 1, 200);
+ substring
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ (two-compressed,2,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012
+ (two-toasted,2,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345
+ ("one-compressed,one-null",2,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+ ("one-toasted,one-null",2,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
+(4 rows)
+
+-- modification modifying, but effectively not changing
+UPDATE toasttest SET cnt = cnt +1, f1 = f1||'' RETURNING substring(toasttest::text, 1, 200);
+ substring
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ (two-compressed,3,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012
+ (two-toasted,3,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345
+ ("one-compressed,one-null",3,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+ ("one-toasted,one-null",3,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
+(4 rows)
+
+UPDATE toasttest SET cnt = cnt +1, f1 = '-'||f1||'-' RETURNING substring(toasttest::text, 1, 200);
+ substring
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ (two-compressed,4,-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901
+ (two-toasted,4,-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234
+ ("one-compressed,one-null",4,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+ ("one-toasted,one-null",4,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
+(4 rows)
+
+SELECT substring(toasttest::text, 1, 200) FROM toasttest;
+ substring
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ (two-compressed,4,-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901
+ (two-toasted,4,-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234
+ ("one-compressed,one-null",4,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+ ("one-toasted,one-null",4,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
+(4 rows)
+
+-- check we didn't screw with main/toast tuple visiblity
+VACUUM FREEZE toasttest;
+SELECT substring(toasttest::text, 1, 200) FROM toasttest;
+ substring
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ (two-compressed,4,-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901
+ (two-toasted,4,-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234
+ ("one-compressed,one-null",4,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+ ("one-toasted,one-null",4,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
+(4 rows)
+
+-- now create a trigger that forces all Datums to be indirect ones
+CREATE FUNCTION update_using_indirect()
+ RETURNS trigger
+ LANGUAGE plpgsql AS $$
+BEGIN
+ NEW := make_tuple_indirect(NEW);
+ RETURN NEW;
+END$$;
+CREATE TRIGGER toasttest_update_indirect
+ BEFORE INSERT OR UPDATE
+ ON toasttest
+ FOR EACH ROW
+ EXECUTE PROCEDURE update_using_indirect();
+-- modification without changing varlenas
+UPDATE toasttest SET cnt = cnt +1 RETURNING substring(toasttest::text, 1, 200);
+ substring
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ (two-compressed,5,-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901
+ (two-toasted,5,-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234
+ ("one-compressed,one-null",5,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+ ("one-toasted,one-null",5,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
+(4 rows)
+
+-- modification without modifying asigned value
+UPDATE toasttest SET cnt = cnt +1, f1 = f1 RETURNING substring(toasttest::text, 1, 200);
+ substring
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ (two-compressed,6,-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901
+ (two-toasted,6,-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234
+ ("one-compressed,one-null",6,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+ ("one-toasted,one-null",6,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
+(4 rows)
+
+-- modification modifying, but effectively not changing
+UPDATE toasttest SET cnt = cnt +1, f1 = f1||'' RETURNING substring(toasttest::text, 1, 200);
+ substring
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ (two-compressed,7,-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901
+ (two-toasted,7,-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234
+ ("one-compressed,one-null",7,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+ ("one-toasted,one-null",7,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
+(4 rows)
+
+UPDATE toasttest SET cnt = cnt +1, f1 = '-'||f1||'-' RETURNING substring(toasttest::text, 1, 200);
+ substring
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ (two-compressed,8,--123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+ (two-toasted,8,--123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
+ ("one-compressed,one-null",8,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+ ("one-toasted,one-null",8,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
+(4 rows)
+
+INSERT INTO toasttest(descr, f1, f2) VALUES('one-toasted,one-null, via indirect', repeat('1234567890',30000), NULL);
+SELECT substring(toasttest::text, 1, 200) FROM toasttest;
+ substring
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ (two-compressed,8,--123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+ (two-toasted,8,--123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
+ ("one-compressed,one-null",8,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+ ("one-toasted,one-null",8,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
+ ("one-toasted,one-null, via indirect",0,1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+(5 rows)
+
+-- check we didn't screw with main/toast tuple visiblity
+VACUUM FREEZE toasttest;
+SELECT substring(toasttest::text, 1, 200) FROM toasttest;
+ substring
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ (two-compressed,8,--123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+ (two-toasted,8,--123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
+ ("one-compressed,one-null",8,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+ ("one-toasted,one-null",8,,12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
+ ("one-toasted,one-null, via indirect",0,1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+(5 rows)
+
+DROP TABLE toasttest;
+DROP FUNCTION update_using_indirect();
diff --git a/src/test/regress/input/create_function_1.source b/src/test/regress/input/create_function_1.source
index a72dd9861c5..aef15182874 100644
--- a/src/test/regress/input/create_function_1.source
+++ b/src/test/regress/input/create_function_1.source
@@ -52,6 +52,11 @@ CREATE FUNCTION set_ttdummy (int4)
AS '@libdir@/regress@DLSUFFIX@'
LANGUAGE C STRICT;
+CREATE FUNCTION make_tuple_indirect (record)
+ RETURNS record
+ AS '@libdir@/regress@DLSUFFIX@'
+ LANGUAGE C STRICT;
+
-- Things that shouldn't work:
CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
diff --git a/src/test/regress/output/create_function_1.source b/src/test/regress/output/create_function_1.source
index 61b87ed953a..9761d127e1f 100644
--- a/src/test/regress/output/create_function_1.source
+++ b/src/test/regress/output/create_function_1.source
@@ -47,6 +47,10 @@ CREATE FUNCTION set_ttdummy (int4)
RETURNS int4
AS '@libdir@/regress@DLSUFFIX@'
LANGUAGE C STRICT;
+CREATE FUNCTION make_tuple_indirect (record)
+ RETURNS record
+ AS '@libdir@/regress@DLSUFFIX@'
+ LANGUAGE C STRICT;
-- Things that shouldn't work:
CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
AS 'SELECT ''not an integer'';';
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 2af28b15029..4bb9cc78579 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -98,7 +98,7 @@ test: event_trigger
# ----------
# Another group of parallel tests
# ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json
+test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json indirect_toast
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index e5136cfa7c8..3bd8a152859 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -7,7 +7,9 @@
#include <float.h>
#include <math.h>
+#include "access/htup_details.h"
#include "access/transam.h"
+#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/pg_type.h"
#include "commands/sequence.h"
@@ -17,6 +19,8 @@
#include "utils/builtins.h"
#include "utils/geo_decls.h"
#include "utils/rel.h"
+#include "utils/typcache.h"
+#include "utils/memutils.h"
#define P_MAXDIG 12
@@ -35,6 +39,7 @@ extern char *reverse_name(char *string);
extern int oldstyle_length(int n, text *t);
extern Datum int44in(PG_FUNCTION_ARGS);
extern Datum int44out(PG_FUNCTION_ARGS);
+extern Datum make_tuple_indirect(PG_FUNCTION_ARGS);
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
@@ -737,3 +742,90 @@ int44out(PG_FUNCTION_ARGS)
*--walk = '\0';
PG_RETURN_CSTRING(result);
}
+
+PG_FUNCTION_INFO_V1(make_tuple_indirect);
+Datum
+make_tuple_indirect(PG_FUNCTION_ARGS)
+{
+ HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0);
+ HeapTupleData tuple;
+ int ncolumns;
+ Datum *values;
+ bool *nulls;
+
+ Oid tupType;
+ int32 tupTypmod;
+ TupleDesc tupdesc;
+
+ HeapTuple newtup;
+
+ int i;
+
+ MemoryContext old_context;
+
+ /* Extract type info from the tuple itself */
+ tupType = HeapTupleHeaderGetTypeId(rec);
+ tupTypmod = HeapTupleHeaderGetTypMod(rec);
+ tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+ ncolumns = tupdesc->natts;
+
+ /* Build a temporary HeapTuple control structure */
+ tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
+ ItemPointerSetInvalid(&(tuple.t_self));
+ tuple.t_tableOid = InvalidOid;
+ tuple.t_data = rec;
+
+ values = (Datum *) palloc(ncolumns * sizeof(Datum));
+ nulls = (bool *) palloc(ncolumns * sizeof(bool));
+
+ heap_deform_tuple(&tuple, tupdesc, values, nulls);
+
+ old_context = MemoryContextSwitchTo(TopTransactionContext);
+
+ for (i = 0; i < ncolumns; i++)
+ {
+ struct varlena *attr;
+ struct varlena *new_attr;
+ struct varatt_indirect redirect_pointer;
+
+ /* only work on existing, not-null varlenas */
+ if (tupdesc->attrs[i]->attisdropped ||
+ nulls[i] ||
+ tupdesc->attrs[i]->attlen != -1)
+ continue;
+
+ attr = (struct varlena *) DatumGetPointer(values[i]);
+
+ /* don't recursively indirect */
+ if (VARATT_IS_EXTERNAL_INDIRECT(attr))
+ continue;
+
+ /* copy datum, so it still lives later */
+ if (VARATT_IS_EXTERNAL_ONDISK(attr))
+ attr = heap_tuple_fetch_attr(attr);
+ else
+ {
+ struct varlena *oldattr = attr;
+ attr = palloc0(VARSIZE_ANY(oldattr));
+ memcpy(attr, oldattr, VARSIZE_ANY(oldattr));
+ }
+
+ /* build indirection Datum */
+ new_attr = (struct varlena *) palloc0(INDIRECT_POINTER_SIZE);
+ redirect_pointer.pointer = attr;
+ SET_VARTAG_EXTERNAL(new_attr, VARTAG_INDIRECT);
+ memcpy(VARDATA_EXTERNAL(new_attr), &redirect_pointer,
+ sizeof(redirect_pointer));
+
+ values[i] = PointerGetDatum(new_attr);
+ }
+
+ newtup = heap_form_tuple(tupdesc, values, nulls);
+ pfree(values);
+ pfree(nulls);
+ ReleaseTupleDesc(tupdesc);
+
+ MemoryContextSwitchTo(old_context);
+
+ PG_RETURN_HEAPTUPLEHEADER(newtup->t_data);
+}
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index d6eaa7aa4da..ceeca734d3a 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -117,6 +117,7 @@ test: xmlmap
test: functional_deps
test: advisory_lock
test: json
+test: indirect_toast
test: plancache
test: limit
test: plpgsql
diff --git a/src/test/regress/sql/indirect_toast.sql b/src/test/regress/sql/indirect_toast.sql
new file mode 100644
index 00000000000..d502480ad3f
--- /dev/null
+++ b/src/test/regress/sql/indirect_toast.sql
@@ -0,0 +1,61 @@
+CREATE TABLE toasttest(descr text, cnt int DEFAULT 0, f1 text, f2 text);
+
+INSERT INTO toasttest(descr, f1, f2) VALUES('two-compressed', repeat('1234567890',1000), repeat('1234567890',1000));
+INSERT INTO toasttest(descr, f1, f2) VALUES('two-toasted', repeat('1234567890',30000), repeat('1234567890',50000));
+INSERT INTO toasttest(descr, f1, f2) VALUES('one-compressed,one-null', NULL, repeat('1234567890',1000));
+INSERT INTO toasttest(descr, f1, f2) VALUES('one-toasted,one-null', NULL, repeat('1234567890',50000));
+
+-- check whether indirect tuples works on the most basic level
+SELECT descr, substring(make_tuple_indirect(toasttest)::text, 1, 200) FROM toasttest;
+
+-- modification without changing varlenas
+UPDATE toasttest SET cnt = cnt +1 RETURNING substring(toasttest::text, 1, 200);
+
+-- modification without modifying asigned value
+UPDATE toasttest SET cnt = cnt +1, f1 = f1 RETURNING substring(toasttest::text, 1, 200);
+
+-- modification modifying, but effectively not changing
+UPDATE toasttest SET cnt = cnt +1, f1 = f1||'' RETURNING substring(toasttest::text, 1, 200);
+
+UPDATE toasttest SET cnt = cnt +1, f1 = '-'||f1||'-' RETURNING substring(toasttest::text, 1, 200);
+
+SELECT substring(toasttest::text, 1, 200) FROM toasttest;
+-- check we didn't screw with main/toast tuple visiblity
+VACUUM FREEZE toasttest;
+SELECT substring(toasttest::text, 1, 200) FROM toasttest;
+
+-- now create a trigger that forces all Datums to be indirect ones
+CREATE FUNCTION update_using_indirect()
+ RETURNS trigger
+ LANGUAGE plpgsql AS $$
+BEGIN
+ NEW := make_tuple_indirect(NEW);
+ RETURN NEW;
+END$$;
+
+CREATE TRIGGER toasttest_update_indirect
+ BEFORE INSERT OR UPDATE
+ ON toasttest
+ FOR EACH ROW
+ EXECUTE PROCEDURE update_using_indirect();
+
+-- modification without changing varlenas
+UPDATE toasttest SET cnt = cnt +1 RETURNING substring(toasttest::text, 1, 200);
+
+-- modification without modifying asigned value
+UPDATE toasttest SET cnt = cnt +1, f1 = f1 RETURNING substring(toasttest::text, 1, 200);
+
+-- modification modifying, but effectively not changing
+UPDATE toasttest SET cnt = cnt +1, f1 = f1||'' RETURNING substring(toasttest::text, 1, 200);
+
+UPDATE toasttest SET cnt = cnt +1, f1 = '-'||f1||'-' RETURNING substring(toasttest::text, 1, 200);
+
+INSERT INTO toasttest(descr, f1, f2) VALUES('one-toasted,one-null, via indirect', repeat('1234567890',30000), NULL);
+
+SELECT substring(toasttest::text, 1, 200) FROM toasttest;
+-- check we didn't screw with main/toast tuple visiblity
+VACUUM FREEZE toasttest;
+SELECT substring(toasttest::text, 1, 200) FROM toasttest;
+
+DROP TABLE toasttest;
+DROP FUNCTION update_using_indirect();