diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 2119db4..41a7067 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -2826,52 +2826,24 @@ CopyFrom(CopyState cstate) /* * It's generally more efficient to prepare a bunch of tuples for * insertion, and insert them in bulk, for example, with one - * table_multi_insert() call than call table_tuple_insert() separately - * for every tuple. However, there are a number of reasons why we might - * not be able to do this. We check some conditions below while some - * other target relation properties are checked in InitResultRelInfo(). - * Partition initialization will use result of this check implicitly as - * the ri_usesMultiInsert value of the parent relation. + * table_multi_insert() call than call table_tuple_insert() separately for + * every tuple. However, there are a number of reasons why we might not be + * able to do this. For example, if there any volatile expressions in the + * table's default values or in the statement's WHERE clause, which may + * query the table we are inserting into, buffering tuples might produce + * wrong results. Also, the relation we are trying to insert into itself + * may not be amenable to buffered inserts. + * + * Note: For partitions, this flag is set considering the target table's + * flag that is being set here and partition's own properties which are + * checked by calling ExecRelationAllowsMultiInsert(). It does not matter + * whether partitions have any volatile default expressions as we use the + * defaults from the target of the COPY command. */ - if (!checkMultiInsertMode(target_resultRelInfo, NULL)) - { - /* - * Do nothing. Can't allow multi-insert mode if previous conditions - * checking disallow this. - */ - } - else if (cstate->volatile_defexprs || list_length(cstate->attnumlist) == 0) - { - /* - * Can't support bufferization of copy into foreign tables without any - * defined columns or if there are any volatile default expressions in the - * table. Similarly to the trigger case above, such expressions may query - * the table we're inserting into. - * - * Note: It does not matter if any partitions have any volatile - * default expressions as we use the defaults from the target of the - * COPY command. - */ - } - else if (contain_volatile_functions(cstate->whereClause)) - { - /* - * Can't support multi-inserts if there are any volatile function - * expressions in WHERE clause. Similarly to the trigger case above, - * such expressions may query the table we're inserting into. - */ - } - else - { - /* - * Looks okay to try multi-insert. - * - * For partitioned tables, whether or not to use multi-insert depends - * on the individual parition's properties which are also checked in - * InitResultRelInfo(). - */ + if (!cstate->volatile_defexprs && + !contain_volatile_functions(cstate->whereClause) && + ExecRelationAllowsMultiInsert(target_resultRelInfo, NULL)) target_resultRelInfo->ri_usesMultiInsert = true; - } /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -2898,10 +2870,10 @@ CopyFrom(CopyState cstate) * Init COPY into foreign table. Initialization of copying into foreign * partitions will be done later. */ - if (target_resultRelInfo->ri_FdwRoutine != NULL && - target_resultRelInfo->ri_FdwRoutine->BeginForeignInsert != NULL) - target_resultRelInfo->ri_FdwRoutine->BeginForeignInsert(mtstate, - resultRelInfo); + if (resultRelInfo->ri_FdwRoutine != NULL && + resultRelInfo->ri_FdwRoutine->BeginForeignInsert != NULL) + resultRelInfo->ri_FdwRoutine->BeginForeignInsert(mtstate, + resultRelInfo); /* Prepare to catch AFTER triggers. */ AfterTriggerBeginQuery(); @@ -3302,7 +3274,7 @@ CopyFrom(CopyState cstate) if (target_resultRelInfo->ri_FdwRoutine != NULL && target_resultRelInfo->ri_FdwRoutine->EndForeignInsert != NULL) target_resultRelInfo->ri_FdwRoutine->EndForeignInsert(estate, - target_resultRelInfo); + target_resultRelInfo); /* Tear down the multi-insert buffer data */ CopyMultiInsertInfoCleanup(&multiInsertInfo); diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 12ee7f2..4999474 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1351,6 +1351,55 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, } /* + * ExecRelationAllowsMultiInsert + * Does this relation allow caller to use multi-insert mode when + * inserting rows into it? + */ +bool +ExecRelationAllowsMultiInsert(const ResultRelInfo *rri, + const ResultRelInfo *partition_root) +{ + Assert(rri->ri_usesMultiInsert == false); + + /* + * If a partition's root parent isn't allowed to use it, neither is the + * partition. + */ + if (partition_root && !partition_root->ri_usesMultiInsert) + return false; + + /* + * Can't support multi-inserts when there are any BEFORE/INSTEAD OF + * triggers on the table. Such triggers might query the table we're + * inserting into and act differently if the tuples that have already + * been processed and prepared for insertion are not there. + */ + if (rri->ri_TrigDesc != NULL && + (rri->ri_TrigDesc->trig_insert_before_row || + rri->ri_TrigDesc->trig_insert_instead_row)) + return false; + + /* + * For partitioned tables we can't support multi-inserts when there are + * any statement level insert triggers. It might be possible to allow + * partitioned tables with such triggers in the future, but for now, + * CopyMultiInsertInfoFlush expects that any before row insert and + * statement level insert triggers are on the same relation. + */ + if (rri->ri_RelationDesc->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && + rri->ri_TrigDesc != NULL && + rri->ri_TrigDesc->trig_insert_new_table) + return false; + + /* Foreign tables don't support multi-inserts. */ + if (rri->ri_FdwRoutine != NULL) + return false; + + /* OK, caller can use multi-insert on this relation. */ + return true; +} + +/* * ExecGetTriggerResultRel * Get a ResultRelInfo for a trigger target relation. * diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index baaa0f6..d752fe3 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -548,46 +548,6 @@ ExecHashSubPlanResultRelsByOid(ModifyTableState *mtstate, } } -bool -checkMultiInsertMode(const ResultRelInfo *rri, const ResultRelInfo *parent) -{ - Assert(rri->ri_usesMultiInsert == false); - - if (parent && !parent->ri_usesMultiInsert) - return false; - - /* Check if the relation allows to use "multi-insert" mode. */ - if (rri->ri_TrigDesc != NULL && - (rri->ri_TrigDesc->trig_insert_before_row || - rri->ri_TrigDesc->trig_insert_instead_row)) - /* - * Can't support multi-inserts when there are any BEFORE/INSTEAD OF - * triggers on the table. Such triggers might query the table we're - * inserting into and act differently if the tuples that have already - * been processed and prepared for insertion are not there. - */ - return false; - - if (rri->ri_RelationDesc->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && - rri->ri_TrigDesc != NULL && - rri->ri_TrigDesc->trig_insert_new_table) - /* - * For partitioned tables we can't support multi-inserts when there - * are any statement level insert triggers. It might be possible to - * allow partitioned tables with such triggers in the future, but for - * now, CopyMultiInsertInfoFlush expects that any before row insert - * and statement level insert triggers are on the same relation. - */ - return false; - - if (rri->ri_FdwRoutine != NULL) - /* Foreign tables don't support multi-inserts. */ - return false; - - /* OK, caller can use multi-insert on this relation. */ - return true; -} - /* * ExecInitPartitionInfo * Lock the partition and initialize ResultRelInfo. Also setup other @@ -628,7 +588,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, * parent and its child. */ leaf_part_rri->ri_usesMultiInsert = - checkMultiInsertMode(leaf_part_rri, rootResultRelInfo); + ExecRelationAllowsMultiInsert(leaf_part_rri, rootResultRelInfo); /* * Verify result relation is a valid target for an INSERT. An UPDATE of a diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 415e117..1146685 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -190,6 +190,8 @@ extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Index resultRelationIndex, Relation partition_root, int instrument_options); +extern bool ExecRelationAllowsMultiInsert(const ResultRelInfo *rri, + const ResultRelInfo *partition_root); extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid); extern void ExecCleanUpTriggerState(EState *estate); extern void ExecConstraints(ResultRelInfo *resultRelInfo,