From 1605d4b12cc638ebcb80c044f0bc7ff55e119fa4 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Mon, 23 Nov 2020 17:57:24 -0600
Subject: [PATCH v6 3/3] Dynamically switch to multi-insert mode..

by popular request
---
 src/backend/executor/nodeModifyTable.c | 47 +++++++++++++++-----------
 src/backend/tcop/utility.c             |  4 ---
 src/backend/utils/misc/guc.c           | 19 +++++------
 src/include/executor/nodeModifyTable.h |  3 +-
 src/include/nodes/execnodes.h          |  1 +
 src/test/regress/expected/insert.out   |  4 +++
 src/test/regress/sql/insert.sql        |  2 ++
 7 files changed, 46 insertions(+), 34 deletions(-)

diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index f8cd537b24..96d5a5d164 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -74,7 +74,7 @@ static TupleTableSlot *ExecPrepareTupleRouting(ModifyTableState *mtstate,
 											   TupleTableSlot *slot,
 											   ResultRelInfo **partRelInfo);
 /* guc */
-bool insert_in_bulk = false;
+int bulk_insert_ntuples = 1000;
 
 /*
  * Verify that the tuples to be produced by INSERT or UPDATE match the
@@ -413,6 +413,33 @@ ExecInsert(ModifyTableState *mtstate,
 
 	resultRelationDesc = resultRelInfo->ri_RelationDesc;
 
+	/* Use bulk insert after a threshold number of tuples */
+	mtstate->ntuples++;
+	if (mtstate->bistate == NULL &&
+			mtstate->operation == CMD_INSERT &&
+			onconflict == ONCONFLICT_NONE &&
+			mtstate->ntuples > bulk_insert_ntuples &&
+			bulk_insert_ntuples >= 0)
+	{
+		elog(DEBUG1, "enabling bulk insert");
+		mtstate->bistate = GetBulkInsertState();
+
+		/*
+		 * For partitioned tables we don't support multi-inserts when there
+		 * are any statement level insert triggers.
+		 */
+		if (proute == 0 ||
+			 !mtstate->rootResultRelInfo->ri_TrigDesc ||
+			 !mtstate->rootResultRelInfo->ri_TrigDesc->trig_insert_new_table)
+		{
+			elog(DEBUG1, "enabling multi insert");
+			mtstate->miinfo = calloc(sizeof(*mtstate->miinfo), 1);
+			MultiInsertInfoInit(mtstate->miinfo, mtstate->rootResultRelInfo,
+					 mtstate->mt_transition_capture,
+					 estate, GetCurrentCommandId(true), 0);
+		}
+	}
+
 	/*
 	 * BEFORE ROW INSERT Triggers.
 	 *
@@ -2285,24 +2312,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	mtstate->mt_nplans = nplans;
 	mtstate->bistate = NULL;
 	mtstate->miinfo = NULL;
-	if (operation == CMD_INSERT && insert_in_bulk)
-	{
-		mtstate->bistate = GetBulkInsertState();
-
-		/*
-		 * For partitioned tables we don't support multi-inserts when there
-		 * are any statement level insert triggers.
-		 */
-		if (node->rootRelation == 0 ||
-			 !mtstate->rootResultRelInfo->ri_TrigDesc ||
-			 !mtstate->rootResultRelInfo->ri_TrigDesc->trig_insert_new_table)
-		{
-			mtstate->miinfo = calloc(sizeof(*mtstate->miinfo), 1);
-			MultiInsertInfoInit(mtstate->miinfo, mtstate->rootResultRelInfo,
-					 mtstate->mt_transition_capture,
-					 estate, GetCurrentCommandId(true), 0);
-		}
-	}
 
 	/* set up epqstate with dummy subplan data for the moment */
 	EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL, node->epqParam);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index a0a4034409..81ac9b1cb2 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -611,10 +611,6 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 									SetPGVariable("transaction_deferrable",
 												  list_make1(item->arg),
 												  true);
