Use multi-inserts for pg_ts_config_map
authorMichael Paquier <michael@paquier.xyz>
Wed, 16 Nov 2022 05:32:09 +0000 (14:32 +0900)
committerMichael Paquier <michael@paquier.xyz>
Wed, 16 Nov 2022 05:32:09 +0000 (14:32 +0900)
Two locations working on pg_ts_config_map are switched from
CatalogTupleInsert() to a multi-insert approach with tuple slots:
- ALTER TEXT SEARCH CONFIGURATION ADD/ALTER MAPPING when inserting new
entries.  The number of entries to insert is known in advance, so is the
number of slots needed.  Note that CatalogTupleInsertWithInfo() is now
used for the entry updates.
- CREATE TEXT SEARCH CONFIGURATION, where up to ~20-ish records could be
inserted at once.  The number of slots is not known in advance, hence
a slot initialization is delayed until a tuple is stored in it.

Like all the changes of this kind (1ff416163110c6 or e3931d01), an
insert batch is capped at 64kB.

Author: Michael Paquier, Ranier Vilela
Reviewed-by: Kyotaro Horiguchi
Discussion: https://postgr.es/m/Y3M5bovrkTQbAO4W@paquier.xyz

src/backend/commands/tsearchcmds.c

index 9304c53d4baedbefaa03ed859c41b9e5bb54fcee..bf0bec2f7052ea58ed0e3b6e8c322ccc2b94331d 100644 (file)
@@ -1004,8 +1004,24 @@ DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied)
        ScanKeyData skey;
        SysScanDesc scan;
        HeapTuple   maptup;
+       TupleDesc   mapDesc;
+       TupleTableSlot **slot;
+       CatalogIndexState indstate;
+       int         max_slots,
+                   slot_init_count,
+                   slot_stored_count;
 
        mapRel = table_open(TSConfigMapRelationId, RowExclusiveLock);
+       mapDesc = RelationGetDescr(mapRel);
+
+       indstate = CatalogOpenIndexes(mapRel);
+
+       /*
+        * Allocate the slots to use, but delay costly initialization until we
+        * know that they will be used.
+        */
+       max_slots = MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_ts_config_map);
+       slot = palloc(sizeof(TupleTableSlot *) * max_slots);
 
        ScanKeyInit(&skey,
                    Anum_pg_ts_config_map_mapcfg,
@@ -1015,29 +1031,54 @@ DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied)
        scan = systable_beginscan(mapRel, TSConfigMapIndexId, true,
                                  NULL, 1, &skey);
 
+       /* number of slots currently storing tuples */
+       slot_stored_count = 0;
+       /* number of slots currently initialized */
+       slot_init_count = 0;
+
        while (HeapTupleIsValid((maptup = systable_getnext(scan))))
        {
            Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
-           HeapTuple   newmaptup;
-           Datum       mapvalues[Natts_pg_ts_config_map];
-           bool        mapnulls[Natts_pg_ts_config_map];
 
-           memset(mapvalues, 0, sizeof(mapvalues));
-           memset(mapnulls, false, sizeof(mapnulls));
+           if (slot_init_count < max_slots)
+           {
+               slot[slot_stored_count] = MakeSingleTupleTableSlot(mapDesc,
+                                                                  &TTSOpsHeapTuple);
+               slot_init_count++;
+           }
+
+           ExecClearTuple(slot[slot_stored_count]);
 
-           mapvalues[Anum_pg_ts_config_map_mapcfg - 1] = cfgOid;
-           mapvalues[Anum_pg_ts_config_map_maptokentype - 1] = cfgmap->maptokentype;
-           mapvalues[Anum_pg_ts_config_map_mapseqno - 1] = cfgmap->mapseqno;
-           mapvalues[Anum_pg_ts_config_map_mapdict - 1] = cfgmap->mapdict;
+           memset(slot[slot_stored_count]->tts_isnull, false,
+                  slot[slot_stored_count]->tts_tupleDescriptor->natts * sizeof(bool));
 
-           newmaptup = heap_form_tuple(mapRel->rd_att, mapvalues, mapnulls);
+           slot[slot_stored_count]->tts_values[Anum_pg_ts_config_map_mapcfg - 1] = cfgOid;
+           slot[slot_stored_count]->tts_values[Anum_pg_ts_config_map_maptokentype - 1] = cfgmap->maptokentype;
+           slot[slot_stored_count]->tts_values[Anum_pg_ts_config_map_mapseqno - 1] = cfgmap->mapseqno;
+           slot[slot_stored_count]->tts_values[Anum_pg_ts_config_map_mapdict - 1] = cfgmap->mapdict;
 
-           CatalogTupleInsert(mapRel, newmaptup);
+           ExecStoreVirtualTuple(slot[slot_stored_count]);
+           slot_stored_count++;
 
-           heap_freetuple(newmaptup);
+           /* If slots are full, insert a batch of tuples */
+           if (slot_stored_count == max_slots)
+           {
+               CatalogTuplesMultiInsertWithInfo(mapRel, slot, slot_stored_count,
+                                                indstate);
+               slot_stored_count = 0;
+           }
        }
 
