diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 242973f20f656eee0cf19e04a64a8b5023a0d644..45749ab673ab65f6ba789eb4fc73609e3c795a11 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -1,13 +1,15 @@ /*------------------------------------------------------------------------- * * copy.c + * COPY command. + * * * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.183 2002/11/26 03:01:57 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.184 2002/12/01 18:14:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -532,6 +534,8 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, char *string; Snapshot mySnapshot; List *cur; + MemoryContext oldcontext; + MemoryContext mycontext; tupDesc = rel->rd_att; attr = tupDesc->attrs; @@ -561,6 +565,18 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, elog(ERROR, "COPY BINARY: cstring not supported"); } + /* + * Create a temporary memory context that we can reset once per row + * to recover palloc'd memory. This avoids any problems with leaks + * inside datatype output routines, and should be faster than retail + * pfree's anyway. (We don't need a whole econtext as CopyFrom does.) + */ + mycontext = AllocSetContextCreate(CurrentMemoryContext, + "COPY TO", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + if (binary) { /* Generate header for a binary copy */ @@ -591,6 +607,9 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, CHECK_FOR_INTERRUPTS(); + MemoryContextReset(mycontext); + oldcontext = MemoryContextSwitchTo(mycontext); + if (binary) { /* Binary per-tuple header */ @@ -615,7 +634,6 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, string = DatumGetCString(DirectFunctionCall1(oidout, ObjectIdGetDatum(HeapTupleGetOid(tuple)))); CopySendString(string, fp); - pfree(string); need_delim = true; } } @@ -623,11 +641,10 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, foreach(cur, attnumlist) { int attnum = lfirsti(cur); - Datum origvalue, - value; + Datum value; bool isnull; - origvalue = heap_getattr(tuple, attnum, tupDesc, &isnull); + value = heap_getattr(tuple, attnum, tupDesc, &isnull); if (!binary) { @@ -650,17 +667,6 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, } else { - /* - * If we have a toasted datum, forcibly detoast it to - * avoid memory leakage inside the type's output routine - * (or for binary case, becase we must output untoasted - * value). - */ - if (isvarlena[attnum - 1]) - value = PointerGetDatum(PG_DETOAST_DATUM(origvalue)); - else - value = origvalue; - if (!binary) { string = DatumGetCString(FunctionCall3(&out_functions[attnum - 1], @@ -668,7 +674,6 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, ObjectIdGetDatum(elements[attnum - 1]), Int32GetDatum(attr[attnum - 1]->atttypmod))); CopyAttributeOut(fp, string, delim); - pfree(string); } else { @@ -678,6 +683,10 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, { /* varlena */ Assert(fld_size == -1); + + /* If we have a toasted datum, detoast it */ + value = PointerGetDatum(PG_DETOAST_DATUM(value)); + CopySendData(DatumGetPointer(value), VARSIZE(value), fp); @@ -706,15 +715,13 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, fp); } } - - /* Clean up detoasted copy, if any */ - if (value != origvalue) - pfree(DatumGetPointer(value)); } } if (!binary) CopySendChar('\n', fp); + + MemoryContextSwitchTo(oldcontext); } heap_endscan(scandesc); @@ -727,6 +734,8 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, CopySendData(&fld_count, sizeof(int16), fp); } + MemoryContextDelete(mycontext); + pfree(out_functions); pfree(elements); pfree(isvarlena); @@ -1235,13 +1244,13 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, */ copy_lineno = 0; + MemoryContextSwitchTo(oldcontext); + /* * Execute AFTER STATEMENT insertion triggers */ ExecASInsertTriggers(estate, resultRelInfo); - MemoryContextSwitchTo(oldcontext); - pfree(values); pfree(nulls); @@ -1463,7 +1472,6 @@ copy_eof: attribute_buf.len = 0; attribute_buf.data[0] = '\0'; appendBinaryStringInfo(&attribute_buf, cvt, strlen(cvt)); - pfree(cvt); } } @@ -1476,24 +1484,16 @@ CopyAttributeOut(FILE *fp, char *server_string, char *delim) char *string; char c; char delimc = delim[0]; - bool same_encoding; - char *string_start; int mblen; int i; same_encoding = (server_encoding == client_encoding); if (!same_encoding) - { string = (char *) pg_server_to_client((unsigned char *) server_string, strlen(server_string)); - string_start = string; - } else - { string = server_string; - string_start = NULL; - } for (; (c = *string) != '\0'; string += mblen) { @@ -1527,7 +1527,11 @@ CopyAttributeOut(FILE *fp, char *server_string, char *delim) CopySendChar('\\', fp); CopySendChar(c, fp); - /* XXX shouldn't this be done even when encoding is same? */ + /* + * We can skip pg_encoding_mblen() overhead when encoding + * is same, because in valid backend encodings, extra + * bytes of a multibyte character never look like ASCII. + */ if (!same_encoding) { /* send additional bytes of the char, if any */ @@ -1538,9 +1542,6 @@ CopyAttributeOut(FILE *fp, char *server_string, char *delim) break; } } - - if (string_start) - pfree(string_start); /* pfree pg_server_to_client result */ } /*