]> granicus.if.org Git - postgresql/commitdiff
Print line number correctly in COPY.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Thu, 23 May 2013 11:49:59 +0000 (07:49 -0400)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Thu, 23 May 2013 11:53:01 +0000 (07:53 -0400)
When COPY uses the multi-insert method to insert a batch of tuples into the
heap at a time, incorrect line number was printed if something went wrong in
inserting the index tuples (primary key failure, for exampl), or processing
after row triggers.

Fixes bug #8173 reported by Lloyd Albin. Backpatch to 9.2, where the multi-
insert code was added.

src/backend/commands/copy.c

index cd6b13c09f32d2ba6de59cd174fe9cfd67e95d4c..ac26e811a3eaca127cd7f2741b6e3c25fc298680 100644 (file)
@@ -176,6 +176,7 @@ typedef struct CopyStateData
         */
        StringInfoData line_buf;
        bool            line_buf_converted;             /* converted to server encoding? */
+       bool            line_buf_valid;                 /* contains the row being processed? */
 
        /*
         * Finally, raw_buf holds raw data read from the data source (file or
@@ -283,7 +284,8 @@ static void CopyFromInsertBatch(CopyState cstate, EState *estate,
                                        CommandId mycid, int hi_options,
                                        ResultRelInfo *resultRelInfo, TupleTableSlot *myslot,
                                        BulkInsertState bistate,
-                                       int nBufferedTuples, HeapTuple *bufferedTuples);
+                                       int nBufferedTuples, HeapTuple *bufferedTuples,
+                                       int firstBufferedLineNo);
 static bool CopyReadLine(CopyState cstate);
 static bool CopyReadLineText(CopyState cstate);
 static int     CopyReadAttributesText(CopyState cstate);
@@ -1776,8 +1778,18 @@ CopyFromErrorCallback(void *arg)
                }
                else
                {
-                       /* error is relevant to a particular line */
-                       if (cstate->line_buf_converted || !cstate->need_transcoding)
+                       /*
+                        * Error is relevant to a particular line.
+                        *
+                        * If line_buf still contains the correct line, and it's already
+                        * transcoded, print it. If it's still in a foreign encoding,
+                        * it's quite likely that the error is precisely a failure to do
+                        * encoding conversion (ie, bad data). We dare not try to convert
+                        * it, and at present there's no way to regurgitate it without
+                        * conversion. So we have to punt and just report the line number.
+                        */
+                       if (cstate->line_buf_valid &&
+                               (cstate->line_buf_converted || !cstate->need_transcoding))
                        {
                                char       *lineval;
 
@@ -1788,14 +1800,6 @@ CopyFromErrorCallback(void *arg)
                        }
                        else
                        {
-                               /*
-                                * Here, the line buffer is still in a foreign encoding, and
-                                * indeed it's quite likely that the error is precisely a
-                                * failure to do encoding conversion (ie, bad data).  We dare
-                                * not try to convert it, and at present there's no way to
-                                * regurgitate it without conversion.  So we have to punt and
-                                * just report the line number.
-                                */
                                errcontext("COPY %s, line %d",
                                                   cstate->cur_relname, cstate->cur_lineno);
                        }
@@ -1865,6 +1869,7 @@ CopyFrom(CopyState cstate)
 #define MAX_BUFFERED_TUPLES 1000
        HeapTuple  *bufferedTuples = NULL;      /* initialize to silence warning */
        Size            bufferedTuplesSize = 0;
+       int                     firstBufferedLineNo = 0;
 
        Assert(cstate->rel);
 
@@ -2064,6 +2069,8 @@ CopyFrom(CopyState cstate)
                        if (useHeapMultiInsert)
                        {
                                /* Add this tuple to the tuple buffer */
+                               if (nBufferedTuples == 0)
+                                       firstBufferedLineNo = cstate->cur_lineno;
                                bufferedTuples[nBufferedTuples++] = tuple;
                                bufferedTuplesSize += tuple->t_len;
 
@@ -2078,7 +2085,8 @@ CopyFrom(CopyState cstate)
                                {
                                        CopyFromInsertBatch(cstate, estate, mycid, hi_options,
                                                                                resultRelInfo, myslot, bistate,
-                                                                               nBufferedTuples, bufferedTuples);
+                                                                               nBufferedTuples, bufferedTuples,
+                                                                               firstBufferedLineNo);
                                        nBufferedTuples = 0;
                                        bufferedTuplesSize = 0;
                                }
@@ -2114,7 +2122,8 @@ CopyFrom(CopyState cstate)
        if (nBufferedTuples > 0)
                CopyFromInsertBatch(cstate, estate, mycid, hi_options,
                                                        resultRelInfo, myslot, bistate,
-                                                       nBufferedTuples, bufferedTuples);
+                                                       nBufferedTuples, bufferedTuples,
+                                                       firstBufferedLineNo);
 
        /* Done, clean up */
        error_context_stack = errcontext.previous;
@@ -2157,10 +2166,19 @@ static void
 CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
                                        int hi_options, ResultRelInfo *resultRelInfo,
                                        TupleTableSlot *myslot, BulkInsertState bistate,
-                                       int nBufferedTuples, HeapTuple *bufferedTuples)
+                                       int nBufferedTuples, HeapTuple *bufferedTuples,
+                                       int firstBufferedLineNo)
 {
        MemoryContext oldcontext;
        int                     i;
+       int                     save_cur_lineno;
+
+       /*
+        * Print error context information correctly, if one of the operations
+        * below fail.
+        */
+       cstate->line_buf_valid = false;
+       save_cur_lineno = cstate->cur_lineno;
 
        /*
         * heap_multi_insert leaks memory, so switch to short-lived memory context
@@ -2185,6 +2203,7 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
                {
                        List       *recheckIndexes;
 
+                       cstate->cur_lineno = firstBufferedLineNo + i;
                        ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
                        recheckIndexes =
                                ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self),
@@ -2204,10 +2223,16 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
                         resultRelInfo->ri_TrigDesc->trig_insert_after_row)
        {
                for (i = 0; i < nBufferedTuples; i++)
+               {
+                       cstate->cur_lineno = firstBufferedLineNo + i;
                        ExecARInsertTriggers(estate, resultRelInfo,
                                                                 bufferedTuples[i],
                                                                 NIL);
+               }
        }
+
+       /* reset cur_lineno to where we were */
+       cstate->cur_lineno = save_cur_lineno;
 }
 
 /*
@@ -2715,6 +2740,7 @@ CopyReadLine(CopyState cstate)
        bool            result;
 
        resetStringInfo(&cstate->line_buf);
+       cstate->line_buf_valid = true;
 
        /* Mark that encoding conversion hasn't occurred yet */
        cstate->line_buf_converted = false;