Print line number correctly in COPY.

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.
This commit is contained in:
Heikki Linnakangas 2013-05-23 07:49:59 -04:00
parent bc41ef4791
commit e2ef289363
1 changed files with 40 additions and 14 deletions

View File

@ -183,6 +183,7 @@ typedef struct CopyStateData
*/ */
StringInfoData line_buf; StringInfoData line_buf;
bool line_buf_converted; /* converted to server encoding? */ 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 * Finally, raw_buf holds raw data read from the data source (file or
@ -292,7 +293,8 @@ static void CopyFromInsertBatch(CopyState cstate, EState *estate,
CommandId mycid, int hi_options, CommandId mycid, int hi_options,
ResultRelInfo *resultRelInfo, TupleTableSlot *myslot, ResultRelInfo *resultRelInfo, TupleTableSlot *myslot,
BulkInsertState bistate, BulkInsertState bistate,
int nBufferedTuples, HeapTuple *bufferedTuples); int nBufferedTuples, HeapTuple *bufferedTuples,
int firstBufferedLineNo);
static bool CopyReadLine(CopyState cstate); static bool CopyReadLine(CopyState cstate);
static bool CopyReadLineText(CopyState cstate); static bool CopyReadLineText(CopyState cstate);
static int CopyReadAttributesText(CopyState cstate); static int CopyReadAttributesText(CopyState cstate);
@ -1923,8 +1925,18 @@ CopyFromErrorCallback(void *arg)
} }
else 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; char *lineval;
@ -1935,14 +1947,6 @@ CopyFromErrorCallback(void *arg)
} }
else 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", errcontext("COPY %s, line %d",
cstate->cur_relname, cstate->cur_lineno); cstate->cur_relname, cstate->cur_lineno);
} }
@ -2012,6 +2016,7 @@ CopyFrom(CopyState cstate)
#define MAX_BUFFERED_TUPLES 1000 #define MAX_BUFFERED_TUPLES 1000
HeapTuple *bufferedTuples = NULL; /* initialize to silence warning */ HeapTuple *bufferedTuples = NULL; /* initialize to silence warning */
Size bufferedTuplesSize = 0; Size bufferedTuplesSize = 0;
int firstBufferedLineNo = 0;
Assert(cstate->rel); Assert(cstate->rel);
@ -2243,6 +2248,8 @@ CopyFrom(CopyState cstate)
if (useHeapMultiInsert) if (useHeapMultiInsert)
{ {
/* Add this tuple to the tuple buffer */ /* Add this tuple to the tuple buffer */
if (nBufferedTuples == 0)
firstBufferedLineNo = cstate->cur_lineno;
bufferedTuples[nBufferedTuples++] = tuple; bufferedTuples[nBufferedTuples++] = tuple;
bufferedTuplesSize += tuple->t_len; bufferedTuplesSize += tuple->t_len;
@ -2257,7 +2264,8 @@ CopyFrom(CopyState cstate)
{ {
CopyFromInsertBatch(cstate, estate, mycid, hi_options, CopyFromInsertBatch(cstate, estate, mycid, hi_options,
resultRelInfo, myslot, bistate, resultRelInfo, myslot, bistate,
nBufferedTuples, bufferedTuples); nBufferedTuples, bufferedTuples,
firstBufferedLineNo);
nBufferedTuples = 0; nBufferedTuples = 0;
bufferedTuplesSize = 0; bufferedTuplesSize = 0;
} }
@ -2293,7 +2301,8 @@ CopyFrom(CopyState cstate)
if (nBufferedTuples > 0) if (nBufferedTuples > 0)
CopyFromInsertBatch(cstate, estate, mycid, hi_options, CopyFromInsertBatch(cstate, estate, mycid, hi_options,
resultRelInfo, myslot, bistate, resultRelInfo, myslot, bistate,
nBufferedTuples, bufferedTuples); nBufferedTuples, bufferedTuples,
firstBufferedLineNo);
/* Done, clean up */ /* Done, clean up */
error_context_stack = errcallback.previous; error_context_stack = errcallback.previous;
@ -2336,10 +2345,19 @@ static void
CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid, CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
int hi_options, ResultRelInfo *resultRelInfo, int hi_options, ResultRelInfo *resultRelInfo,
TupleTableSlot *myslot, BulkInsertState bistate, TupleTableSlot *myslot, BulkInsertState bistate,
int nBufferedTuples, HeapTuple *bufferedTuples) int nBufferedTuples, HeapTuple *bufferedTuples,
int firstBufferedLineNo)
{ {
MemoryContext oldcontext; MemoryContext oldcontext;
int i; 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 * heap_multi_insert leaks memory, so switch to short-lived memory context
@ -2364,6 +2382,7 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
{ {
List *recheckIndexes; List *recheckIndexes;
cstate->cur_lineno = firstBufferedLineNo + i;
ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false); ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
recheckIndexes = recheckIndexes =
ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self), ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self),
@ -2383,10 +2402,16 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
resultRelInfo->ri_TrigDesc->trig_insert_after_row) resultRelInfo->ri_TrigDesc->trig_insert_after_row)
{ {
for (i = 0; i < nBufferedTuples; i++) for (i = 0; i < nBufferedTuples; i++)
{
cstate->cur_lineno = firstBufferedLineNo + i;
ExecARInsertTriggers(estate, resultRelInfo, ExecARInsertTriggers(estate, resultRelInfo,
bufferedTuples[i], bufferedTuples[i],
NIL); NIL);
} }
}
/* reset cur_lineno to where we were */
cstate->cur_lineno = save_cur_lineno;
} }
/* /*
@ -2915,6 +2940,7 @@ CopyReadLine(CopyState cstate)
bool result; bool result;
resetStringInfo(&cstate->line_buf); resetStringInfo(&cstate->line_buf);
cstate->line_buf_valid = true;
/* Mark that encoding conversion hasn't occurred yet */ /* Mark that encoding conversion hasn't occurred yet */
cstate->line_buf_converted = false; cstate->line_buf_converted = false;