/* low-level state data */
CopyDest copy_dest; /* type of copy source/destination */
FILE *copy_file; /* used if copy_dest == COPY_FILE */
StringInfo fe_msgbuf; /* used for all dests during COPY TO */
int file_encoding; /* file or remote side's character encoding */ bool need_transcoding; /* file encoding diff from server? */ bool encoding_embeds_ascii; /* ASCII can be non-first byte? */
/* parameters from the COPY command */
Relation rel; /* relation to copy to */
QueryDesc *queryDesc; /* executable query to copy from */
List *attnumlist; /* integer list of attnums to copy */ char *filename; /* filename, or NULL for STDOUT */ bool is_program; /* is 'filename' a program to popen? */
copy_data_dest_cb data_dest_cb; /* function for writing data */
CopyFormatOptions opts;
Node *whereClause; /* WHERE condition (or NULL) */
FmgrInfo *out_functions; /* lookup info for output functions */
MemoryContext rowcontext; /* per-row evaluation context */
uint64 bytes_processed; /* number of bytes processed so far */
} CopyToStateData;
/* DestReceiver for COPY (query) TO */ typedefstruct
{
DestReceiver pub; /* publicly-known function pointers */
CopyToState cstate; /* CopyToStateData for the command */
uint64 processed; /* # of tuples processed */
} DR_copy;
/* NOTE: there's a copy of this in copyfromparse.c */ staticconstchar BinarySignature[11] = "PGCOPY\n\377\r\n\0";
/* Return a COPY TO routine for the given options */ staticconst CopyToRoutine *
CopyToGetRoutine(const CopyFormatOptions *opts)
{ if (opts->csv_mode) return &CopyToRoutineCSV; elseif (opts->binary) return &CopyToRoutineBinary;
/* default is text */ return &CopyToRoutineText;
}
/* Implementation of the start callback for text and CSV formats */ staticvoid
CopyToTextLikeStart(CopyToState cstate, TupleDesc tupDesc)
{ /* *Fornon-binarycopy,weneedtoconvertnull_printtofileencoding, *becauseitwillbesentdirectlywithCopySendString.
*/ if (cstate->need_transcoding)
cstate->opts.null_print_client = pg_server_to_any(cstate->opts.null_print,
cstate->opts.null_print_len,
cstate->file_encoding);
/* if a header has been requested send the line */ if (cstate->opts.header_line)
{
ListCell *cur; bool hdr_delim = false;
foreach(cur, cstate->attnumlist)
{ int attnum = lfirst_int(cur); char *colname;
if (hdr_delim)
CopySendChar(cstate, cstate->opts.delim[0]);
hdr_delim = true;
if (cstate->opts.csv_mode)
CopyAttributeOutCSV(cstate, colname, false); else
CopyAttributeOutText(cstate, colname);
}
CopySendTextLikeEndOfRow(cstate);
}
}
/* *ImplementationoftheoutfunccallbackfortextandCSVformats.Assign *theoutputfunctiondatatothegiven*finfo.
*/ staticvoid
CopyToTextLikeOutFunc(CopyToState cstate, Oid atttypid, FmgrInfo *finfo)
{
Oid func_oid; bool is_varlena;
/* Set output function for an attribute */
getTypeOutputInfo(atttypid, &func_oid, &is_varlena);
fmgr_info(func_oid, finfo);
}
/* Implementation of the per-row callback for text format */ staticvoid
CopyToTextOneRow(CopyToState cstate, TupleTableSlot *slot)
{
CopyToTextLikeOneRow(cstate, slot, false);
}
/* Implementation of the per-row callback for CSV format */ staticvoid
CopyToCSVOneRow(CopyToState cstate, TupleTableSlot *slot)
{
CopyToTextLikeOneRow(cstate, slot, true);
}
/* *Implementationoftheoutfunccallbackforbinaryformat.Assign *thebinaryoutputfunctiontothegiven*finfo.
*/ staticvoid
CopyToBinaryOutFunc(CopyToState cstate, Oid atttypid, FmgrInfo *finfo)
{
Oid func_oid; bool is_varlena;
/* Set output function for an attribute */
getTypeBinaryOutputInfo(atttypid, &func_oid, &is_varlena);
fmgr_info(func_oid, finfo);
}
/* Implementation of the per-row callback for binary format */ staticvoid
CopyToBinaryOneRow(CopyToState cstate, TupleTableSlot *slot)
{
FmgrInfo *out_functions = cstate->out_functions;
/* Implementation of the end callback for binary format */ staticvoid
CopyToBinaryEnd(CopyToState cstate)
{ /* Generate trailer for a binary copy */
CopySendInt16(cstate, -1); /* Need to flush out the trailer */
CopySendEndOfRow(cstate);
}
/* *Sendcopystart/stopmessagesforfrontendcopies.Thesehavechanged *inpastprotocolredesigns.
*/ staticvoid
SendCopyBegin(CopyToState cstate)
{
StringInfoData buf; int natts = list_length(cstate->attnumlist);
int16 format = (cstate->opts.binary ? 1 : 0); int i;
pq_beginmessage(&buf, PqMsg_CopyOutResponse);
pq_sendbyte(&buf, format); /* overall format */
pq_sendint16(&buf, natts); for (i = 0; i < natts; i++)
pq_sendint16(&buf, format); /* per-column formats */
pq_endmessage(&buf);
cstate->copy_dest = COPY_FRONTEND;
}
staticvoid
SendCopyEnd(CopyToState cstate)
{ /* Shouldn't have any unsent data */
Assert(cstate->fe_msgbuf->len == 0); /* Send Copy Done message */
pq_putemptymessage(PqMsg_CopyDone);
}
/* check that we got back something we can work with */ if (rewritten == NIL)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("DO INSTEAD NOTHING rules are not supported for COPY")));
} elseif (list_length(rewritten) > 1)
{
ListCell *lc;
/* examine queries to determine which error message to issue */
foreach(lc, rewritten)
{
Query *q = lfirst_node(Query, lc);
if (q->querySource == QSRC_QUAL_INSTEAD_RULE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("conditional DO INSTEAD rules are not supported for COPY"))); if (q->querySource == QSRC_NON_INSTEAD_RULE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("DO ALSO rules are not supported for COPY")));
}
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("multi-statement DO INSTEAD rules are not supported for COPY")));
}
query = linitial_node(Query, rewritten);
/* The grammar allows SELECT INTO, but we don't support that */ if (query->utilityStmt != NULL &&
IsA(query->utilityStmt, CreateTableAsStmt))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("COPY (SELECT INTO) is not supported")));
/* The only other utility command we could see is NOTIFY */ if (query->utilityStmt != NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("COPY query must not be a utility command")));
if (!list_member_int(cstate->attnumlist, attnum))
ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE), /*- translator: %s is the name of a COPY option, e.g. FORCE_NOT_NULL */
errmsg("%s column \"%s\" not referenced by COPY", "FORCE_QUOTE", NameStr(attr->attname))));
cstate->opts.force_quote_flags[attnum - 1] = true;
}
}
/* Use client encoding when ENCODING option is not specified. */ if (cstate->opts.file_encoding < 0)
cstate->file_encoding = pg_get_client_encoding(); else
cstate->file_encoding = cstate->opts.file_encoding;
Assert(!is_program); /* the grammar does not allow this */ if (whereToSendOutput != DestRemote)
cstate->copy_file = stdout;
} else
{
cstate->filename = pstrdup(filename);
cstate->is_program = is_program;
if (is_program)
{
progress_vals[1] = PROGRESS_COPY_TYPE_PROGRAM;
cstate->copy_file = OpenPipeStream(cstate->filename, PG_BINARY_W); if (cstate->copy_file == NULL)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not execute command \"%s\": %m",
cstate->filename)));
} else
{
mode_t oumask; /* Pre-existing umask value */ struct stat st;
progress_vals[1] = PROGRESS_COPY_TYPE_FILE;
/* *Preventwritetorelativepath...tooeasytoshootoneselfin *thefootbyoverwritingadatabasefile...
*/ if (!is_absolute_path(filename))
ereport(ERROR,
(errcode(ERRCODE_INVALID_NAME),
errmsg("relative path not allowed for COPY to file")));
oumask = umask(S_IWGRP | S_IWOTH);
PG_TRY();
{
cstate->copy_file = AllocateFile(cstate->filename, PG_BINARY_W);
}
PG_FINALLY();
{
umask(oumask);
}
PG_END_TRY(); if (cstate->copy_file == NULL)
{ /* copy errno because ereport subfunctions might change it */ int save_errno = errno;
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not open file \"%s\" for writing: %m",
cstate->filename),
(save_errno == ENOENT || save_errno == EACCES) ?
errhint("COPY TO instructs the PostgreSQL server process to write a file. " "You may want a client-side facility such as psql's \\copy.") : 0));
}
if (fstat(fileno(cstate->copy_file), &st))
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not stat file \"%s\": %m",
cstate->filename)));
if (S_ISDIR(st.st_mode))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is a directory", cstate->filename)));
}
}
/* We use fe_msgbuf as a per-row buffer regardless of copy_dest */
cstate->fe_msgbuf = makeStringInfo();
/* Get info about the columns we need to process. */
cstate->out_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo));
foreach(cur, cstate->attnumlist)
{ int attnum = lfirst_int(cur);
Form_pg_attribute attr = TupleDescAttr(tupDesc, attnum - 1);
ExecDropSingleTupleTableSlot(slot);
table_endscan(scandesc);
} else
{ /* run the plan --- the dest receiver will send tuples */
ExecutorRun(cstate->queryDesc, ForwardScanDirection, 0);
processed = ((DR_copy *) cstate->queryDesc->dest)->processed;
}
/* *Wehavetogrovelthroughthestringsearchingforcontrolcharacters *andinstancesofthedelimitercharacter.Inmostcases,though,these *areinfrequent.ToavoidoverheadfromcallingCopySendDataonceper *character,wedumpoutallcharactersbetweenescapedcharactersina *singlecall.Theloopinvariantisthatthedatafrom"start"to"ptr" *canbesentliterally,buthasn'tyetbeen. * *Wecanskippg_encoding_mblen()overheadwhenencodingissafe,because *invalidbackendencodings,extrabytesofamultibytecharacternever *looklikeASCII.Thisloopissufficientlyperformance-criticalthat *it'sworthmakingtwocopiesofittogettheIS_HIGHBIT_SET()testout *ofthenormalsafe-encodingpath.
*/ if (cstate->encoding_embeds_ascii)
{
start = ptr; while ((c = *ptr) != '\0')
{ if ((unsignedchar) c < (unsignedchar) 0x20)
{ /* *\rand\nmustbeescaped,theothersaretraditional.We *prefertodumptheseusingtheC-likenotation,ratherthan *abackslashandtheliteralcharacter,becauseitmakesthe *dumpfileabitmoreproofagainstMicrosoftishdata *mangling.
*/ switch (c)
{ case'\b':
c = 'b'; break; case'\f':
c = 'f'; break; case'\n':
c = 'n'; break; case'\r':
c = 'r'; break; case'\t':
c = 't'; break; case'\v':
c = 'v'; break; default: /* If it's the delimiter, must backslash it */ if (c == delimc) break; /* All ASCII control chars are length 1 */
ptr++; continue; /* fall to end of loop */
} /* if we get here, we need to convert the control char */
DUMPSOFAR();
CopySendChar(cstate, '\\');
CopySendChar(cstate, c);
start = ++ptr; /* do not include char in next run */
} elseif (c == '\\' || c == delimc)
{
DUMPSOFAR();
CopySendChar(cstate, '\\');
start = ptr++; /* we include char in next run */
} elseif (IS_HIGHBIT_SET(c))
ptr += pg_encoding_mblen(cstate->file_encoding, ptr); else
ptr++;
}
} else
{
start = ptr; while ((c = *ptr) != '\0')
{ if ((unsignedchar) c < (unsignedchar) 0x20)
{ /* *\rand\nmustbeescaped,theothersaretraditional.We *prefertodumptheseusingtheC-likenotation,ratherthan *abackslashandtheliteralcharacter,becauseitmakesthe *dumpfileabitmoreproofagainstMicrosoftishdata *mangling.
*/ switch (c)
{ case'\b':
c = 'b'; break; case'\f':
c = 'f'; break; case'\n':
c = 'n'; break; case'\r':
c = 'r'; break; case'\t':
c = 't'; break; case'\v':
c = 'v'; break; default: /* If it's the delimiter, must backslash it */ if (c == delimc) break; /* All ASCII control chars are length 1 */
ptr++; continue; /* fall to end of loop */
} /* if we get here, we need to convert the control char */
DUMPSOFAR();
CopySendChar(cstate, '\\');
CopySendChar(cstate, c);
start = ++ptr; /* do not include char in next run */
} elseif (c == '\\' || c == delimc)
{
DUMPSOFAR();
CopySendChar(cstate, '\\');
start = ptr++; /* we include char in next run */
} else
ptr++;
}
}
/* Increment the number of processed tuples, and report the progress */
pgstat_progress_update_param(PROGRESS_COPY_TUPLES_PROCESSED,
++myState->processed);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.