Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit e3931d0

Browse files
committed
Use multi-inserts for pg_attribute and pg_shdepend
For pg_attribute, this allows to insert at once a full set of attributes for a relation (roughly 15% of WAL reduction in extreme cases). For pg_shdepend, this reduces the work done when creating new shared dependencies from a database template. The number of slots used for the insertion is capped at 64kB of data inserted for both, depending on the number of items to insert and the length of the rows involved. More can be done for other catalogs, like pg_depend. This part requires a different approach as the number of slots to use depends also on the number of entries discarded as pinned dependencies. This is also related to the rework or dependency handling for ALTER TABLE and CREATE TABLE, mainly. Author: Daniel Gustafsson Reviewed-by: Andres Freund, Michael Paquier Discussion: https://postgr.es/m/20190213182737.mxn6hkdxwrzgxk35@alap3.anarazel.de
1 parent cab2556 commit e3931d0

File tree

8 files changed

+225
-119
lines changed

8 files changed

+225
-119
lines changed

src/backend/access/heap/heapam.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -2164,8 +2164,8 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
21642164
RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
21652165

21662166
/*
2167-
* Note that heap_multi_insert is not used for catalog tuples yet, but
2168-
* this will cover the gap once that is the case.
2167+
* For logical decoding we need combocids to properly decode the
2168+
* catalog.
21692169
*/
21702170
if (needwal && need_cids)
21712171
log_heap_new_cid(relation, heaptuples[ndone]);
@@ -2180,8 +2180,8 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
21802180
RelationPutHeapTuple(relation, buffer, heaptup, false);
21812181

21822182
/*
2183-
* We don't use heap_multi_insert for catalog tuples yet, but
2184-
* better be prepared...
2183+
* For logical decoding we need combocids to properly decode the
2184+
* catalog.
21852185
*/
21862186
if (needwal && need_cids)
21872187
log_heap_new_cid(relation, heaptup);

src/backend/catalog/heap.c

+120-79
Original file line numberDiff line numberDiff line change
@@ -710,70 +710,122 @@ CheckAttributeType(const char *attname,
710710
}
711711

