On 09/12/2017 12:11 PM, Simon Riggs wrote:
>
>> OK, so table-level option for "toast_tuple_target", not attribute-level 
>> option
>>
>> The attached patch and test shows this concept is useful and doesn't
>> affect existing data.
>>
>> For 4x 4000 byte rows:
>> * by default we use 1 heap block and 3 toast blocks
>> * toast_tuple_target=4080 uses 2 heap blocks and 0 toast blocks
>
> New patch, v2, since one line in the docs failed to apply because of
> recent changes.
>



This has bitrotted ever so slightly. I fixed that and took the
opportunity to edit the docs text slightly to improve the clarity a bit.
Revised patch attached - adjust to your taste.

The code is extremely simple and small and straightforward, and the
tests are good.

Marking as Ready for Committer,

cheers

andrew

-- 
Andrew Dunstan                https://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 3b19ea7..92db00f 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -629,7 +629,7 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
 
      <para>
       <literal>SHARE UPDATE EXCLUSIVE</literal> lock will be taken for
-      fillfactor and autovacuum storage parameters, as well as the
+      fillfactor, toast and autovacuum storage parameters, as well as the
       following planner related parameters:
       <varname>effective_io_concurrency</varname>, <varname>parallel_workers</varname>, <varname>seq_page_cost</varname>,
       <varname>random_page_cost</varname>, <varname>n_distinct</varname> and <varname>n_distinct_inherited</varname>.
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index bbb3a51..83eef7f 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -1201,6 +1201,27 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
    </varlistentry>
 
    <varlistentry>
+    <term><literal>toast_tuple_target</> (<type>integer</>)</term>
+    <listitem>
+     <para>
+      The toast_tuple_target specifies the minimum tuple length required before
+      we try to move long column values into TOAST tables, and is also the
+      target length we try to reduce the length below once toasting begins.
+      This only affects columns marked as either External or Extended
+      and applies only to new tuples - there is no effect on existing rows.
+      By default this parameter is set to allow at least 4 tuples per block,
+      which with the default blocksize will be 2040 bytes. Valid values are
+      between 128 bytes and the (blocksize - header), by default 8160 bytes.
+      Changing this value may not be useful for very short or very long rows.
+      Note that the default setting is often close to optimal, and
+      it is possible that setting this parameter could have negative
+      effects in some cases.
+      This parameter cannot be set for TOAST tables.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><literal>parallel_workers</literal> (<type>integer</type>)</term>
     <listitem>
      <para>
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 3d0ce9a..aa9c0f1 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -23,6 +23,7 @@
 #include "access/nbtree.h"
 #include "access/reloptions.h"
 #include "access/spgist.h"
+#include "access/tuptoaster.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/tablespace.h"
@@ -292,6 +293,15 @@ static relopt_int intRelOpts[] =
 	},
 	{
 		{
+			"toast_tuple_target",
+			"Sets the target tuple length at which external columns will be toasted",
+			RELOPT_KIND_HEAP,
+			ShareUpdateExclusiveLock
+		},
+		TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN
+	},
+	{
+		{
 			"pages_per_range",
 			"Number of pages that each page range covers in a BRIN index",
 			RELOPT_KIND_BRIN,
@@ -1344,6 +1354,8 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
 		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_table_age)},
 		{"log_autovacuum_min_duration", RELOPT_TYPE_INT,
 		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_min_duration)},