-								else if (strcmp(item->defname, "bulk") == 0)
-									SetPGVariable("bulk_insert",
-												  list_make1(item->arg),
-												  true);
 							}
 						}
 						break;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 170c96e749..3ed6b0650c 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -2037,16 +2037,6 @@ static struct config_bool ConfigureNamesBool[] =
 		NULL, NULL, NULL
 	},
 
-	{
-		{"bulk_insert", PGC_USERSET, CLIENT_CONN_STATEMENT,
-			gettext_noop("Sets the transaction to bulk insert mode."),
-			gettext_noop("A ring buffer of limited size will be used."),
-		},
-		&insert_in_bulk,
-		false,
-		NULL, NULL, NULL
-	},
-
 	/* End-of-list marker */
 	{
 		{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
@@ -3410,6 +3400,15 @@ static struct config_int ConfigureNamesInt[] =
 		check_huge_page_size, NULL, NULL
 	},
 
+	{
+		{"bulk_insert_ntuples", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Enable bulk insertions after this number of tuples."),
+			gettext_noop("A ring buffer of limited size will be used and updates done in batch"),
+		},
+		&bulk_insert_ntuples,
+		1000, -1, INT_MAX,
+	},
+
 	/* End-of-list marker */
 	{
 		{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/executor/nodeModifyTable.h b/src/include/executor/nodeModifyTable.h
index 80d82a9715..16a04151c8 100644
--- a/src/include/executor/nodeModifyTable.h
+++ b/src/include/executor/nodeModifyTable.h
@@ -17,7 +17,7 @@
 #include "executor/executor.h" // XXX
 #include "nodes/execnodes.h"
 
-extern PGDLLIMPORT bool insert_in_bulk;
+extern PGDLLIMPORT int bulk_insert_ntuples;
 
 extern void ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo,
 									   EState *estate, TupleTableSlot *slot,
@@ -74,6 +74,7 @@ typedef struct MultiInsertInfo
 	EState	   *estate;			/* Executor state used for COPY */
 	CommandId	mycid;			/* Command Id used for COPY */
 	int			ti_options;		/* table insert options */
+	// size_t		ntuples;		/* Number of rows inserted */
 
 	/* Line number for errors in copyfrom.c */
 	uint64		cur_lineno;
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 985a98f854..535380d276 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1183,6 +1183,7 @@ typedef struct ModifyTableState
 	BulkInsertState	bistate;	/* state for bulk insert like INSERT SELECT, when miinfo cannot be used */
 	ResultRelInfo	*prevResultRelInfo; /* last child inserted with bistate */
 	struct MultiInsertInfo	*miinfo;
+	size_t		ntuples;	/* Number of tuples inserted; XXX: could be in miinfo ? */
 
 	/*
 	 * Slot for storing tuples in the root partitioned table's rowtype during
diff --git a/src/test/regress/expected/insert.out b/src/test/regress/expected/insert.out
index e0c83d7427..b894180152 100644
--- a/src/test/regress/expected/insert.out
+++ b/src/test/regress/expected/insert.out
@@ -477,7 +477,11 @@ select * from hash_parted;
 (2 rows)
 
 -- exercise bulk insert to partitions
+SET client_min_messages=debug;
 insert into hash_parted select generate_series(1,9999);
+DEBUG:  enabling bulk insert
+DEBUG:  enabling multi insert
+RESET client_min_messages;
 select count(1) from hash_parted;
  count 
 -------
diff --git a/src/test/regress/sql/insert.sql b/src/test/regress/sql/insert.sql
index 99ec18d9a2..ff9b57af5e 100644
--- a/src/test/regress/sql/insert.sql
+++ b/src/test/regress/sql/insert.sql
@@ -289,7 +289,9 @@ insert into hash_parted values(11);
 insert into hpart0 values(12);
 select * from hash_parted;
 -- exercise bulk insert to partitions
+SET client_min_messages=debug;
 insert into hash_parted select generate_series(1,9999);
+RESET client_min_messages;
 select count(1) from hash_parted;
 commit;
 -- test that index was updated
-- 
2.17.0