+       /* Insert any tuples left in the buffer */
+       if (slot_stored_count > 0)
+           CatalogTuplesMultiInsertWithInfo(mapRel, slot, slot_stored_count,
+                                            indstate);
+
+       for (int i = 0; i < slot_init_count; i++)
+           ExecDropSingleTupleTableSlot(slot[i]);
+
        systable_endscan(scan);
+       CatalogCloseIndexes(indstate);
    }
 
    address = makeConfigurationDependencies(tup, false, mapRel);
@@ -1225,6 +1266,7 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
    Oid        *dictIds;
    int         ndict;
    ListCell   *c;
+   CatalogIndexState indstate;
 
    tsform = (Form_pg_ts_config) GETSTRUCT(tup);
    cfgId = tsform->oid;
@@ -1275,6 +1317,8 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
        i++;
    }
 
+   indstate = CatalogOpenIndexes(relMap);
+
    if (stmt->replace)
    {
        /*
@@ -1334,7 +1378,7 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
                newtup = heap_modify_tuple(maptup,
                                           RelationGetDescr(relMap),
                                           repl_val, repl_null, repl_repl);
-               CatalogTupleUpdate(relMap, &newtup->t_self, newtup);
+               CatalogTupleUpdateWithInfo(relMap, &newtup->t_self, newtup, indstate);
            }
        }
 
@@ -1342,6 +1386,18 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
    }
    else
    {
+       TupleTableSlot **slot;
+       int         slotCount = 0;
+       int         nslots;
+
+       /* Allocate the slots to use and initialize them */
+       nslots = Min(ntoken * ndict,
+                    MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_ts_config_map));
+       slot = palloc(sizeof(TupleTableSlot *) * nslots);
+       for (i = 0; i < nslots; i++)
+           slot[i] = MakeSingleTupleTableSlot(RelationGetDescr(relMap),
+                                              &TTSOpsHeapTuple);
+
        /*
         * Insertion of new entries
         */
@@ -1349,23 +1405,41 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
        {
            for (j = 0; j < ndict; j++)
            {
-               Datum       values[Natts_pg_ts_config_map];
-               bool        nulls[Natts_pg_ts_config_map];
+               ExecClearTuple(slot[slotCount]);
+
+               memset(slot[slotCount]->tts_isnull, false,
+                      slot[slotCount]->tts_tupleDescriptor->natts * sizeof(bool));
 
-               memset(nulls, false, sizeof(nulls));
-               values[Anum_pg_ts_config_map_mapcfg - 1] = ObjectIdGetDatum(cfgId);
-               values[Anum_pg_ts_config_map_maptokentype - 1] = Int32GetDatum(tokens[i]);
-               values[Anum_pg_ts_config_map_mapseqno - 1] = Int32GetDatum(j + 1);
-               values[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictIds[j]);
+               slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapcfg - 1] = ObjectIdGetDatum(cfgId);
+               slot[slotCount]->tts_values[Anum_pg_ts_config_map_maptokentype - 1] = Int32GetDatum(tokens[i]);
+               slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapseqno - 1] = Int32GetDatum(j + 1);
+               slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictIds[j]);
 
-               tup = heap_form_tuple(relMap->rd_att, values, nulls);
-               CatalogTupleInsert(relMap, tup);
+               ExecStoreVirtualTuple(slot[slotCount]);
+               slotCount++;
 
-               heap_freetuple(tup);
+               /* If slots are full, insert a batch of tuples */
+               if (slotCount == nslots)
+               {
+                   CatalogTuplesMultiInsertWithInfo(relMap, slot, slotCount,
+                                                    indstate);
+                   slotCount = 0;
+               }
            }
        }
+
+       /* Insert any tuples left in the buffer */
+       if (slotCount > 0)
+           CatalogTuplesMultiInsertWithInfo(relMap, slot, slotCount,
+                                            indstate);
+
+       for (i = 0; i < nslots; i++)
+           ExecDropSingleTupleTableSlot(slot[i]);
    }
 
+   /* clean up */
+   CatalogCloseIndexes(indstate);
+
    EventTriggerCollectAlterTSConfig(stmt, cfgId, dictIds, ndict);
 }