@@ -2323,9 +2323,9 @@ CopyFrom(CopyState cstate)
23232323 ExprContext * econtext ;
23242324 TupleTableSlot * myslot ;
23252325 MemoryContext oldcontext = CurrentMemoryContext ;
2326+ MemoryContext batchcontext ;
23262327
23272328 PartitionTupleRouting * proute = NULL ;
2328- ExprContext * secondaryExprContext = NULL ;
23292329 ErrorContextCallback errcallback ;
23302330 CommandId mycid = GetCurrentCommandId (true);
23312331 int hi_options = 0 ; /* start with default heap_insert options */
@@ -2639,20 +2639,10 @@ CopyFrom(CopyState cstate)
26392639 * Normally, when performing bulk inserts we just flush the insert
26402640 * buffer whenever it becomes full, but for the partitioned table
26412641 * case, we flush it whenever the current tuple does not belong to the
2642- * same partition as the previous tuple, and since we flush the
2643- * previous partition's buffer once the new tuple has already been
2644- * built, we're unable to reset the estate since we'd free the memory
2645- * in which the new tuple is stored. To work around this we maintain
2646- * a secondary expression context and alternate between these when the
2647- * partition changes. This does mean we do store the first new tuple
2648- * in a different context than subsequent tuples, but that does not
2649- * matter, providing we don't free anything while it's still needed.
2642+ * same partition as the previous tuple.
26502643 */
26512644 if (proute )
2652- {
26532645 insertMethod = CIM_MULTI_CONDITIONAL ;
2654- secondaryExprContext = CreateExprContext (estate );
2655- }
26562646 else
26572647 insertMethod = CIM_MULTI ;
26582648
@@ -2685,29 +2675,39 @@ CopyFrom(CopyState cstate)
26852675 errcallback .previous = error_context_stack ;
26862676 error_context_stack = & errcallback ;
26872677
2678+ /*
2679+ * Set up memory context for batches. For cases without batching we could
2680+ * use the per-tuple context, but it's simpler to just use it every time.
2681+ */
2682+ batchcontext = AllocSetContextCreate (CurrentMemoryContext ,
2683+ "batch context" ,
2684+ ALLOCSET_DEFAULT_SIZES );
2685+
26882686 for (;;)
26892687 {
26902688 TupleTableSlot * slot ;
26912689 bool skip_tuple ;
26922690
26932691 CHECK_FOR_INTERRUPTS ();
26942692
2695- if (nBufferedTuples == 0 )
2696- {
2697- /*
2698- * Reset the per-tuple exprcontext. We can only do this if the
2699- * tuple buffer is empty. (Calling the context the per-tuple
2700- * memory context is a bit of a misnomer now.)
2701- */
2702- ResetPerTupleExprContext (estate );
2703- }
2693+ /*
2694+ * Reset the per-tuple exprcontext. We do this after every tuple, to
2695+ * clean-up after expression evaluations etc.
2696+ */
2697+ ResetPerTupleExprContext (estate );
27042698
2705- /* Switch into its memory context */
2699+ /*
2700+ * Switch to per-tuple context before calling NextCopyFrom, which does
2701+ * evaluate default expressions etc. and requires per-tuple context.
2702+ */
27062703 MemoryContextSwitchTo (GetPerTupleMemoryContext (estate ));
27072704
27082705 if (!NextCopyFrom (cstate , econtext , values , nulls ))
27092706 break ;
27102707
2708+ /* Switch into per-batch memory context before forming the tuple. */
2709+ MemoryContextSwitchTo (batchcontext );
2710+
27112711 /* And now we can form the input tuple. */
27122712 tuple = heap_form_tuple (tupDesc , values , nulls );
27132713
@@ -2756,7 +2756,7 @@ CopyFrom(CopyState cstate)
27562756 */
27572757 if (nBufferedTuples > 0 )
27582758 {
2759- ExprContext * swapcontext ;
2759+ MemoryContext oldcontext ;
27602760
27612761 CopyFromInsertBatch (cstate , estate , mycid , hi_options ,
27622762 prevResultRelInfo , myslot , bistate ,
@@ -2765,29 +2765,29 @@ CopyFrom(CopyState cstate)
27652765 nBufferedTuples = 0 ;
27662766 bufferedTuplesSize = 0 ;
27672767
2768- Assert (secondaryExprContext );
2769-
27702768 /*
2771- * Normally we reset the per-tuple context whenever
2772- * the bufferedTuples array is empty at the beginning
2773- * of the loop, however, it is possible since we flush
2774- * the buffer here that the buffer is never empty at
2775- * the start of the loop. To prevent the per-tuple
2776- * context from never being reset we maintain a second
2777- * context and alternate between them when the
2778- * partition changes. We can now reset
2779- * secondaryExprContext as this is no longer needed,
2780- * since we just flushed any tuples stored in it. We
2781- * also now switch over to the other context. This
2782- * does mean that the first tuple in the buffer won't
2783- * be in the same context as the others, but that does
2784- * not matter since we only reset it after the flush.
2769+ * The tuple is already allocated in the batch context, which
2770+ * we want to reset. So to keep the tuple we copy it into the
2771+ * short-lived (per-tuple) context, reset the batch context
2772+ * and then copy it back into the per-batch one.
27852773 */
2786- ReScanExprContext (secondaryExprContext );
2774+ oldcontext = MemoryContextSwitchTo (GetPerTupleMemoryContext (estate ));
2775+ tuple = heap_copytuple (tuple );
2776+ MemoryContextSwitchTo (oldcontext );
2777+
2778+ /* cleanup the old batch */
2779+ MemoryContextReset (batchcontext );
27872780
2788- swapcontext = secondaryExprContext ;
2789- secondaryExprContext = estate -> es_per_tuple_exprcontext ;
2790- estate -> es_per_tuple_exprcontext = swapcontext ;
2781+ /* copy the tuple back to the per-batch context */
2782+ oldcontext = MemoryContextSwitchTo (batchcontext );
2783+ tuple = heap_copytuple (tuple );
2784+ MemoryContextSwitchTo (oldcontext );
2785+
2786+ /*
2787+ * Also push the tuple copy to the slot (resetting the context
2788+ * invalidated the slot contents).
2789+ */
2790+ ExecStoreHeapTuple (tuple , slot , false);
27912791 }
27922792
27932793 nPartitionChanges ++ ;
@@ -2893,10 +2893,10 @@ CopyFrom(CopyState cstate)
28932893 slot = execute_attr_map_slot (map -> attrMap , slot , new_slot );
28942894
28952895 /*
2896- * Get the tuple in the per-tuple context, so that it will be
2896+ * Get the tuple in the per-batch context, so that it will be
28972897 * freed after each batch insert.
28982898 */
2899- oldcontext = MemoryContextSwitchTo (GetPerTupleMemoryContext ( estate ) );
2899+ oldcontext = MemoryContextSwitchTo (batchcontext );
29002900 tuple = ExecCopySlotHeapTuple (slot );
29012901 MemoryContextSwitchTo (oldcontext );
29022902 }
@@ -2972,6 +2972,9 @@ CopyFrom(CopyState cstate)
29722972 firstBufferedLineNo );
29732973 nBufferedTuples = 0 ;
29742974 bufferedTuplesSize = 0 ;
2975+
2976+ /* free memory occupied by tuples from the batch */
2977+ MemoryContextReset (batchcontext );
29752978 }
29762979 }
29772980 else
@@ -3053,6 +3056,8 @@ CopyFrom(CopyState cstate)
30533056
30543057 MemoryContextSwitchTo (oldcontext );
30553058
3059+ MemoryContextDelete (batchcontext );
3060+
30563061 /*
30573062 * In the old protocol, tell pqcomm that we can process normal protocol
30583063 * messages again.
0 commit comments