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; --