From 167346887f538e2b05b76510b6d5dadfbad16d21 Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Mon, 31 Aug 2020 16:46:47 +0900 Subject: [PATCH v2 1/2] Use multi-inserts for pg_depend This is a follow-up of the work done in e3931d01. This case is a bit different than pg_attribute and pg_shdepend: the maximum number of items to insert is known in advance, but there is no need to handle pinned dependencies. Hence, the base allocation for slots is done based on the number of items and the maximum allowed with a cap at 64kB, and items are initialized once used to minimize the overhead of the operation. Author: Daniel Gustafsson, Michael Paquier Discussion: https://postgr.es/m/XXX --- src/include/catalog/indexing.h | 6 +++ src/backend/catalog/heap.c | 8 +-- src/backend/catalog/pg_depend.c | 89 ++++++++++++++++++++++--------- src/backend/catalog/pg_shdepend.c | 8 +-- 4 files changed, 71 insertions(+), 40 deletions(-) diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index a7e2a9b26b..2fbd9b18ed 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -29,6 +29,12 @@ */ typedef struct ResultRelInfo *CatalogIndexState; +/* + * Cap the maximum amount of bytes allocated for InsertPgAttributeTuples() + * slots. + */ +#define MAX_CATALOG_INSERT_BYTES 65535 + /* * indexing.c prototypes */ diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index abd5bdb866..9d3ebd93ad 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -709,12 +709,6 @@ CheckAttributeType(const char *attname, } } -/* - * Cap the maximum amount of bytes allocated for InsertPgAttributeTuples() - * slots. - */ -#define MAX_PGATTRIBUTE_INSERT_BYTES 65535 - /* * InsertPgAttributeTuples * Construct and insert a set of tuples in pg_attribute. @@ -750,7 +744,7 @@ InsertPgAttributeTuples(Relation pg_attribute_rel, /* Initialize the number of slots to use */ nslots = Min(tupdesc->natts, - (MAX_PGATTRIBUTE_INSERT_BYTES / sizeof(FormData_pg_attribute))); + (MAX_CATALOG_INSERT_BYTES / sizeof(FormData_pg_attribute))); slot = palloc(sizeof(TupleTableSlot *) * nslots); for (int i = 0; i < nslots; i++) slot[i] = MakeSingleTupleTableSlot(td, &TTSOpsHeapTuple); diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index 70baf03178..8c4844f146 100644 --- a/src/backend/catalog/pg_depend.c +++ b/src/backend/catalog/pg_depend.c @@ -59,10 +59,10 @@ recordMultipleDependencies(const ObjectAddress *depender, { Relation dependDesc; CatalogIndexState indstate; - HeapTuple tup; - int i; - bool nulls[Natts_pg_depend]; - Datum values[Natts_pg_depend]; + int slotCount, i; + TupleTableSlot **slot; + int nslots, max_slots; + bool slot_init = true; if (nreferenced <= 0) return; /* nothing to do */ @@ -76,11 +76,18 @@ recordMultipleDependencies(const ObjectAddress *depender, dependDesc = table_open(DependRelationId, RowExclusiveLock); + /* + * Allocate the slots to use, but delay initialization until we know that + * they will be used. + */ + max_slots = Min(nreferenced, + MAX_CATALOG_INSERT_BYTES / sizeof(FormData_pg_depend)); + slot = palloc(sizeof(TupleTableSlot *) * max_slots); + /* Don't open indexes unless we need to make an update */ indstate = NULL; - memset(nulls, false, sizeof(nulls)); - + slotCount = 0; for (i = 0; i < nreferenced; i++, referenced++) { /* @@ -88,38 +95,68 @@ recordMultipleDependencies(const ObjectAddress *depender, * need to record dependencies on it. This saves lots of space in * pg_depend, so it's worth the time taken to check. */ - if (!isObjectPinned(referenced, dependDesc)) + if (isObjectPinned(referenced, dependDesc)) + continue; + + if (slot_init) + slot[slotCount] = MakeSingleTupleTableSlot(RelationGetDescr(dependDesc), + &TTSOpsHeapTuple); + + ExecClearTuple(slot[slotCount]); + + /* + * Record the Dependency. Note we don't bother to check for duplicate + * dependencies; there's no harm in them. + */ + slot[slotCount]->tts_values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId); + slot[slotCount]->tts_values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId); + slot[slotCount]->tts_values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId); + slot[slotCount]->tts_values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior); + slot[slotCount]->tts_values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId); + slot[slotCount]->tts_values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId); + slot[slotCount]->tts_values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId); + + memset(slot[slotCount]->tts_isnull, false, + slot[slotCount]->tts_tupleDescriptor->natts * sizeof(bool)); + + ExecStoreVirtualTuple(slot[slotCount]); + slotCount++; + + /* If slots are full, insert a batch of tuples */ + if (slotCount == max_slots) { - /* - * Record the Dependency. Note we don't bother to check for - * duplicate dependencies; there's no harm in them. - */ - values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId); - values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId); - values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId); - - values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId); - values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId); - values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId); - - values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior); - - tup = heap_form_tuple(dependDesc->rd_att, values, nulls); - /* fetch index info only when we know we need it */ if (indstate == NULL) indstate = CatalogOpenIndexes(dependDesc); - CatalogTupleInsertWithInfo(dependDesc, tup, indstate); - - heap_freetuple(tup); + CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slotCount, + indstate); + slotCount = 0; + slot_init = false; } } + /* Insert any tuples left in the buffer */ + if (slotCount > 0) + { + /* fetch index info only when we know we need it */ + if (indstate == NULL) + indstate = CatalogOpenIndexes(dependDesc); + + CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slotCount, + indstate); + } + if (indstate != NULL) CatalogCloseIndexes(indstate); table_close(dependDesc, RowExclusiveLock); + + /* Drop only the number of slots used */ + nslots = slot_init ? slotCount : max_slots; + for (i = 0; i < nslots; i++) + ExecDropSingleTupleTableSlot(slot[i]); + pfree(slot); } /* diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c index 30b234e90e..d5f2d1a2e3 100644 --- a/src/backend/catalog/pg_shdepend.c +++ b/src/backend/catalog/pg_shdepend.c @@ -786,12 +786,6 @@ checkSharedDependencies(Oid classId, Oid objectId, } -/* - * Cap the maximum amount of bytes allocated for copyTemplateDependencies() - * slots. - */ -#define MAX_PGSHDEPEND_INSERT_BYTES 65535 - /* * copyTemplateDependencies * @@ -820,7 +814,7 @@ copyTemplateDependencies(Oid templateDbId, Oid newDbId) * Allocate the slots to use, but delay initialization until we know that * they will be used. */ - max_slots = MAX_PGSHDEPEND_INSERT_BYTES / sizeof(FormData_pg_shdepend); + max_slots = MAX_CATALOG_INSERT_BYTES / sizeof(FormData_pg_shdepend); slot = palloc(sizeof(TupleTableSlot *) * max_slots); indstate = CatalogOpenIndexes(sdepRel); -- 2.28.0