712712
/*
713-
* InsertPgAttributeTuple
714-
* Construct and insert a new tuple in pg_attribute.
713+
* Cap the maximum amount of bytes allocated for InsertPgAttributeTuples()
714+
* slots.
715+
*/
716+
#define MAX_PGATTRIBUTE_INSERT_BYTES 65535
717+
718+
/*
719+
* InsertPgAttributeTuples
720+
* Construct and insert a set of tuples in pg_attribute.
715721
*
716-
* Caller has already opened and locked pg_attribute. new_attribute is the
717-
* attribute to insert. attcacheoff is always initialized to -1, attacl,
718-
* attfdwoptions and attmissingval are always initialized to NULL.
722+
* Caller has already opened and locked pg_attribute. tupdesc contains the
723+
* attributes to insert. attcacheoff is always initialized to -1, attacl,
724+
* attfdwoptions and attmissingval are always initialized to NULL. attoptions
725+
* must contain the same number of elements as tupdesc, or be NULL.
719726
*
720727
* indstate is the index state for CatalogTupleInsertWithInfo. It can be
721728
* passed as NULL, in which case we'll fetch the necessary info. (Don't do
722729
* this when inserting multiple attributes, because it's a tad more
723730
* expensive.)
731+
*
732+
* new_rel_oid is the relation OID assigned to the attributes inserted.
733+
* If set to InvalidOid, the relation OID from tupdesc is used instead.
724734
*/
725735
void
726-
InsertPgAttributeTuple(Relation pg_attribute_rel,
727-
Form_pg_attribute new_attribute,
728-
Datum attoptions,
729-
CatalogIndexState indstate)
736+
InsertPgAttributeTuples(Relation pg_attribute_rel,
737+
TupleDesc tupdesc,
738+
Oid new_rel_oid,
739+
Datum *attoptions,
740+
CatalogIndexState indstate)
730741
{
731-
Datum values[Natts_pg_attribute];
732-
bool nulls[Natts_pg_attribute];
733-
HeapTuple tup;
742+
TupleTableSlot **slot;
743+
TupleDesc td;
744+
int nslots;
745+
int natts = 0;
746+
int slotCount = 0;
747+
bool close_index = false;
748+
749+
td = RelationGetDescr(pg_attribute_rel);
750+
751+
/* Initialize the number of slots to use */
752+
nslots = Min(tupdesc->natts,
753+
(MAX_PGATTRIBUTE_INSERT_BYTES / sizeof(FormData_pg_attribute)));
754+
slot = palloc(sizeof(TupleTableSlot *) * nslots);
755+
for (int i = 0; i < nslots; i++)
756+
slot[i] = MakeSingleTupleTableSlot(td, &TTSOpsHeapTuple);
757+
758+
while (natts < tupdesc->natts)
759+
{
760+
Form_pg_attribute attrs = TupleDescAttr(tupdesc, natts);
734761

735-
/* This is a tad tedious, but way cleaner than what we used to do... */
736-
memset(values, 0, sizeof(values));
737-
memset(nulls, false, sizeof(nulls));
762+
ExecClearTuple(slot[slotCount]);
738763

739-
values[Anum_pg_attribute_attrelid - 1] = ObjectIdGetDatum(new_attribute->attrelid);
740-
values[Anum_pg_attribute_attname - 1] = NameGetDatum(&new_attribute->attname);
741-
values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(new_attribute->atttypid);
742-
values[Anum_pg_attribute_attstattarget - 1] = Int32GetDatum(new_attribute->attstattarget);
743-
values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(new_attribute->attlen);
744-
values[Anum_pg_attribute_attnum - 1] = Int16GetDatum(new_attribute->attnum);
745-
values[Anum_pg_attribute_attndims - 1] = Int32GetDatum(new_attribute->attndims);
746-
values[Anum_pg_attribute_attcacheoff - 1] = Int32GetDatum(-1);
747-
values[Anum_pg_attribute_atttypmod - 1] = Int32GetDatum(new_attribute->atttypmod);
748-
values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(new_attribute->attbyval);
749-
values[Anum_pg_attribute_attstorage - 1] = CharGetDatum(new_attribute->attstorage);
750-
values[Anum_pg_attribute_attalign - 1] = CharGetDatum(new_attribute->attalign);
751-
values[Anum_pg_attribute_attnotnull - 1] = BoolGetDatum(new_attribute->attnotnull);
752-
values[Anum_pg_attribute_atthasdef - 1] = BoolGetDatum(new_attribute->atthasdef);
753-
values[Anum_pg_attribute_atthasmissing - 1] = BoolGetDatum(new_attribute->atthasmissing);
754-
values[Anum_pg_attribute_attidentity - 1] = CharGetDatum(new_attribute->attidentity);
755-
values[Anum_pg_attribute_attgenerated - 1] = CharGetDatum(new_attribute->attgenerated);
756-
values[Anum_pg_attribute_attisdropped - 1] = BoolGetDatum(new_attribute->attisdropped);
757-
values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
758-
values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
759-
values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
760-
values[Anum_pg_attribute_attoptions - 1] = attoptions;
761-
762-
/* start out with empty permissions and empty options */
763-
nulls[Anum_pg_attribute_attacl - 1] = true;
764-
nulls[Anum_pg_attribute_attoptions - 1] = attoptions == (Datum) 0;
765-
nulls[Anum_pg_attribute_attfdwoptions - 1] = true;
766-
nulls[Anum_pg_attribute_attmissingval - 1] = true;
767-
768-
tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls);
764+
if (new_rel_oid != InvalidOid)
765+
slot[slotCount]->tts_values[Anum_pg_attribute_attrelid - 1] = ObjectIdGetDatum(new_rel_oid);
766+
else
767+
slot[slotCount]->tts_values[Anum_pg_attribute_attrelid - 1] = ObjectIdGetDatum(attrs->attrelid);
768+
769+
slot[slotCount]->tts_values[Anum_pg_attribute_attname - 1] = NameGetDatum(&attrs->attname);
770+
slot[slotCount]->tts_values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(attrs->atttypid);
771+
slot[slotCount]->tts_values[Anum_pg_attribute_attstattarget - 1] = Int32GetDatum(attrs->attstattarget);
772+
slot[slotCount]->tts_values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(attrs->attlen);
773+
slot[slotCount]->tts_values[Anum_pg_attribute_attnum - 1] = Int16GetDatum(attrs->attnum);
774+
slot[slotCount]->tts_values[Anum_pg_attribute_attndims - 1] = Int32GetDatum(attrs->attndims);
775+
slot[slotCount]->tts_values[Anum_pg_attribute_attcacheoff - 1] = Int32GetDatum(-1);
776+
slot[slotCount]->tts_values[Anum_pg_attribute_atttypmod - 1] = Int32GetDatum(attrs->atttypmod);
777+
slot[slotCount]->tts_values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(attrs->attbyval);
778+
slot[slotCount]->tts_values[Anum_pg_attribute_attstorage - 1] = CharGetDatum(attrs->attstorage);
779+
slot[slotCount]->tts_values[Anum_pg_attribute_attalign - 1] = CharGetDatum(attrs->attalign);
780+
slot[slotCount]->tts_values[Anum_pg_attribute_attnotnull - 1] = BoolGetDatum(attrs->attnotnull);
781+
slot[slotCount]->tts_values[Anum_pg_attribute_atthasdef - 1] = BoolGetDatum(attrs->atthasdef);
782+
slot[slotCount]->tts_values[Anum_pg_attribute_atthasmissing - 1] = BoolGetDatum(attrs->atthasmissing);
783+
slot[slotCount]->tts_values[Anum_pg_attribute_attidentity - 1] = CharGetDatum(attrs->attidentity);
784+
slot[slotCount]->tts_values[Anum_pg_attribute_attgenerated - 1] = CharGetDatum(attrs->attgenerated);
785+
slot[slotCount]->tts_values[Anum_pg_attribute_attisdropped - 1] = BoolGetDatum(attrs->attisdropped);
786+
slot[slotCount]->tts_values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(attrs->attislocal);
787+
slot[slotCount]->tts_values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(attrs->attinhcount);
788+
slot[slotCount]->tts_values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(attrs->attcollation);
789+
if (attoptions && attoptions[natts] != (Datum) 0)
790+
slot[slotCount]->tts_values[Anum_pg_attribute_attoptions - 1] = attoptions[natts];
791+
else
792+
slot[slotCount]->tts_isnull[Anum_pg_attribute_attoptions - 1] = true;
769793

770-
/* finally insert the new tuple, update the indexes, and clean up */
771-
if (indstate != NULL)
772-
CatalogTupleInsertWithInfo(pg_attribute_rel, tup, indstate);
773-
else
774-
CatalogTupleInsert(pg_attribute_rel, tup);
794+
/* start out with empty permissions and empty options */
795+
slot[slotCount]->tts_isnull[Anum_pg_attribute_attacl - 1] = true;
796+
slot[slotCount]->tts_isnull[Anum_pg_attribute_attfdwoptions - 1] = true;
797+
slot[slotCount]->tts_isnull[Anum_pg_attribute_attmissingval - 1] = true;
775798

776-
heap_freetuple(tup);
799+
ExecStoreVirtualTuple(slot[slotCount]);
800+
slotCount++;
801+
802+
/*
803+
* If slots are full or the end of processing has been reached, insert
804+
* a batch of tuples.
805+
*/
806+
if (slotCount == nslots || natts == tupdesc->natts - 1)
807+
{
808+
/* fetch index info only when we know we need it */
809+
if (!indstate)
810+
{
811+
indstate = CatalogOpenIndexes(pg_attribute_rel);
812+
close_index = true;
813+
}
814+
815+
/* insert the new tuples and update the indexes */
816+
CatalogTuplesMultiInsertWithInfo(pg_attribute_rel, slot, slotCount,
817+
indstate);
818+
slotCount = 0;
819+
}
820+
821+
natts++;
822+
}
823+
824+
if (close_index)
825+
CatalogCloseIndexes(indstate);
826+
for (int i = 0; i < nslots; i++)
827+
ExecDropSingleTupleTableSlot(slot[i]);
828+
pfree(slot);
777829
}
778830