+		{"toast_tuple_target", RELOPT_TYPE_INT,
+		offsetof(StdRdOptions, toast_tuple_target)},
 		{"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
 		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_scale_factor)},
 		{"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 5a8f1da..c74945a 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -727,7 +727,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 		hoff += sizeof(Oid);
 	hoff = MAXALIGN(hoff);
 	/* now convert to a limit on the tuple data size */
-	maxDataLen = TOAST_TUPLE_TARGET - hoff;
+	maxDataLen = RelationGetToastTupleTarget(rel, TOAST_TUPLE_TARGET) - hoff;
 
 	/*
 	 * Look for attributes with attstorage 'x' to compress.  Also find large
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 4bc61e5..68fd6fb 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -277,6 +277,7 @@ typedef struct StdRdOptions
 {
 	int32		vl_len_;		/* varlena header (do not touch directly!) */
 	int			fillfactor;		/* page fill factor in percent (0..100) */
+	int			toast_tuple_target; /* target for tuple toasting */
 	AutoVacOpts autovacuum;		/* autovacuum-related options */
 	bool		user_catalog_table; /* use as an additional catalog relation */
 	int			parallel_workers;	/* max number of parallel workers */
@@ -286,6 +287,14 @@ typedef struct StdRdOptions
 #define HEAP_DEFAULT_FILLFACTOR		100
 
 /*
+ * RelationGetToastTupleTarget
+ *		Returns the relation's toast_tuple_target.  Note multiple eval of argument!
+ */
+#define RelationGetToastTupleTarget(relation, defaulttarg) \
+	((relation)->rd_options ? \
+	 ((StdRdOptions *) (relation)->rd_options)->toast_tuple_target : (defaulttarg))
+
+/*
  * RelationGetFillFactor
  *		Returns the relation's fillfactor.  Note multiple eval of argument!
  */
diff --git a/src/test/regress/expected/strings.out b/src/test/regress/expected/strings.out
index 35cadb2..3a42ef7 100644
--- a/src/test/regress/expected/strings.out
+++ b/src/test/regress/expected/strings.out
@@ -1166,6 +1166,53 @@ SELECT substr(f1, 99995, 10) from toasttest;
  567890
 (4 rows)
 
+TRUNCATE TABLE toasttest;
+INSERT INTO toasttest values (repeat('1234567890',400));
+INSERT INTO toasttest values (repeat('1234567890',400));
+INSERT INTO toasttest values (repeat('1234567890',400));
+INSERT INTO toasttest values (repeat('1234567890',400));
+SELECT pg_relation_size('toasttest')/current_setting('block_size')::integer as blocks;
+ blocks 
+--------
+      1
+(1 row)
+
+select pg_relation_size('pg_toast.pg_toast_'||(select oid from pg_class where relname = 'toasttest'))/current_setting('block_size')::integer as blocks;
+ blocks 
+--------
+      3
+(1 row)
+
+SELECT pg_total_relation_size('toasttest')/current_setting('block_size')::integer as blocks;
+ blocks 
+--------
+      9
+(1 row)
+
+TRUNCATE TABLE toasttest;
+ALTER TABLE toasttest set (toast_tuple_target = 4080);
+INSERT INTO toasttest values (repeat('1234567890',400));
+INSERT INTO toasttest values (repeat('1234567890',400));
+INSERT INTO toasttest values (repeat('1234567890',400));
+INSERT INTO toasttest values (repeat('1234567890',400));
+SELECT pg_relation_size('toasttest')/current_setting('block_size')::integer as blocks;
+ blocks 
+--------
+      2
+(1 row)
+
+select pg_relation_size('pg_toast.pg_toast_'||(select oid from pg_class where relname = 'toasttest'))/current_setting('block_size')::integer as blocks;
+ blocks 
+--------
+      0
+(1 row)
+
+SELECT pg_total_relation_size('toasttest')/current_setting('block_size')::integer as blocks;
+ blocks 
+--------
+      6
+(1 row)
+
 DROP TABLE toasttest;
 --
 -- test substr with toasted bytea values
diff --git a/src/test/regress/sql/strings.sql b/src/test/regress/sql/strings.sql
index f9cfaeb..6396693 100644
--- a/src/test/regress/sql/strings.sql
+++ b/src/test/regress/sql/strings.sql
@@ -366,6 +366,25 @@ SELECT substr(f1, 99995) from toasttest;
 -- string length
 SELECT substr(f1, 99995, 10) from toasttest;
 
+TRUNCATE TABLE toasttest;
+INSERT INTO toasttest values (repeat('1234567890',400));
+INSERT INTO toasttest values (repeat('1234567890',400));
+INSERT INTO toasttest values (repeat('1234567890',400));
+INSERT INTO toasttest values (repeat('1234567890',400));
+SELECT pg_relation_size('toasttest')/current_setting('block_size')::integer as blocks;
+select pg_relation_size('pg_toast.pg_toast_'||(select oid from pg_class where relname = 'toasttest'))/current_setting('block_size')::integer as blocks;
+SELECT pg_total_relation_size('toasttest')/current_setting('block_size')::integer as blocks;
+
+TRUNCATE TABLE toasttest;
+ALTER TABLE toasttest set (toast_tuple_target = 4080);
+INSERT INTO toasttest values (repeat('1234567890',400));
+INSERT INTO toasttest values (repeat('1234567890',400));
+INSERT INTO toasttest values (repeat('1234567890',400));
+INSERT INTO toasttest values (repeat('1234567890',400));
+SELECT pg_relation_size('toasttest')/current_setting('block_size')::integer as blocks;
+select pg_relation_size('pg_toast.pg_toast_'||(select oid from pg_class where relname = 'toasttest'))/current_setting('block_size')::integer as blocks;
+SELECT pg_total_relation_size('toasttest')/current_setting('block_size')::integer as blocks;
+
 DROP TABLE toasttest;
 
 --

Reply via email to