From 37d925e89bbf2764eb749a11f53b082e40577203 Mon Sep 17 00:00:00 2001 From: Daniel Gustafsson Date: Wed, 22 May 2019 09:59:55 +0200 Subject: [PATCH] WIP: Use heap_multi_insert for catalog relations Introduce a new function CatalogMultiInsertWithInfo which can replace multiple calls to CatalogTupleInsertWithInfo by instead taking set of slots to perform heap_multi_insert on. InsertPgAttributeTuples is also introduced as a way to insert multiple attributes at once. For insertions performing recordDependencyOn or InsertPgAttributeTuple in a loop context, move these to collecting a set of tuples and use the new InsertPgAttributeTuples or recordMultipleDependencies instead. Also make recordMultipleDependencies use CatalogMultiInsertWithInfo to ensure insertion via heap_multi_insert. --- src/backend/catalog/dependency.c | 54 ++++++++---- src/backend/catalog/heap.c | 138 ++++++++++++++++++++++++------- src/backend/catalog/index.c | 58 ++++++++----- src/backend/catalog/indexing.c | 37 +++++++++ src/backend/catalog/pg_aggregate.c | 69 ++++------------ src/backend/catalog/pg_constraint.c | 75 +++++------------ src/backend/catalog/pg_depend.c | 84 ++++++++++++++++--- src/backend/catalog/pg_operator.c | 58 ++++--------- src/backend/catalog/pg_proc.c | 50 ++++------- src/backend/catalog/pg_shdepend.c | 26 ++++-- src/backend/catalog/pg_type.c | 51 +++++------- src/backend/replication/logical/decode.c | 3 +- src/include/catalog/dependency.h | 5 ++ src/include/catalog/heap.h | 4 + src/include/catalog/indexing.h | 3 + 15 files changed, 414 insertions(+), 301 deletions(-) diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 6315fc4b2f..7425bb76e5 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -205,8 +205,6 @@ static bool find_expr_references_walker(Node *node, find_expr_references_context *context); static void eliminate_duplicate_dependencies(ObjectAddresses *addrs); static int object_address_comparator(const void *a, const void *b); -static void add_object_address(ObjectClass oclass, Oid objectId, int32 subId, - ObjectAddresses *addrs); static void add_exact_object_address_extra(const ObjectAddress *object, const ObjectAddressExtra *extra, ObjectAddresses *addrs); @@ -1574,10 +1572,16 @@ recordDependencyOnExpr(const ObjectAddress *depender, /* Remove any duplicates */ eliminate_duplicate_dependencies(context.addrs); - /* And record 'em */ - recordMultipleDependencies(depender, - context.addrs->refs, context.addrs->numrefs, - behavior); + /* + * And record 'em. If we know we only have a single dependency then + * avoid the extra cost of setting up a multi insert. + */ + if (context.addrs->numrefs == 1) + recordDependencyOn(depender, &context.addrs->refs[0], behavior); + else + recordMultipleDependencies(depender, + context.addrs->refs, context.addrs->numrefs, + behavior); free_object_addresses(context.addrs); } @@ -1666,10 +1670,16 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender, free_object_addresses(self_addrs); } - /* Record the external dependencies */ - recordMultipleDependencies(depender, - context.addrs->refs, context.addrs->numrefs, - behavior); + /* + * Record the external dependencies. If we only have a single dependency + * then avoid the extra cost of setting up a multi insert. + */ + if (context.addrs->numrefs == 1) + recordDependencyOn(depender, &context.addrs->refs[0], behavior); + else + recordMultipleDependencies(depender, + context.addrs->refs, context.addrs->numrefs, + behavior); free_object_addresses(context.addrs); } @@ -2362,7 +2372,7 @@ new_object_addresses(void) * It is convenient to specify the class by ObjectClass rather than directly * by catalog OID. */ -static void +void add_object_address(ObjectClass oclass, Oid objectId, int32 subId, ObjectAddresses *addrs) { @@ -2610,9 +2620,13 @@ record_object_address_dependencies(const ObjectAddress *depender, DependencyType behavior) { eliminate_duplicate_dependencies(referenced); - recordMultipleDependencies(depender, - referenced->refs, referenced->numrefs, - behavior); + + if (referenced->numrefs == 1) + recordDependencyOn(depender, &referenced->refs[0], behavior); + else + recordMultipleDependencies(depender, + referenced->refs, referenced->numrefs, + behavior); } /* @@ -2632,6 +2646,18 @@ sort_object_addresses(ObjectAddresses *addrs) object_address_comparator); } +void +reset_object_addresses(ObjectAddresses *addrs) +{ + if (addrs->numrefs == 0) + return; + + memset(addrs->refs, 0, addrs->maxrefs * sizeof(ObjectAddress)); + if (addrs->extras) + memset(addrs->extras, 0, addrs->maxrefs * sizeof(ObjectAddressExtra)); + addrs->numrefs = 0; +} + /* * Clean up when done with an ObjectAddresses array. */ diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 3b8c8b193a..5f443b798f 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -678,6 +678,79 @@ CheckAttributeType(const char *attname, errhint("Use the COLLATE clause to set the collation explicitly."))); } +/* + * InsertPgAttributeTuples + * Construct and insert multiple tuples in pg_attribute. + * + * This is a variant of InsertPgAttributeTuple() which dynamically allocates + * space for multiple tuples. Having two so similar functions is a kludge, but + * for now it's a TODO to make it less terrible. + */ +void +InsertPgAttributeTuples(Relation pg_attribute_rel, + FormData_pg_attribute *new_attributes, + int natts, + CatalogIndexState indstate) +{ + Datum values[Natts_pg_attribute]; + bool nulls[Natts_pg_attribute]; + HeapTuple tup; + int i; + TupleTableSlot **slot; + + /* + * The slots are dropped in CatalogMultiInsertWithInfo(). TODO: natts + * number of slots is not a reasonable assumption as a wide relation + * would be detrimental, figuring a good number is left as a TODO. + */ + slot = palloc(sizeof(TupleTableSlot *) * natts); + + /* This is a tad tedious, but way cleaner than what we used to do... */ + memset(values, 0, sizeof(values)); + memset(nulls, false, sizeof(nulls)); + + /* start out with empty permissions and empty options */ + nulls[Anum_pg_attribute_attacl - 1] = true; + nulls[Anum_pg_attribute_attoptions - 1] = true; + nulls[Anum_pg_attribute_attfdwoptions - 1] = true; + nulls[Anum_pg_attribute_attmissingval - 1] = true; + + /* attcacheoff is always -1 in storage */ + values[Anum_pg_attribute_attcacheoff - 1] = Int32GetDatum(-1); + + for (i = 0; i < natts; i++) + { + values[Anum_pg_attribute_attrelid - 1] = ObjectIdGetDatum(new_attributes[i].attrelid); + values[Anum_pg_attribute_attname - 1] = NameGetDatum(&new_attributes[i].attname); + values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(new_attributes[i].atttypid); + values[Anum_pg_attribute_attstattarget - 1] = Int32GetDatum(new_attributes[i].attstattarget); + values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(new_attributes[i].attlen); + values[Anum_pg_attribute_attnum - 1] = Int16GetDatum(new_attributes[i].attnum); + values[Anum_pg_attribute_attndims - 1] = Int32GetDatum(new_attributes[i].attndims); + values[Anum_pg_attribute_atttypmod - 1] = Int32GetDatum(new_attributes[i].atttypmod); + values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(new_attributes[i].attbyval); + values[Anum_pg_attribute_attstorage - 1] = CharGetDatum(new_attributes[i].attstorage); + values[Anum_pg_attribute_attalign - 1] = CharGetDatum(new_attributes[i].attalign); + values[Anum_pg_attribute_attnotnull - 1] = BoolGetDatum(new_attributes[i].attnotnull); + values[Anum_pg_attribute_atthasdef - 1] = BoolGetDatum(new_attributes[i].atthasdef); + values[Anum_pg_attribute_atthasmissing - 1] = BoolGetDatum(new_attributes[i].atthasmissing); + values[Anum_pg_attribute_attidentity - 1] = CharGetDatum(new_attributes[i].attidentity); + values[Anum_pg_attribute_attgenerated - 1] = CharGetDatum(new_attributes[i].attgenerated); + values[Anum_pg_attribute_attisdropped - 1] = BoolGetDatum(new_attributes[i].attisdropped); + values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attributes[i].attislocal); + values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attributes[i].attinhcount); + values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attributes[i].attcollation); + + slot[i] = MakeSingleTupleTableSlot(RelationGetDescr(pg_attribute_rel), + &TTSOpsHeapTuple); + tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls); + ExecStoreHeapTuple(heap_copytuple(tup), slot[i], false); + } + + /* finally insert the new tuples, update the indexes, and clean up */ + CatalogMultiInsertWithInfo(pg_attribute_rel, slot, natts, indstate); +} + /* * InsertPgAttributeTuple * Construct and insert a new tuple in pg_attribute. @@ -755,7 +828,7 @@ AddNewAttributeTuples(Oid new_rel_oid, TupleDesc tupdesc, char relkind) { - Form_pg_attribute attr; + Form_pg_attribute *attrs; int i; Relation rel; CatalogIndexState indstate; @@ -770,35 +843,42 @@ AddNewAttributeTuples(Oid new_rel_oid, indstate = CatalogOpenIndexes(rel); + attrs = palloc(sizeof(Form_pg_attribute) * natts); + /* - * First we add the user attributes. This is also a convenient place to - * add dependencies on their datatypes and collations. + * First we add the user attributes. */ for (i = 0; i < natts; i++) { - attr = TupleDescAttr(tupdesc, i); + attrs[i] = TupleDescAttr(tupdesc, i); /* Fill in the correct relation OID */ - attr->attrelid = new_rel_oid; + attrs[i]->attrelid = new_rel_oid; /* Make sure this is OK, too */ - attr->attstattarget = -1; + attrs[i]->attstattarget = -1; + } - InsertPgAttributeTuple(rel, attr, indstate); + InsertPgAttributeTuples(rel, *attrs, natts, indstate); + /* + * Now add dependencies on their datatypes and collations. + */ + for (i = 0; i < natts; i++) + { /* Add dependency info */ myself.classId = RelationRelationId; myself.objectId = new_rel_oid; myself.objectSubId = i + 1; referenced.classId = TypeRelationId; - referenced.objectId = attr->atttypid; + referenced.objectId = attrs[i]->atttypid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* The default collation is pinned, so don't bother recording it */ - if (OidIsValid(attr->attcollation) && - attr->attcollation != DEFAULT_COLLATION_OID) + if (OidIsValid(attrs[i]->attcollation) && + attrs[i]->attcollation != DEFAULT_COLLATION_OID) { referenced.classId = CollationRelationId; - referenced.objectId = attr->attcollation; + referenced.objectId = attrs[i]->attcollation; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } @@ -811,22 +891,22 @@ AddNewAttributeTuples(Oid new_rel_oid, */ if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE) { + FormData_pg_attribute attStructs[lengthof(SysAtt)]; + + /* Fill in the correct relation OID in the copied tuple */ for (i = 0; i < (int) lengthof(SysAtt); i++) { - FormData_pg_attribute attStruct; - - memcpy(&attStruct, SysAtt[i], sizeof(FormData_pg_attribute)); - - /* Fill in the correct relation OID in the copied tuple */ - attStruct.attrelid = new_rel_oid; - - InsertPgAttributeTuple(rel, &attStruct, indstate); + memcpy(&attStructs[i], SysAtt[i], sizeof(FormData_pg_attribute)); + attStructs[i].attrelid = new_rel_oid; } + + InsertPgAttributeTuples(rel, attStructs, lengthof(SysAtt), indstate); } /* * clean up */ + pfree(attrs); CatalogCloseIndexes(indstate); table_close(rel, RowExclusiveLock); @@ -3458,7 +3538,7 @@ StorePartitionKey(Relation rel, Datum values[Natts_pg_partitioned_table]; bool nulls[Natts_pg_partitioned_table]; ObjectAddress myself; - ObjectAddress referenced; + ObjectAddresses *refobjs; Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE); @@ -3506,27 +3586,25 @@ StorePartitionKey(Relation rel, myself.objectId = RelationGetRelid(rel); myself.objectSubId = 0; + refobjs = new_object_addresses(); + /* Operator class and collation per key column */ for (i = 0; i < partnatts; i++) { - referenced.classId = OperatorClassRelationId; - referenced.objectId = partopclass[i]; - referenced.objectSubId = 0; - - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + add_object_address(OCLASS_OPCLASS, partopclass[i], 0, refobjs); /* The default collation is pinned, so don't bother recording it */ if (OidIsValid(partcollation[i]) && partcollation[i] != DEFAULT_COLLATION_OID) { - referenced.classId = CollationRelationId; - referenced.objectId = partcollation[i]; - referenced.objectSubId = 0; - - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + add_object_address(OCLASS_COLLATION, partcollation[i], 0, refobjs); } } + /* Store the dependencies in the catalog */ + record_object_address_dependencies(&myself, refobjs, DEPENDENCY_NORMAL); + free_object_addresses(refobjs); + /* * Anything mentioned in the expressions. We must ignore the column * references, which will depend on the table itself; there is no separate diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index bb60b23093..52d132113b 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -505,14 +505,30 @@ AppendAttributeTuples(Relation indexRelation, int numatts) */ indexTupDesc = RelationGetDescr(indexRelation); - for (i = 0; i < numatts; i++) + /* + * If we only have a single attribute we can use the single tuple insert + * function which avoids multi_insert scaffolding costs. + */ + if (numatts == 1) { - Form_pg_attribute attr = TupleDescAttr(indexTupDesc, i); - - Assert(attr->attnum == i + 1); + Form_pg_attribute attr = TupleDescAttr(indexTupDesc, 0); InsertPgAttributeTuple(pg_attribute, attr, indstate); } + else + { + Form_pg_attribute *attrs = palloc(sizeof(Form_pg_attribute) * numatts); + + for (i = 0; i < numatts; i++) + { + attrs[i] = TupleDescAttr(indexTupDesc, i); + + Assert(attrs[i]->attnum == i + 1); + } + + InsertPgAttributeTuples(pg_attribute, *attrs, numatts, indstate); + pfree(attrs); + } CatalogCloseIndexes(indstate); @@ -982,11 +998,14 @@ index_create(Relation heapRelation, { ObjectAddress myself, referenced; + ObjectAddresses *refobjs; myself.classId = RelationRelationId; myself.objectId = indexRelationId; myself.objectSubId = 0; + refobjs = new_object_addresses(); + if ((flags & INDEX_CREATE_ADD_CONSTRAINT) != 0) { char constraintType; @@ -1025,12 +1044,9 @@ index_create(Relation heapRelation, { if (indexInfo->ii_IndexAttrNumbers[i] != 0) { - referenced.classId = RelationRelationId; - referenced.objectId = heapRelationId; - referenced.objectSubId = indexInfo->ii_IndexAttrNumbers[i]; - - recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); - + add_object_address(OCLASS_CLASS, heapRelationId, + indexInfo->ii_IndexAttrNumbers[i], + refobjs); have_simple_col = true; } } @@ -1049,6 +1065,11 @@ index_create(Relation heapRelation, recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); } + else + { + record_object_address_dependencies(&myself, refobjs, DEPENDENCY_AUTO); + reset_object_addresses(refobjs); + } } /* @@ -1079,23 +1100,16 @@ index_create(Relation heapRelation, if (OidIsValid(collationObjectId[i]) && collationObjectId[i] != DEFAULT_COLLATION_OID) { - referenced.classId = CollationRelationId; - referenced.objectId = collationObjectId[i]; - referenced.objectSubId = 0; - - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + add_object_address(OCLASS_COLLATION, collationObjectId[i], 0, + refobjs); } } /* Store dependency on operator classes */ for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++) - { - referenced.classId = OperatorClassRelationId; - referenced.objectId = classObjectId[i]; - referenced.objectSubId = 0; + add_object_address(OCLASS_OPCLASS, classObjectId[i], 0, refobjs); - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + record_object_address_dependencies(&myself, refobjs, DEPENDENCY_NORMAL); /* Store dependencies on anything mentioned in index expressions */ if (indexInfo->ii_Expressions) @@ -1116,6 +1130,8 @@ index_create(Relation heapRelation, DEPENDENCY_NORMAL, DEPENDENCY_AUTO, false); } + + free_object_addresses(refobjs); } else { diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c index f237e62bc9..0819a6449c 100644 --- a/src/backend/catalog/indexing.c +++ b/src/backend/catalog/indexing.c @@ -18,6 +18,7 @@ #include "access/genam.h" #include "access/heapam.h" #include "access/htup_details.h" +#include "access/xact.h" #include "catalog/index.h" #include "catalog/indexing.h" #include "executor/executor.h" @@ -209,6 +210,42 @@ CatalogTupleInsertWithInfo(Relation heapRel, HeapTuple tup, CatalogIndexInsert(indstate, tup); } +/* + * CatalogMultiInsertWithInfo + * + * Insert multiple tuples into the catalog relation at once, with an amortized + * cost of CatalogOpenIndexes. + */ +void +CatalogMultiInsertWithInfo(Relation heapRel, TupleTableSlot **slot, int ntuples, + CatalogIndexState indstate) +{ + /* Nothing to do */ + if (ntuples <= 0) + return; + + heap_multi_insert(heapRel, slot, ntuples, + GetCurrentCommandId(true), 0, NULL); + + /* + * There is no equivalent to heap_multi_insert for the catalog indexes, so + * we must loop over and insert individually. + */ + for (int i = 0; i < ntuples; i++) + { + bool should_free; + HeapTuple tuple; + + tuple = ExecFetchSlotHeapTuple(slot[i], true, &should_free); + tuple->t_tableOid = slot[i]->tts_tableOid; + CatalogIndexInsert(indstate, tuple); + ExecDropSingleTupleTableSlot(slot[i]); + + if (should_free) + heap_freetuple(tuple); + } +} + /* * CatalogTupleUpdate - do heap and indexing work for updating a catalog tuple * diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 7cab039ded..5d3d3e9069 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -104,8 +104,8 @@ AggregateCreate(const char *aggName, Oid procOid; TupleDesc tupDesc; int i; - ObjectAddress myself, - referenced; + ObjectAddress myself; + ObjectAddresses *refobjs; AclResult aclresult; /* sanity checks (caller should have caught these) */ @@ -740,83 +740,44 @@ AggregateCreate(const char *aggName, * way. */ + refobjs = new_object_addresses(); + /* Depends on transition function */ - referenced.classId = ProcedureRelationId; - referenced.objectId = transfn; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + add_object_address(OCLASS_PROC, transfn, 0, refobjs); /* Depends on final function, if any */ if (OidIsValid(finalfn)) - { - referenced.classId = ProcedureRelationId; - referenced.objectId = finalfn; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_PROC, finalfn, 0, refobjs); /* Depends on combine function, if any */ if (OidIsValid(combinefn)) - { - referenced.classId = ProcedureRelationId; - referenced.objectId = combinefn; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_PROC, combinefn, 0, refobjs); /* Depends on serialization function, if any */ if (OidIsValid(serialfn)) - { - referenced.classId = ProcedureRelationId; - referenced.objectId = serialfn; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_PROC, serialfn, 0, refobjs); /* Depends on deserialization function, if any */ if (OidIsValid(deserialfn)) - { - referenced.classId = ProcedureRelationId; - referenced.objectId = deserialfn; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_PROC, deserialfn, 0, refobjs); /* Depends on forward transition function, if any */ if (OidIsValid(mtransfn)) - { - referenced.classId = ProcedureRelationId; - referenced.objectId = mtransfn; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_PROC, mtransfn, 0, refobjs); /* Depends on inverse transition function, if any */ if (OidIsValid(minvtransfn)) - { - referenced.classId = ProcedureRelationId; - referenced.objectId = minvtransfn; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_PROC, minvtransfn, 0, refobjs); /* Depends on final function, if any */ if (OidIsValid(mfinalfn)) - { - referenced.classId = ProcedureRelationId; - referenced.objectId = mfinalfn; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_PROC, mfinalfn, 0, refobjs); /* Depends on sort operator, if any */ if (OidIsValid(sortop)) - { - referenced.classId = OperatorRelationId; - referenced.objectId = sortop; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_OPERATOR, sortop, 0, refobjs); + + record_object_address_dependencies(&myself, refobjs, DEPENDENCY_NORMAL); return myself; } diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index b6145593a3..be7c6b8c12 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -92,6 +92,7 @@ CreateConstraintEntry(const char *constraintName, NameData cname; int i; ObjectAddress conobject; + ObjectAddresses *refobjs; conDesc = table_open(ConstraintRelationId, RowExclusiveLock); @@ -230,30 +231,22 @@ CreateConstraintEntry(const char *constraintName, table_close(conDesc, RowExclusiveLock); + refobjs = new_object_addresses(); + if (OidIsValid(relId)) { /* * Register auto dependency from constraint to owning relation, or to * specific column(s) if any are mentioned. */ - ObjectAddress relobject; - - relobject.classId = RelationRelationId; - relobject.objectId = relId; if (constraintNTotalKeys > 0) { for (i = 0; i < constraintNTotalKeys; i++) - { - relobject.objectSubId = constraintKey[i]; - - recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO); - } + add_object_address(OCLASS_CLASS, relId, constraintKey[i], refobjs); } else { - relobject.objectSubId = 0; - - recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO); + add_object_address(OCLASS_CLASS, relId, 0, refobjs); } } @@ -262,39 +255,27 @@ CreateConstraintEntry(const char *constraintName, /* * Register auto dependency from constraint to owning domain */ - ObjectAddress domobject; - - domobject.classId = TypeRelationId; - domobject.objectId = domainId; - domobject.objectSubId = 0; - - recordDependencyOn(&conobject, &domobject, DEPENDENCY_AUTO); + add_object_address(OCLASS_TYPE, domainId, 0, refobjs); } + /* record the AUTO dependencies we have so far */ + record_object_address_dependencies(&conobject, refobjs, DEPENDENCY_AUTO); + reset_object_addresses(refobjs); + if (OidIsValid(foreignRelId)) { /* * Register normal dependency from constraint to foreign relation, or * to specific column(s) if any are mentioned. */ - ObjectAddress relobject; - - relobject.classId = RelationRelationId; - relobject.objectId = foreignRelId; if (foreignNKeys > 0) { for (i = 0; i < foreignNKeys; i++) - { - relobject.objectSubId = foreignKey[i]; - - recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_CLASS, foreignRelId, foreignKey[i], refobjs); } else { - relobject.objectSubId = 0; - - recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL); + add_object_address(OCLASS_CLASS, foreignRelId, 0, refobjs); } } @@ -306,13 +287,7 @@ CreateConstraintEntry(const char *constraintName, * or primary-key constraints, the dependency runs the other way, and * is not made here.) */ - ObjectAddress relobject; - - relobject.classId = RelationRelationId; - relobject.objectId = indexRelId; - relobject.objectSubId = 0; - - recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL); + add_object_address(OCLASS_CLASS, indexRelId, 0, refobjs); } if (foreignNKeys > 0) @@ -323,28 +298,22 @@ CreateConstraintEntry(const char *constraintName, * all three operators for a column are the same; otherwise they are * different. */ - ObjectAddress oprobject; - - oprobject.classId = OperatorRelationId; - oprobject.objectSubId = 0; - for (i = 0; i < foreignNKeys; i++) { - oprobject.objectId = pfEqOp[i]; - recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL); + add_object_address(OCLASS_OPERATOR, pfEqOp[i], 0, refobjs); + if (ppEqOp[i] != pfEqOp[i]) - { - oprobject.objectId = ppEqOp[i]; - recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_OPERATOR, ppEqOp[i], 0, refobjs); + if (ffEqOp[i] != pfEqOp[i]) - { - oprobject.objectId = ffEqOp[i]; - recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_OPERATOR, ffEqOp[i], 0, refobjs); } } + record_object_address_dependencies(&conobject, refobjs, DEPENDENCY_NORMAL); + + free_object_addresses(refobjs); + /* * We don't bother to register dependencies on the exclusion operators of * an exclusion constraint. We assume they are members of the opclass diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index 4116e93b64..8977169380 100644 --- a/src/backend/catalog/pg_depend.c +++ b/src/backend/catalog/pg_depend.c @@ -35,7 +35,8 @@ static bool isObjectPinned(const ObjectAddress *object, Relation rel); /* * Record a dependency between 2 objects via their respective objectAddress. * The first argument is the dependent object, the second the one it - * references. + * references. This is a simplified version of recordMultipleDependencies() + * aiming to avoid some overhead when we know there is only a single tuple. * * This simply creates an entry in pg_depend, without any other processing. */ @@ -44,7 +45,51 @@ recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior) { - recordMultipleDependencies(depender, referenced, 1, behavior); + Relation dependDesc; + HeapTuple tuple; + bool nulls[Natts_pg_depend]; + Datum values[Natts_pg_depend]; + + /* + * During bootstrap, do nothing since pg_depend may not exist yet. initdb + * will fill in appropriate pg_depend entries after bootstrap. + */ + if (IsBootstrapProcessingMode()) + return; + + dependDesc = table_open(DependRelationId, RowExclusiveLock); + + memset(nulls, false, sizeof(nulls)); + + /* + * If the referenced object is pinned by the system, there's no real + * 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)) + { + table_close(dependDesc, RowExclusiveLock); + return; + } + + /* + * 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); + + tuple = heap_form_tuple(dependDesc->rd_att, values, nulls); + CatalogTupleInsert(dependDesc, tuple); + + table_close(dependDesc, RowExclusiveLock); } /* @@ -59,7 +104,9 @@ recordMultipleDependencies(const ObjectAddress *depender, { Relation dependDesc; CatalogIndexState indstate; - HeapTuple tup; + HeapTuple tuple; + TupleTableSlot **slot; + int ntuples; int i; bool nulls[Natts_pg_depend]; Datum values[Natts_pg_depend]; @@ -81,7 +128,14 @@ recordMultipleDependencies(const ObjectAddress *depender, memset(nulls, false, sizeof(nulls)); - for (i = 0; i < nreferenced; i++, referenced++) + 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); + + /* TODO is nreferenced a reasonable allocation of slots? */ + slot = palloc(sizeof(TupleTableSlot *) * nreferenced); + + for (i = 0, ntuples = 0; i < nreferenced; i++, referenced++) { /* * If the referenced object is pinned by the system, there's no real @@ -94,30 +148,34 @@ recordMultipleDependencies(const ObjectAddress *depender, * 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); + slot[ntuples] = MakeSingleTupleTableSlot(RelationGetDescr(dependDesc), + &TTSOpsHeapTuple); + + tuple = heap_form_tuple(dependDesc->rd_att, values, nulls); + ExecStoreHeapTuple(heap_copytuple(tuple), slot[ntuples], false); + ntuples++; /* fetch index info only when we know we need it */ if (indstate == NULL) indstate = CatalogOpenIndexes(dependDesc); - - CatalogTupleInsertWithInfo(dependDesc, tup, indstate); - - heap_freetuple(tup); } } + /* + * We will have an indstate in case we found any tuples to insert in the + * catalog, so perform a multi insert and close the index again when done. + */ if (indstate != NULL) + { + CatalogMultiInsertWithInfo(dependDesc, slot, ntuples, indstate); CatalogCloseIndexes(indstate); + } table_close(dependDesc, RowExclusiveLock); } diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index bcaa26c997..b37f01df09 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -773,13 +773,15 @@ ObjectAddress makeOperatorDependencies(HeapTuple tuple, bool isUpdate) { Form_pg_operator oper = (Form_pg_operator) GETSTRUCT(tuple); - ObjectAddress myself, - referenced; + ObjectAddress myself; + ObjectAddresses *refobjs; myself.classId = OperatorRelationId; myself.objectId = oper->oid; myself.objectSubId = 0; + refobjs = new_object_addresses(); + /* * If we are updating the operator, delete any existing entries, except * for extension membership which should remain the same. @@ -792,39 +794,19 @@ makeOperatorDependencies(HeapTuple tuple, bool isUpdate) /* Dependency on namespace */ if (OidIsValid(oper->oprnamespace)) - { - referenced.classId = NamespaceRelationId; - referenced.objectId = oper->oprnamespace; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_SCHEMA, oper->oprnamespace, 0, refobjs); /* Dependency on left type */ if (OidIsValid(oper->oprleft)) - { - referenced.classId = TypeRelationId; - referenced.objectId = oper->oprleft; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_TYPE, oper->oprleft, 0, refobjs); /* Dependency on right type */ if (OidIsValid(oper->oprright)) - { - referenced.classId = TypeRelationId; - referenced.objectId = oper->oprright; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_TYPE, oper->oprright, 0, refobjs); /* Dependency on result type */ if (OidIsValid(oper->oprresult)) - { - referenced.classId = TypeRelationId; - referenced.objectId = oper->oprresult; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_TYPE, oper->oprresult, 0, refobjs); /* * NOTE: we do not consider the operator to depend on the associated @@ -837,30 +819,18 @@ makeOperatorDependencies(HeapTuple tuple, bool isUpdate) /* Dependency on implementation function */ if (OidIsValid(oper->oprcode)) - { - referenced.classId = ProcedureRelationId; - referenced.objectId = oper->oprcode; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_PROC, oper->oprcode, 0, refobjs); /* Dependency on restriction selectivity function */ if (OidIsValid(oper->oprrest)) - { - referenced.classId = ProcedureRelationId; - referenced.objectId = oper->oprrest; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_PROC, oper->oprrest, 0, refobjs); /* Dependency on join selectivity function */ if (OidIsValid(oper->oprjoin)) - { - referenced.classId = ProcedureRelationId; - referenced.objectId = oper->oprjoin; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_PROC, oper->oprjoin, 0, refobjs); + + record_object_address_dependencies(&myself, refobjs, DEPENDENCY_NORMAL); + free_object_addresses(refobjs); /* Dependency on owner */ recordDependencyOnOwner(OperatorRelationId, oper->oid, diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 02b7fdd494..a8195b3e21 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -114,8 +114,8 @@ ProcedureCreate(const char *procedureName, NameData procname; TupleDesc tupDesc; bool is_update; - ObjectAddress myself, - referenced; + ObjectAddress myself; + ObjectAddresses *refobjs; int i; Oid trfid; @@ -613,49 +613,29 @@ ProcedureCreate(const char *procedureName, myself.objectId = retval; myself.objectSubId = 0; + refobjs = new_object_addresses(); + /* dependency on namespace */ - referenced.classId = NamespaceRelationId; - referenced.objectId = procNamespace; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + add_object_address(OCLASS_SCHEMA, procNamespace, 0, refobjs); /* dependency on implementation language */ - referenced.classId = LanguageRelationId; - referenced.objectId = languageObjectId; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + add_object_address(OCLASS_LANGUAGE, languageObjectId, 0, refobjs); /* dependency on return type */ - referenced.classId = TypeRelationId; - referenced.objectId = returnType; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + add_object_address(OCLASS_TYPE, returnType, 0, refobjs); /* dependency on transform used by return type, if any */ if ((trfid = get_transform_oid(returnType, languageObjectId, true))) - { - referenced.classId = TransformRelationId; - referenced.objectId = trfid; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_TRANSFORM, trfid, 0, refobjs); /* dependency on parameter types */ for (i = 0; i < allParamCount; i++) { - referenced.classId = TypeRelationId; - referenced.objectId = allParams[i]; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + add_object_address(OCLASS_TYPE, allParams[i], 0, refobjs); /* dependency on transform used by parameter type, if any */ if ((trfid = get_transform_oid(allParams[i], languageObjectId, true))) - { - referenced.classId = TransformRelationId; - referenced.objectId = trfid; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_TRANSFORM, trfid, 0, refobjs); } /* dependency on parameter default expressions */ @@ -665,12 +645,10 @@ ProcedureCreate(const char *procedureName, /* dependency on support function, if any */ if (OidIsValid(prosupport)) - { - referenced.classId = ProcedureRelationId; - referenced.objectId = prosupport; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - } + add_object_address(OCLASS_PROC, prosupport, 0, refobjs); + + record_object_address_dependencies(&myself, refobjs, DEPENDENCY_NORMAL); + free_object_addresses(refobjs); /* dependency on owner */ if (!is_update) diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c index fb7f8ddefc..5a24890d8a 100644 --- a/src/backend/catalog/pg_shdepend.c +++ b/src/backend/catalog/pg_shdepend.c @@ -66,7 +66,6 @@ #include "utils/fmgroids.h" #include "utils/syscache.h" - typedef enum { LOCAL_OBJECT, @@ -800,10 +799,15 @@ copyTemplateDependencies(Oid templateDbId, Oid newDbId) ScanKeyData key[1]; SysScanDesc scan; HeapTuple tup; + HeapTuple newtuple; + int ntuples; CatalogIndexState indstate; Datum values[Natts_pg_shdepend]; bool nulls[Natts_pg_shdepend]; bool replace[Natts_pg_shdepend]; + /* TODO figure out a sensible value for the amount of slots */ +#define DEPEND_TUPLE_BUF 32 + TupleTableSlot *slot[DEPEND_TUPLE_BUF]; sdepRel = table_open(SharedDependRelationId, RowExclusiveLock); sdepDesc = RelationGetDescr(sdepRel); @@ -834,16 +838,26 @@ copyTemplateDependencies(Oid templateDbId, Oid newDbId) * copy the ownership dependency of the template database itself; this is * what we want. */ + ntuples = 0; while (HeapTupleIsValid(tup = systable_getnext(scan))) { - HeapTuple newtup; - - newtup = heap_modify_tuple(tup, sdepDesc, values, nulls, replace); - CatalogTupleInsertWithInfo(sdepRel, newtup, indstate); + slot[ntuples] = MakeSingleTupleTableSlot(RelationGetDescr(sdepRel), + &TTSOpsHeapTuple); + newtuple = heap_modify_tuple(tup, sdepDesc, values, nulls, replace); + ExecStoreHeapTuple(heap_copytuple(newtuple), slot[ntuples], false); + ntuples++; - heap_freetuple(newtup); + if (ntuples == DEPEND_TUPLE_BUF) + { + CatalogMultiInsertWithInfo(sdepRel, slot, ntuples, indstate); + ntuples = 0; + } } + /* Insert any tuples left in the buffer */ + if (ntuples) + CatalogMultiInsertWithInfo(sdepRel, slot, ntuples, indstate); + systable_endscan(scan); CatalogCloseIndexes(indstate); diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index 2a51501d8d..b3707b0f7e 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -544,7 +544,9 @@ GenerateTypeDependencies(Oid typeObjectId, bool rebuild) { ObjectAddress myself, - referenced; + referenced, + refobjs[8]; + int nref; /* If rebuild, first flush old dependencies, except extension deps */ if (rebuild) @@ -579,63 +581,54 @@ GenerateTypeDependencies(Oid typeObjectId, recordDependencyOnCurrentExtension(&myself, rebuild); } + nref = 0; + /* Normal dependencies on the I/O functions */ if (OidIsValid(typeForm->typinput)) { - referenced.classId = ProcedureRelationId; - referenced.objectId = typeForm->typinput; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + ObjectAddressSet(refobjs[nref], ProcedureRelationId, typeForm->typinput); + nref++; } if (OidIsValid(typeForm->typoutput)) { - referenced.classId = ProcedureRelationId; - referenced.objectId = typeForm->typoutput; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + ObjectAddressSet(refobjs[nref], ProcedureRelationId, typeForm->typoutput); + nref++; } if (OidIsValid(typeForm->typreceive)) { - referenced.classId = ProcedureRelationId; - referenced.objectId = typeForm->typreceive; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + ObjectAddressSet(refobjs[nref], ProcedureRelationId, typeForm->typreceive); + nref++; } if (OidIsValid(typeForm->typsend)) { - referenced.classId = ProcedureRelationId; - referenced.objectId = typeForm->typsend; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + ObjectAddressSet(refobjs[nref], ProcedureRelationId, typeForm->typsend); + nref++; } if (OidIsValid(typeForm->typmodin)) { - referenced.classId = ProcedureRelationId; - referenced.objectId = typeForm->typmodin; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + ObjectAddressSet(refobjs[nref], ProcedureRelationId, typeForm->typmodin); + nref++; } if (OidIsValid(typeForm->typmodout)) { - referenced.classId = ProcedureRelationId; - referenced.objectId = typeForm->typmodout; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + ObjectAddressSet(refobjs[nref], ProcedureRelationId, typeForm->typmodout); + nref++; } if (OidIsValid(typeForm->typanalyze)) { - referenced.classId = ProcedureRelationId; - referenced.objectId = typeForm->typanalyze; - referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + ObjectAddressSet(refobjs[nref], ProcedureRelationId, typeForm->typanalyze); + nref++; } + if (nref) + recordMultipleDependencies(&myself, refobjs, nref, DEPENDENCY_NORMAL); + /* * If the type is a rowtype for a relation, mark it as internally * dependent on the relation, *unless* it is a stand-alone composite type diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c index 151c3ef882..5468a6ecb8 100644 --- a/src/backend/replication/logical/decode.c +++ b/src/backend/replication/logical/decode.c @@ -974,7 +974,8 @@ DecodeMultiInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf) ReorderBufferQueueChange(ctx->reorder, XLogRecGetXid(r), buf->origptr, change); } - Assert(data == tupledata + tuplelen); + Assert(xlrec->flags & XLH_INSERT_CONTAINS_NEW_TUPLE && + data == tupledata + tuplelen); } /* diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index ef9c86864c..0a8d3ff613 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -162,6 +162,9 @@ extern ObjectClass getObjectClass(const ObjectAddress *object); extern ObjectAddresses *new_object_addresses(void); +extern void add_object_address(ObjectClass oclass, Oid objectId, int32 subId, + ObjectAddresses *addrs); + extern void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs); @@ -174,6 +177,8 @@ extern void record_object_address_dependencies(const ObjectAddress *depender, extern void sort_object_addresses(ObjectAddresses *addrs); +extern void reset_object_addresses(ObjectAddresses *addrs); + extern void free_object_addresses(ObjectAddresses *addrs); /* in pg_depend.c */ diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index eec71c29d5..24405977ff 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -95,6 +95,10 @@ extern List *heap_truncate_find_FKs(List *relationIds); extern void InsertPgAttributeTuple(Relation pg_attribute_rel, Form_pg_attribute new_attribute, CatalogIndexState indstate); +extern void InsertPgAttributeTuples(Relation pg_attribute_rel, + FormData_pg_attribute *new_attributes, + int natts, + CatalogIndexState indstate); extern void InsertPgClassTuple(Relation pg_class_desc, Relation new_rel_desc, diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index ef4445b017..4ea212ee59 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -19,6 +19,7 @@ #define INDEXING_H #include "access/htup.h" +#include "nodes/execnodes.h" #include "utils/relcache.h" /* @@ -36,6 +37,8 @@ extern void CatalogCloseIndexes(CatalogIndexState indstate); extern void CatalogTupleInsert(Relation heapRel, HeapTuple tup); extern void CatalogTupleInsertWithInfo(Relation heapRel, HeapTuple tup, CatalogIndexState indstate); +extern void CatalogMultiInsertWithInfo(Relation heapRel, TupleTableSlot **slot, + int ntuples, CatalogIndexState indstate); extern void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup); extern void CatalogTupleUpdateWithInfo(Relation heapRel, -- 2.14.1.145.gb3622a4ee