779831
/* --------------------------------
@@ -788,8 +840,6 @@ AddNewAttributeTuples(Oid new_rel_oid,
788840
TupleDesc tupdesc,
789841
char relkind)
790842
{
791-
Form_pg_attribute attr;
792-
int i;
793843
Relation rel;
794844
CatalogIndexState indstate;
795845
int natts = tupdesc->natts;
@@ -803,30 +853,26 @@ AddNewAttributeTuples(Oid new_rel_oid,
803853

804854
indstate = CatalogOpenIndexes(rel);
805855

806-
/*
807-
* First we add the user attributes. This is also a convenient place to
808-
* add dependencies on their datatypes and collations.
809-
*/
810-
for (i = 0; i < natts; i++)
811-
{
812-
attr = TupleDescAttr(tupdesc, i);
813-
/* Fill in the correct relation OID */
814-
attr->attrelid = new_rel_oid;
815-
/* Make sure this is OK, too */
816-
attr->attstattarget = -1;
817-
818-
InsertPgAttributeTuple(rel, attr, (Datum) 0, indstate);
856+
/* set stats detail level to a sane default */
857+
for (int i = 0; i < natts; i++)
858+
tupdesc->attrs[i].attstattarget = -1;
859+
InsertPgAttributeTuples(rel, tupdesc, new_rel_oid, NULL, indstate);
819860

861+
/* add dependencies on their datatypes and collations */
862+
for (int i = 0; i < natts; i++)
863+
{
820864
/* Add dependency info */
821865
ObjectAddressSubSet(myself, RelationRelationId, new_rel_oid, i + 1);
822-
ObjectAddressSet(referenced, TypeRelationId, attr->atttypid);
866+
ObjectAddressSet(referenced, TypeRelationId,
867+
tupdesc->attrs[i].atttypid);
823868
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
824869

825870
/* The default collation is pinned, so don't bother recording it */
826-
if (OidIsValid(attr->attcollation) &&
827-
attr->attcollation != DEFAULT_COLLATION_OID)
871+
if (OidIsValid(tupdesc->attrs[i].attcollation) &&
872+
tupdesc->attrs[i].attcollation != DEFAULT_COLLATION_OID)
828873
{
829-
ObjectAddressSet(referenced, CollationRelationId, attr->attcollation);
874+
ObjectAddressSet(referenced, CollationRelationId,
875+
tupdesc->attrs[i].attcollation);
830876
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
831877
}
832878
}
@@ -838,17 +884,12 @@ AddNewAttributeTuples(Oid new_rel_oid,
838884
*/
839885
if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE)
840886
{
841-
for (i = 0; i < (int) lengthof(SysAtt); i++)
842-
{
843-
FormData_pg_attribute attStruct;
887+
TupleDesc td;
844888

845-
memcpy(&attStruct, SysAtt[i], sizeof(FormData_pg_attribute));
889+
td = CreateTupleDesc(lengthof(SysAtt), (FormData_pg_attribute **) &SysAtt);
846890

847-
/* Fill in the correct relation OID in the copied tuple */
848-
attStruct.attrelid = new_rel_oid;
849-
850-
InsertPgAttributeTuple(rel, &attStruct, (Datum) 0, indstate);
851-
}
891+
InsertPgAttributeTuples(rel, td, new_rel_oid, NULL, indstate);
892+
FreeTupleDesc(td);
852893
}
853894

854895
/*

src/backend/catalog/index.c

+4-15
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,7 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation,
106106
Oid *classObjectId);
107107
static void InitializeAttributeOids(Relation indexRelation,
108108
int numatts, Oid indexoid);
109-
static void AppendAttributeTuples(Relation indexRelation, int numatts,
110-
Datum *attopts);
109+
static void AppendAttributeTuples(Relation indexRelation, Datum *attopts);
111110
static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
112111
Oid parentIndexId,
113112
IndexInfo *indexInfo,
@@ -485,12 +484,11 @@ InitializeAttributeOids(Relation indexRelation,
485484
* ----------------------------------------------------------------
486485
*/
487486
static void
488-
AppendAttributeTuples(Relation indexRelation, int numatts, Datum *attopts)
487+
AppendAttributeTuples(Relation indexRelation, Datum *attopts)
489488
{
490489
Relation pg_attribute;
491490
CatalogIndexState indstate;
492491
TupleDesc indexTupDesc;
493-
int i;
494492

495493
/*
496494
* open the attribute relation and its indexes
@@ -504,15 +502,7 @@ AppendAttributeTuples(Relation indexRelation, int numatts, Datum *attopts)
504502
*/
505503
indexTupDesc = RelationGetDescr(indexRelation);
506504

507-
for (i = 0; i < numatts; i++)
508-
{
509-
Form_pg_attribute attr = TupleDescAttr(indexTupDesc, i);
510-
Datum attoptions = attopts ? attopts[i] : (Datum) 0;
511-
512-
Assert(attr->attnum == i + 1);
513-
514-
InsertPgAttributeTuple(pg_attribute, attr, attoptions, indstate);
515-
}
505+
InsertPgAttributeTuples(pg_attribute, indexTupDesc, InvalidOid, attopts, indstate);
516506

517507
CatalogCloseIndexes(indstate);
518508

@@ -979,8 +969,7 @@ index_create(Relation heapRelation,
979969
/*
980970
* append ATTRIBUTE tuples for the index
981971
*/
982-
AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs,
983-
indexInfo->ii_OpclassOptions);
972+
AppendAttributeTuples(indexRelation, indexInfo->ii_OpclassOptions);
984973

985974
/* ----------------
986975
* update pg_index

src/backend/catalog/indexing.c

+36
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "access/genam.h"
1919
#include "access/heapam.h"
2020
#include "access/htup_details.h"
21+
#include "access/xact.h"
2122
#include "catalog/index.h"
2223
#include "catalog/indexing.h"
2324
#include "executor/executor.h"
@@ -250,6 +251,41 @@ CatalogTupleInsertWithInfo(Relation heapRel, HeapTuple tup,
250251
CatalogIndexInsert(indstate, tup);
251252
}
252253

254+
/*
255+
* CatalogTuplesMultiInsertWithInfo - as above, but for multiple tuples
256+
*
257+
* Insert multiple tuples into the given catalog relation at once, with an
258+
* amortized cost of CatalogOpenIndexes.
259+
*/
260+
void
261+
CatalogTuplesMultiInsertWithInfo(Relation heapRel, TupleTableSlot **slot,
262+
int ntuples, CatalogIndexState indstate)
263+
{
264+
/* Nothing to do */
265+
if (ntuples <= 0)
266+
return;
267+
268+
heap_multi_insert(heapRel, slot, ntuples,
269+
GetCurrentCommandId(true), 0, NULL);
270+
271+
/*
272+
* There is no equivalent to heap_multi_insert for the catalog indexes, so
273+
* we must loop over and insert individually.
274+
*/
275+
for (int i = 0; i < ntuples; i++)
276+
{
277+
bool should_free;
278+
HeapTuple tuple;
279+
280+
tuple = ExecFetchSlotHeapTuple(slot[i], true, &should_free);
281+
tuple->t_tableOid = slot[i]->tts_tableOid;
282+
CatalogIndexInsert(indstate, tuple);
283+
284+
if (should_free)
285+
heap_freetuple(tuple);
286+
}
287+
}
288+
253289
/*
254290
* CatalogTupleUpdate - do heap and indexing work for updating a catalog tuple
255291
*

0 commit comments

Comments
 (0)