Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1juniN-0008KV-SK for pgsql-hackers@arkaria.postgresql.org; Mon, 13 Jul 2020 01:57:08 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1juniM-0006X6-Rb for pgsql-hackers@arkaria.postgresql.org; Mon, 13 Jul 2020 01:57:06 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1juniM-0006Wz-B6 for pgsql-hackers@lists.postgresql.org; Mon, 13 Jul 2020 01:57:06 +0000 Received: from mail-il1-x141.google.com ([2607:f8b0:4864:20::141]) by makus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1juniJ-00060L-In for pgsql-hackers@lists.postgresql.org; Mon, 13 Jul 2020 01:57:05 +0000 Received: by mail-il1-x141.google.com with SMTP id t18so9886316ilh.2 for ; Sun, 12 Jul 2020 18:57:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=telsasoft-com.20150623.gappssmtp.com; s=20150623; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to:user-agent; bh=OTJCBuX9Wn3KgieHy2cnAVBs0ScSSU1VFZhyjkGyaCk=; b=Go67o9gLt7jfYElz0TZfTO3CgSVMabFSwp+qbbftQq5lKwA2N7T4k3twsfRokB/02z wyIVsTE0QR0AZ+j6R2ZXIDG3vtUz3dETQpFpPcjCVv5XxzdRVppc6+zI15bPOXHM/ygu znydkvsyhOKoOg+bbxJDU29P6//NqZdhQTIIL/trF2rQp81rPoF8C+C4CWGTXCKw3AGx YK/FqirsDWJkDAa3+ixpLXRc9Ym5bSr//uct1PHLNXVvdFkcAQMdcHVyEN56+JqVm6G8 ALhJWY2x5y2n4C8RtKFLACaVI/IGiBz9fFYF8zpITgKGsLOuFPRLbuxcmJuRILfI9kuF f+xQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to:user-agent; bh=OTJCBuX9Wn3KgieHy2cnAVBs0ScSSU1VFZhyjkGyaCk=; b=nuIXz3z0VBto8GpHILKHMqOlHMjS+EoNiDL4ULHmiApVtNsgoQkmKgWtG1BhRhF6b8 LPrqm+vLTHGusR5Q3KQ+XU3RltgcQWNfCt69CIyY5nuB17if+ozxRrq5vtYx4vJbEHdI b/6+sscY/392hhpzsczHvGJzlgEJdAJprmo8wKQwE5N7kPZgMeUCWiEpp2XurnBHVCeP egwArHT6eie7VQ48nLPUrtZpwLAkr1/seVxmwq04UolL9u6LoLBIzJcEt7i9rcIbp9WA YmutoPdMUzugDiIu9py/d7UMNZ1TtMKiJD21Pi+Yq7v+dUs3Jhx8VfS75QK+29bW9RRc uTBA== X-Gm-Message-State: AOAM531O1LqCLrQCybMxhDYYEV3KFqsEzvFwGcqgLn0muYM0/5Cqe8B+ /IYChDU95xJPr1sVSr3Qqq+zWaa4KHI= X-Google-Smtp-Source: ABdhPJzBu0Nfd3U0wHEui9FyWmCP3Morq+fKZyTEIRm4rmmd5Y20+7wi9tac+eE96mqWWXTFv31hsw== X-Received: by 2002:a05:6e02:103:: with SMTP id t3mr28228611ilm.147.1594605422804; Sun, 12 Jul 2020 18:57:02 -0700 (PDT) Received: from pryzbyj.telsasoft (charmander.telsasoft.com. [50.244.222.1]) by smtp.gmail.com with ESMTPSA id m16sm7512090ili.26.2020.07.12.18.57.01 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 12 Jul 2020 18:57:01 -0700 (PDT) Received: by pryzbyj.telsasoft (Postfix, from userid 1000) id A74A58003B6; Sun, 12 Jul 2020 20:57:00 -0500 (CDT) Date: Sun, 12 Jul 2020 20:57:00 -0500 From: Justin Pryzby To: Andres Freund Cc: pgsql-hackers@lists.postgresql.org Subject: Re: should INSERT SELECT use a BulkInsertState? Message-ID: <20200713015700.GA23581@telsasoft.com> References: <20200508072545.GA9701@telsasoft.com> <20200604173047.dirdp6zln7ogj54k@alap3.anarazel.de> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="9zSXsLTf0vkW971A" Content-Disposition: inline In-Reply-To: <20200604173047.dirdp6zln7ogj54k@alap3.anarazel.de> User-Agent: Mutt/1.9.4 (2018-02-28) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk --9zSXsLTf0vkW971A Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Thu, Jun 04, 2020 at 10:30:47AM -0700, Andres Freund wrote: > On 2020-05-08 02:25:45 -0500, Justin Pryzby wrote: > > Seems to me it should, at least conditionally. At least if there's a function > > scan or a relation or .. > > Well, the problem is that this can cause very very significant > regressions. As in 10x slower or more. The ringbuffer can cause constant > XLogFlush() calls (due to the lsn interlock), and the eviction from > shared_buffers (regardless of actual available) will mean future vacuums > etc will be much slower. I think this is likely to cause pretty > widespread regressions on upgrades. > > Now, it sucks that we have this problem in the general facility that's > supposed to be used for this kind of bulk operation. But I don't really > see it realistic as expanding use of bulk insert strategies unless we > have some more fundamental fixes. I made this conditional on BEGIN BULK/SET bulk, so I'll solicit comments on that. postgres=# \t on \\ \set QUIET \\ VACUUM FULL t; \dt+ t \\ begin ; \timing on \\ INSERT INTO t SELECT * FROM t; rollback; SELECT COUNT(1), usagecount FROM pg_buffercache GROUP BY 2 ORDER BY 2; | public | t | table | pryzbyj | 35 MB | |Time: 9497.318 ms (00:09.497) | 33 | 1 | 3 | 2 | 18 | 3 | 5 | 4 | 4655 | 5 | 11670 | vs postgres=# \t on \\ \set QUIET \\ VACUUM FULL t; \dt+ t \\ begin BULK ; \timing on \\ INSERT INTO t SELECT * FROM t; rollback; SELECT COUNT(1), usagecount FROM pg_buffercache GROUP BY 2 ORDER BY 2; | public | t | table | pryzbyj | 35 MB | |Time: 8268.780 ms (00:08.269) | 2080 | 1 | 3 | 2 | 19 | 4 | 234 | 5 | 14048 | And: postgres=# begin ; \x \\ \t \\ SELECT statement_timestamp(); \o /dev/null \\ SELECT 'INSERT INTO t VALUES(0)' FROM generate_series(1,999999); \set ECHO errors \\ \set QUIET on \\ \o \\ \gexec \\ SELECT statement_timestamp(); abort; \x \\ SELECT COUNT(1), usagecount FROM pg_buffercache GROUP BY 2 ORDER BY 2; a |statement_timestamp | 2020-07-12 20:31:43.717328-05 |statement_timestamp | 2020-07-12 20:36:16.692469-05 | | 52 | 1 | 24 | 2 | 17 | 3 | 6 | 4 | 4531 | 5 | 11754 | vs postgres=# begin BULK ; \x \\ \t \\ SELECT statement_timestamp(); \o /dev/null \\ SELECT 'INSERT INTO t VALUES(0)' FROM generate_series(1,999999); \set ECHO errors \\ \set QUIET on \\ \o \\ \gexec \\ SELECT statement_timestamp(); abort; \x \\ SELECT COUNT(1), usagecount FROM pg_buffercache GROUP BY 2 ORDER BY 2; a |statement_timestamp | 2020-07-12 20:43:47.089538-05 |statement_timestamp | 2020-07-12 20:48:04.798138-05 | | 4456 | 1 | 22 | 2 | 1 | 3 | 7 | 4 | 79 | 5 | 11819 | -- Justin --9zSXsLTf0vkW971A Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v2-0001-Allow-INSERT-SELECT-to-use-a-BulkInsertState.patch" From 0362537e0f3a8496ac760574931db66c59f7c1ba Mon Sep 17 00:00:00 2001 From: Justin Pryzby Date: Fri, 8 May 2020 02:17:32 -0500 Subject: [PATCH v2] Allow INSERT SELECT to use a BulkInsertState --- src/backend/executor/nodeModifyTable.c | 23 +++++++++++++++++++++-- src/backend/parser/gram.y | 6 +++++- src/backend/tcop/utility.c | 4 ++++ src/backend/utils/misc/guc.c | 12 +++++++++++- src/include/executor/nodeModifyTable.h | 2 ++ src/include/nodes/execnodes.h | 2 ++ src/include/parser/kwlist.h | 1 + 7 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 20a4c474cc..5ff4a2e901 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -75,6 +75,8 @@ static ResultRelInfo *getTargetResultRelInfo(ModifyTableState *node); static void ExecSetupChildParentMapForSubplan(ModifyTableState *mtstate); static TupleConversionMap *tupconv_map_for_subplan(ModifyTableState *node, int whichplan); +/* guc */ +bool insert_in_bulk = false; /* * Verify that the tuples to be produced by INSERT or UPDATE match the @@ -578,7 +580,7 @@ ExecInsert(ModifyTableState *mtstate, table_tuple_insert_speculative(resultRelationDesc, slot, estate->es_output_cid, 0, - NULL, + NULL, /* Bulk insert not supported */ specToken); /* insert index entries for tuple */ @@ -617,7 +619,7 @@ ExecInsert(ModifyTableState *mtstate, /* insert the tuple normally */ table_tuple_insert(resultRelationDesc, slot, estate->es_output_cid, - 0, NULL); + 0, mtstate->bistate); /* insert index entries for tuple */ if (resultRelInfo->ri_NumIndices > 0) @@ -2332,6 +2334,17 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) mtstate->mt_arowmarks = (List **) palloc0(sizeof(List *) * nplans); mtstate->mt_nplans = nplans; + mtstate->bistate = NULL; + if (operation == CMD_INSERT && + node->onConflictAction != ONCONFLICT_UPDATE && + node->rootResultRelIndex < 0) + { + // Plan *p = linitial(node->plans); + Assert(nplans == 1); + + if (insert_in_bulk) + mtstate->bistate = GetBulkInsertState(); + } /* set up epqstate with dummy subplan data for the moment */ EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL, node->epqParam); @@ -2776,6 +2789,12 @@ ExecEndModifyTable(ModifyTableState *node) resultRelInfo); } + if (node->bistate) + { + FreeBulkInsertState(node->bistate); + table_finish_bulk_insert((getTargetResultRelInfo(node))->ri_RelationDesc, 0); + } + /* * Close all the partitioned tables, leaf partitions, and their indices * and release the slot used for tuple routing, if set. diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dbb47d4982..38c4d1d1e7 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -629,7 +629,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); ASSERTION ASSIGNMENT ASYMMETRIC AT ATTACH ATTRIBUTE AUTHORIZATION BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT - BOOLEAN_P BOTH BY + BOOLEAN_P BOTH BY BULK CACHE CALL CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE @@ -9841,6 +9841,9 @@ transaction_mode_item: | NOT DEFERRABLE { $$ = makeDefElem("transaction_deferrable", makeIntConst(false, @1), @1); } + | BULK + { $$ = makeDefElem("bulk", + makeIntConst(true, @1), @1); } ; /* Syntax with commas is SQL-spec, without commas is Postgres historical */ @@ -15041,6 +15044,7 @@ unreserved_keyword: | BACKWARD | BEFORE | BEGIN_P + | BULK | BY | CACHE | CALL diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 9b0c376c8c..061a022374 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -611,6 +611,10 @@ 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 442e5af2b2..66a7dcf5a9 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -51,6 +51,7 @@ #include "libpq/pqformat.h" #include "miscadmin.h" #include "optimizer/cost.h" +#include "executor/nodeModifyTable.h" #include "optimizer/geqo.h" #include "optimizer/optimizer.h" #include "optimizer/paths.h" @@ -917,7 +918,6 @@ static const unit_conversion time_unit_conversion_table[] = * variable_is_guc_list_quote() in src/bin/pg_dump/dumputils.c. */ - /******** option records follow ********/ static struct config_bool ConfigureNamesBool[] = @@ -2041,6 +2041,16 @@ 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 diff --git a/src/include/executor/nodeModifyTable.h b/src/include/executor/nodeModifyTable.h index 4ec4ebdabc..c0a16e6098 100644 --- a/src/include/executor/nodeModifyTable.h +++ b/src/include/executor/nodeModifyTable.h @@ -15,6 +15,8 @@ #include "nodes/execnodes.h" +extern PGDLLIMPORT bool insert_in_bulk; + extern void ExecComputeStoredGenerated(EState *estate, TupleTableSlot *slot, CmdType cmdtype); extern ModifyTableState *ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 0187989fd1..2679cbbb9b 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -14,6 +14,7 @@ #ifndef EXECNODES_H #define EXECNODES_H +#include "access/heapam.h" #include "access/tupconvert.h" #include "executor/instrument.h" #include "fmgr.h" @@ -1177,6 +1178,7 @@ typedef struct ModifyTableState List **mt_arowmarks; /* per-subplan ExecAuxRowMark lists */ EPQState mt_epqstate; /* for evaluating EvalPlanQual rechecks */ bool fireBSTriggers; /* do we need to fire stmt triggers? */ + BulkInsertState bistate; /* State for bulk insert like INSERT SELECT */ /* * Slot for storing tuples in the root partitioned table's rowtype during diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index 08f22ce211..80dfbac702 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -60,6 +60,7 @@ PG_KEYWORD("binary", BINARY, TYPE_FUNC_NAME_KEYWORD) PG_KEYWORD("bit", BIT, COL_NAME_KEYWORD) PG_KEYWORD("boolean", BOOLEAN_P, COL_NAME_KEYWORD) PG_KEYWORD("both", BOTH, RESERVED_KEYWORD) +PG_KEYWORD("bulk", BULK, UNRESERVED_KEYWORD) PG_KEYWORD("by", BY, UNRESERVED_KEYWORD) PG_KEYWORD("cache", CACHE, UNRESERVED_KEYWORD) PG_KEYWORD("call", CALL, UNRESERVED_KEYWORD) -- 2.17.0 --9zSXsLTf0vkW971A--