#include "nodes/execnodes.h"
#include "nodes/parsenodes.h"
#include "tcop/dest.h"
Go to the source code of this file.
Typedefs | |
typedef struct CopyStateData * | CopyState |
Functions | |
Oid | DoCopy (const CopyStmt *stmt, const char *queryString, uint64 *processed) |
void | ProcessCopyOptions (CopyState cstate, bool is_from, List *options) |
CopyState | BeginCopyFrom (Relation rel, const char *filename, bool is_program, List *attnamelist, List *options) |
void | EndCopyFrom (CopyState cstate) |
bool | NextCopyFrom (CopyState cstate, ExprContext *econtext, Datum *values, bool *nulls, Oid *tupleOid) |
bool | NextCopyFromRawFields (CopyState cstate, char ***fields, int *nfields) |
void | CopyFromErrorCallback (void *arg) |
DestReceiver * | CreateCopyDestReceiver (void) |
typedef struct CopyStateData* CopyState |
CopyState BeginCopyFrom | ( | Relation | rel, | |
const char * | filename, | |||
bool | is_program, | |||
List * | attnamelist, | |||
List * | options | |||
) |
Definition at line 2403 of file copy.c.
References AllocateFile(), Assert, CopyStateData::attnumlist, CopyStateData::attribute_buf, tupleDesc::attrs, BeginCopy(), CopyStateData::binary, BinarySignature, build_column_default(), contain_volatile_functions(), CopyStateData::copy_file, CopyStateData::copycontext, CopyGetData(), CopyGetInt32(), CopyStateData::cur_attname, CopyStateData::cur_attval, CopyStateData::cur_lineno, CopyStateData::cur_relname, CopyStateData::defexprs, CopyStateData::defmap, DestRemote, CopyStateData::eol_type, ereport, errcode(), errcode_for_file_access(), errmsg(), ERROR, ExecInitExpr(), expression_planner(), CopyStateData::fe_eof, CopyStateData::file_has_oids, CopyStateData::filename, fmgr_info(), getTypeBinaryInputInfo(), getTypeInputInfo(), CopyStateData::in_functions, initStringInfo(), CopyStateData::is_program, CopyStateData::line_buf, CopyStateData::line_buf_converted, list_length(), list_member_int(), CopyStateData::max_fields, memcmp(), MemoryContextSwitchTo(), tupleDesc::natts, NULL, CopyStateData::num_defaults, CopyStateData::oid_in_function, CopyStateData::oid_typioparam, OIDOID, CopyStateData::oids, OpenPipeStream(), palloc(), PG_BINARY_R, pstrdup(), CopyStateData::raw_buf, CopyStateData::raw_buf_index, CopyStateData::raw_buf_len, RAW_BUF_SIZE, CopyStateData::raw_fields, ReceiveCopyBegin(), CopyStateData::rel, RelationGetDescr, RelationGetRelationName, CopyStateData::typioparams, CopyStateData::volatile_defexprs, and whereToSendOutput.
Referenced by DoCopy(), file_acquire_sample_rows(), fileBeginForeignScan(), and fileReScanForeignScan().
{ CopyState cstate; bool pipe = (filename == NULL); TupleDesc tupDesc; Form_pg_attribute *attr; AttrNumber num_phys_attrs, num_defaults; FmgrInfo *in_functions; Oid *typioparams; int attnum; Oid in_func_oid; int *defmap; ExprState **defexprs; MemoryContext oldcontext; bool volatile_defexprs; cstate = BeginCopy(true, rel, NULL, NULL, attnamelist, options); oldcontext = MemoryContextSwitchTo(cstate->copycontext); /* Initialize state variables */ cstate->fe_eof = false; cstate->eol_type = EOL_UNKNOWN; cstate->cur_relname = RelationGetRelationName(cstate->rel); cstate->cur_lineno = 0; cstate->cur_attname = NULL; cstate->cur_attval = NULL; /* Set up variables to avoid per-attribute overhead. */ initStringInfo(&cstate->attribute_buf); initStringInfo(&cstate->line_buf); cstate->line_buf_converted = false; cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1); cstate->raw_buf_index = cstate->raw_buf_len = 0; tupDesc = RelationGetDescr(cstate->rel); attr = tupDesc->attrs; num_phys_attrs = tupDesc->natts; num_defaults = 0; volatile_defexprs = false; /* * Pick up the required catalog information for each attribute in the * relation, including the input function, the element type (to pass to * the input function), and info about defaults and constraints. (Which * input function we use depends on text/binary format choice.) */ in_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo)); typioparams = (Oid *) palloc(num_phys_attrs * sizeof(Oid)); defmap = (int *) palloc(num_phys_attrs * sizeof(int)); defexprs = (ExprState **) palloc(num_phys_attrs * sizeof(ExprState *)); for (attnum = 1; attnum <= num_phys_attrs; attnum++) { /* We don't need info for dropped attributes */ if (attr[attnum - 1]->attisdropped) continue; /* Fetch the input function and typioparam info */ if (cstate->binary) getTypeBinaryInputInfo(attr[attnum - 1]->atttypid, &in_func_oid, &typioparams[attnum - 1]); else getTypeInputInfo(attr[attnum - 1]->atttypid, &in_func_oid, &typioparams[attnum - 1]); fmgr_info(in_func_oid, &in_functions[attnum - 1]); /* Get default info if needed */ if (!list_member_int(cstate->attnumlist, attnum)) { /* attribute is NOT to be copied from input */ /* use default value if one exists */ Node *defexpr = build_column_default(cstate->rel, attnum); if (defexpr != NULL) { /* Initialize expressions in copycontext. */ defexprs[num_defaults] = ExecInitExpr( expression_planner((Expr *) defexpr), NULL); defmap[num_defaults] = attnum - 1; num_defaults++; if (!volatile_defexprs) volatile_defexprs = contain_volatile_functions(defexpr); } } } /* We keep those variables in cstate. */ cstate->in_functions = in_functions; cstate->typioparams = typioparams; cstate->defmap = defmap; cstate->defexprs = defexprs; cstate->volatile_defexprs = volatile_defexprs; cstate->num_defaults = num_defaults; cstate->is_program = is_program; if (pipe) { Assert(!is_program); /* the grammar does not allow this */ if (whereToSendOutput == DestRemote) ReceiveCopyBegin(cstate); else cstate->copy_file = stdin; } else { cstate->filename = pstrdup(filename); if (cstate->is_program) { cstate->copy_file = OpenPipeStream(cstate->filename, PG_BINARY_R); if (cstate->copy_file == NULL) ereport(ERROR, (errmsg("could not execute command \"%s\": %m", cstate->filename))); } else { struct stat st; cstate->copy_file = AllocateFile(cstate->filename, PG_BINARY_R); if (cstate->copy_file == NULL) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open file \"%s\" for reading: %m", cstate->filename))); fstat(fileno(cstate->copy_file), &st); if (S_ISDIR(st.st_mode)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is a directory", cstate->filename))); } } if (!cstate->binary) { /* must rely on user to tell us... */ cstate->file_has_oids = cstate->oids; } else { /* Read and verify binary header */ char readSig[11]; int32 tmp; /* Signature */ if (CopyGetData(cstate, readSig, 11, 11) != 11 || memcmp(readSig, BinarySignature, 11) != 0) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("COPY file signature not recognized"))); /* Flags field */ if (!CopyGetInt32(cstate, &tmp)) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("invalid COPY file header (missing flags)"))); cstate->file_has_oids = (tmp & (1 << 16)) != 0; tmp &= ~(1 << 16); if ((tmp >> 16) != 0) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("unrecognized critical flags in COPY file header"))); /* Header extension length */ if (!CopyGetInt32(cstate, &tmp) || tmp < 0) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("invalid COPY file header (missing length)"))); /* Skip extension header, if present */ while (tmp-- > 0) { if (CopyGetData(cstate, readSig, 1, 1) != 1) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("invalid COPY file header (wrong length)"))); } } if (cstate->file_has_oids && cstate->binary) { getTypeBinaryInputInfo(OIDOID, &in_func_oid, &cstate->oid_typioparam); fmgr_info(in_func_oid, &cstate->oid_in_function); } /* create workspace for CopyReadAttributes results */ if (!cstate->binary) { AttrNumber attr_count = list_length(cstate->attnumlist); int nfields = cstate->file_has_oids ? (attr_count + 1) : attr_count; cstate->max_fields = nfields; cstate->raw_fields = (char **) palloc(nfields * sizeof(char *)); } MemoryContextSwitchTo(oldcontext); return cstate; }
void CopyFromErrorCallback | ( | void * | arg | ) |
Definition at line 1889 of file copy.c.
References CopyStateData::binary, CopyStateData::cur_attname, CopyStateData::cur_attval, CopyStateData::cur_lineno, CopyStateData::cur_relname, StringInfoData::data, errcontext, limit_printout_length(), CopyStateData::line_buf, CopyStateData::line_buf_converted, CopyStateData::need_transcoding, and pfree().
Referenced by fileIterateForeignScan().
{ CopyState cstate = (CopyState) arg; if (cstate->binary) { /* can't usefully display the data */ if (cstate->cur_attname) errcontext("COPY %s, line %d, column %s", cstate->cur_relname, cstate->cur_lineno, cstate->cur_attname); else errcontext("COPY %s, line %d", cstate->cur_relname, cstate->cur_lineno); } else { if (cstate->cur_attname && cstate->cur_attval) { /* error is relevant to a particular column */ char *attval; attval = limit_printout_length(cstate->cur_attval); errcontext("COPY %s, line %d, column %s: \"%s\"", cstate->cur_relname, cstate->cur_lineno, cstate->cur_attname, attval); pfree(attval); } else if (cstate->cur_attname) { /* error is relevant to a particular column, value is NULL */ errcontext("COPY %s, line %d, column %s: null input", cstate->cur_relname, cstate->cur_lineno, cstate->cur_attname); } else { /* error is relevant to a particular line */ if (cstate->line_buf_converted || !cstate->need_transcoding) { char *lineval; lineval = limit_printout_length(cstate->line_buf.data); errcontext("COPY %s, line %d: \"%s\"", cstate->cur_relname, cstate->cur_lineno, lineval); pfree(lineval); } 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); } } } }
DestReceiver* CreateCopyDestReceiver | ( | void | ) |
Definition at line 4204 of file copy.c.
References palloc().
Referenced by CreateDestReceiver().
{ DR_copy *self = (DR_copy *) palloc(sizeof(DR_copy)); self->pub.receiveSlot = copy_dest_receive; self->pub.rStartup = copy_dest_startup; self->pub.rShutdown = copy_dest_shutdown; self->pub.rDestroy = copy_dest_destroy; self->pub.mydest = DestCopyOut; self->cstate = NULL; /* will be set later */ self->processed = 0; return (DestReceiver *) self; }
Definition at line 776 of file copy.c.
References AccessShareLock, ACL_INSERT, Assert, CopyStmt::attlist, BeginCopyFrom(), BeginCopyTo(), bms_add_member(), CopyFrom(), CopyGetAttnums(), cur, DoCopyTo(), EndCopyFrom(), EndCopyTo(), ereport, errcode(), errhint(), errmsg(), ERROR, ExecCheckRTPerms(), CopyStmt::filename, heap_close, heap_openrv(), CopyStmt::is_from, CopyStmt::is_program, lfirst_int, list_make1, makeNode, RangeTblEntry::modifiedCols, NoLock, NULL, CopyStmt::options, PreventCommandIfReadOnly(), CopyStmt::query, RelationData::rd_islocaltemp, RelationData::rd_rel, CopyStmt::relation, RelationGetDescr, RelationGetRelid, RangeTblEntry::relid, RangeTblEntry::relkind, RangeTblEntry::requiredPerms, RowExclusiveLock, RangeTblEntry::rtekind, RangeTblEntry::selectedCols, superuser(), and XactReadOnly.
Referenced by standard_ProcessUtility().
{ CopyState cstate; bool is_from = stmt->is_from; bool pipe = (stmt->filename == NULL); Relation rel; Oid relid; /* Disallow COPY to/from file or program except to superusers. */ if (!pipe && !superuser()) { if (stmt->is_program) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to COPY to or from an external program"), errhint("Anyone can COPY to stdout or from stdin. " "psql's \\copy command also works for anyone."))); else ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to COPY to or from a file"), errhint("Anyone can COPY to stdout or from stdin. " "psql's \\copy command also works for anyone."))); } if (stmt->relation) { TupleDesc tupDesc; AclMode required_access = (is_from ? ACL_INSERT : ACL_SELECT); RangeTblEntry *rte; List *attnums; ListCell *cur; Assert(!stmt->query); /* Open and lock the relation, using the appropriate lock type. */ rel = heap_openrv(stmt->relation, (is_from ? RowExclusiveLock : AccessShareLock)); relid = RelationGetRelid(rel); rte = makeNode(RangeTblEntry); rte->rtekind = RTE_RELATION; rte->relid = RelationGetRelid(rel); rte->relkind = rel->rd_rel->relkind; rte->requiredPerms = required_access; tupDesc = RelationGetDescr(rel); attnums = CopyGetAttnums(tupDesc, rel, stmt->attlist); foreach(cur, attnums) { int attno = lfirst_int(cur) - FirstLowInvalidHeapAttributeNumber; if (is_from) rte->modifiedCols = bms_add_member(rte->modifiedCols, attno); else rte->selectedCols = bms_add_member(rte->selectedCols, attno); } ExecCheckRTPerms(list_make1(rte), true); } else { Assert(stmt->query); relid = InvalidOid; rel = NULL; } if (is_from) { Assert(rel); /* check read-only transaction */ if (XactReadOnly && !rel->rd_islocaltemp) PreventCommandIfReadOnly("COPY FROM"); cstate = BeginCopyFrom(rel, stmt->filename, stmt->is_program, stmt->attlist, stmt->options); *processed = CopyFrom(cstate); /* copy from file to database */ EndCopyFrom(cstate); } else { cstate = BeginCopyTo(rel, stmt->query, queryString, stmt->filename, stmt->is_program, stmt->attlist, stmt->options); *processed = DoCopyTo(cstate); /* copy from database to file */ EndCopyTo(cstate); } /* * Close the relation. If reading, we can release the AccessShareLock we * got; if writing, we should hold the lock until end of transaction to * ensure that updates will be committed before lock is released. */ if (rel != NULL) heap_close(rel, (is_from ? NoLock : AccessShareLock)); return relid; }
void EndCopyFrom | ( | CopyState | cstate | ) |
Definition at line 2897 of file copy.c.
References EndCopy().
Referenced by DoCopy(), file_acquire_sample_rows(), fileEndForeignScan(), and fileReScanForeignScan().
{ /* No COPY FROM related resources except memory. */ EndCopy(cstate); }
bool NextCopyFrom | ( | CopyState | cstate, | |
ExprContext * | econtext, | |||
Datum * | values, | |||
bool * | nulls, | |||
Oid * | tupleOid | |||
) |
Definition at line 2673 of file copy.c.
References Assert, CopyStateData::attnumlist, tupleDesc::attrs, CopyStateData::binary, CopyStateData::convert_select_flags, CopyStateData::copy_dest, COPY_OLD_FE, CopyGetData(), CopyGetInt16(), CopyReadBinaryAttribute(), CStringGetDatum, CopyStateData::csv_mode, cur, CopyStateData::cur_attname, CopyStateData::cur_attval, CopyStateData::cur_lineno, CurrentMemoryContext, DatumGetObjectId, CopyStateData::defexprs, CopyStateData::defmap, DirectFunctionCall1, ExprContext::ecxt_per_tuple_memory, ereport, errcode(), errmsg(), ERROR, ExecEvalExpr, CopyStateData::file_has_oids, CopyStateData::force_notnull_flags, i, CopyStateData::in_functions, InputFunctionCall(), InvalidOid, lfirst_int, list_length(), MemSet, NameStr, tupleDesc::natts, NextCopyFromRawFields(), NULL, CopyStateData::null_print, CopyStateData::num_defaults, CopyStateData::oid_in_function, CopyStateData::oid_typioparam, oidin(), CopyStateData::oids, CopyStateData::rel, RelationGetDescr, and CopyStateData::typioparams.
Referenced by CopyFrom(), file_acquire_sample_rows(), and fileIterateForeignScan().
{ TupleDesc tupDesc; Form_pg_attribute *attr; AttrNumber num_phys_attrs, attr_count, num_defaults = cstate->num_defaults; FmgrInfo *in_functions = cstate->in_functions; Oid *typioparams = cstate->typioparams; int i; int nfields; bool isnull; bool file_has_oids = cstate->file_has_oids; int *defmap = cstate->defmap; ExprState **defexprs = cstate->defexprs; tupDesc = RelationGetDescr(cstate->rel); attr = tupDesc->attrs; num_phys_attrs = tupDesc->natts; attr_count = list_length(cstate->attnumlist); nfields = file_has_oids ? (attr_count + 1) : attr_count; /* Initialize all values for row to NULL */ MemSet(values, 0, num_phys_attrs * sizeof(Datum)); MemSet(nulls, true, num_phys_attrs * sizeof(bool)); if (!cstate->binary) { char **field_strings; ListCell *cur; int fldct; int fieldno; char *string; /* read raw fields in the next line */ if (!NextCopyFromRawFields(cstate, &field_strings, &fldct)) return false; /* check for overflowing fields */ if (nfields > 0 && fldct > nfields) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("extra data after last expected column"))); fieldno = 0; /* Read the OID field if present */ if (file_has_oids) { if (fieldno >= fldct) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("missing data for OID column"))); string = field_strings[fieldno++]; if (string == NULL) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("null OID in COPY data"))); else if (cstate->oids && tupleOid != NULL) { cstate->cur_attname = "oid"; cstate->cur_attval = string; *tupleOid = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(string))); if (*tupleOid == InvalidOid) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("invalid OID in COPY data"))); cstate->cur_attname = NULL; cstate->cur_attval = NULL; } } /* Loop to read the user attributes on the line. */ foreach(cur, cstate->attnumlist) { int attnum = lfirst_int(cur); int m = attnum - 1; if (fieldno >= fldct) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("missing data for column \"%s\"", NameStr(attr[m]->attname)))); string = field_strings[fieldno++]; if (cstate->convert_select_flags && !cstate->convert_select_flags[m]) { /* ignore input field, leaving column as NULL */ continue; } if (cstate->csv_mode && string == NULL && cstate->force_notnull_flags[m]) { /* Go ahead and read the NULL string */ string = cstate->null_print; } cstate->cur_attname = NameStr(attr[m]->attname); cstate->cur_attval = string; values[m] = InputFunctionCall(&in_functions[m], string, typioparams[m], attr[m]->atttypmod); if (string != NULL) nulls[m] = false; cstate->cur_attname = NULL; cstate->cur_attval = NULL; } Assert(fieldno == nfields); } else { /* binary */ int16 fld_count; ListCell *cur; cstate->cur_lineno++; if (!CopyGetInt16(cstate, &fld_count)) { /* EOF detected (end of file, or protocol-level EOF) */ return false; } if (fld_count == -1) { /* * Received EOF marker. In a V3-protocol copy, wait for the * protocol-level EOF, and complain if it doesn't come * immediately. This ensures that we correctly handle CopyFail, * if client chooses to send that now. * * Note that we MUST NOT try to read more data in an old-protocol * copy, since there is no protocol-level EOF marker then. We * could go either way for copy from file, but choose to throw * error if there's data after the EOF marker, for consistency * with the new-protocol case. */ char dummy; if (cstate->copy_dest != COPY_OLD_FE && CopyGetData(cstate, &dummy, 1, 1) > 0) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("received copy data after EOF marker"))); return false; } if (fld_count != attr_count) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("row field count is %d, expected %d", (int) fld_count, attr_count))); if (file_has_oids) { Oid loaded_oid; cstate->cur_attname = "oid"; loaded_oid = DatumGetObjectId(CopyReadBinaryAttribute(cstate, 0, &cstate->oid_in_function, cstate->oid_typioparam, -1, &isnull)); if (isnull || loaded_oid == InvalidOid) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("invalid OID in COPY data"))); cstate->cur_attname = NULL; if (cstate->oids && tupleOid != NULL) *tupleOid = loaded_oid; } i = 0; foreach(cur, cstate->attnumlist) { int attnum = lfirst_int(cur); int m = attnum - 1; cstate->cur_attname = NameStr(attr[m]->attname); i++; values[m] = CopyReadBinaryAttribute(cstate, i, &in_functions[m], typioparams[m], attr[m]->atttypmod, &nulls[m]); cstate->cur_attname = NULL; } } /* * Now compute and insert any defaults available for the columns not * provided by the input data. Anything not processed here or above will * remain NULL. */ for (i = 0; i < num_defaults; i++) { /* * The caller must supply econtext and have switched into the * per-tuple memory context in it. */ Assert(econtext != NULL); Assert(CurrentMemoryContext == econtext->ecxt_per_tuple_memory); values[defmap[i]] = ExecEvalExpr(defexprs[i], econtext, &nulls[defmap[i]], NULL); } return true; }
Definition at line 2621 of file copy.c.
References Assert, CopyStateData::binary, CopyReadAttributesCSV(), CopyReadAttributesText(), CopyReadLine(), CopyStateData::csv_mode, CopyStateData::cur_lineno, CopyStateData::header_line, StringInfoData::len, CopyStateData::line_buf, and CopyStateData::raw_fields.
Referenced by NextCopyFrom().
{ int fldct; bool done; /* only available for text or csv input */ Assert(!cstate->binary); /* on input just throw the header line away */ if (cstate->cur_lineno == 0 && cstate->header_line) { cstate->cur_lineno++; if (CopyReadLine(cstate)) return false; /* done */ } cstate->cur_lineno++; /* Actually read the line into memory here */ done = CopyReadLine(cstate); /* * EOF at start of line means we're done. If we see EOF after some * characters, we act as though it was newline followed by EOF, ie, * process the line and then exit loop on next iteration. */ if (done && cstate->line_buf.len == 0) return false; /* Parse the line into de-escaped field values */ if (cstate->csv_mode) fldct = CopyReadAttributesCSV(cstate); else fldct = CopyReadAttributesText(cstate); *fields = cstate->raw_fields; *nfields = fldct; return true; }
Definition at line 896 of file copy.c.
References DefElem::arg, CopyStateData::binary, CopyStateData::convert_select, CopyStateData::convert_selectively, CopyStateData::csv_mode, defGetBoolean(), defGetString(), DefElem::defname, CopyStateData::delim, ereport, errcode(), errmsg(), ERROR, CopyStateData::escape, CopyStateData::file_encoding, CopyStateData::force_notnull, CopyStateData::force_quote, CopyStateData::force_quote_all, CopyStateData::freeze, CopyStateData::header_line, IsA, lfirst, NIL, NULL, CopyStateData::null_print, CopyStateData::null_print_len, CopyStateData::oids, palloc0(), pg_char_to_encoding(), and CopyStateData::quote.
Referenced by BeginCopy(), and file_fdw_validator().
{ bool format_specified = false; ListCell *option; /* Support external use for option sanity checking */ if (cstate == NULL) cstate = (CopyStateData *) palloc0(sizeof(CopyStateData)); cstate->file_encoding = -1; /* Extract options from the statement node tree */ foreach(option, options) { DefElem *defel = (DefElem *) lfirst(option); if (strcmp(defel->defname, "format") == 0) { char *fmt = defGetString(defel); if (format_specified) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); format_specified = true; if (strcmp(fmt, "text") == 0) /* default format */ ; else if (strcmp(fmt, "csv") == 0) cstate->csv_mode = true; else if (strcmp(fmt, "binary") == 0) cstate->binary = true; else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("COPY format \"%s\" not recognized", fmt))); } else if (strcmp(defel->defname, "oids") == 0) { if (cstate->oids) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); cstate->oids = defGetBoolean(defel); } else if (strcmp(defel->defname, "freeze") == 0) { if (cstate->freeze) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); cstate->freeze = defGetBoolean(defel); } else if (strcmp(defel->defname, "delimiter") == 0) { if (cstate->delim) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); cstate->delim = defGetString(defel); } else if (strcmp(defel->defname, "null") == 0) { if (cstate->null_print) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); cstate->null_print = defGetString(defel); } else if (strcmp(defel->defname, "header") == 0) { if (cstate->header_line) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); cstate->header_line = defGetBoolean(defel); } else if (strcmp(defel->defname, "quote") == 0) { if (cstate->quote) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); cstate->quote = defGetString(defel); } else if (strcmp(defel->defname, "escape") == 0) { if (cstate->escape) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); cstate->escape = defGetString(defel); } else if (strcmp(defel->defname, "force_quote") == 0) { if (cstate->force_quote || cstate->force_quote_all) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); if (defel->arg && IsA(defel->arg, A_Star)) cstate->force_quote_all = true; else if (defel->arg && IsA(defel->arg, List)) cstate->force_quote = (List *) defel->arg; else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("argument to option \"%s\" must be a list of column names", defel->defname))); } else if (strcmp(defel->defname, "force_not_null") == 0) { if (cstate->force_notnull) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); if (defel->arg && IsA(defel->arg, List)) cstate->force_notnull = (List *) defel->arg; else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("argument to option \"%s\" must be a list of column names", defel->defname))); } else if (strcmp(defel->defname, "convert_selectively") == 0) { /* * Undocumented, not-accessible-from-SQL option: convert only * the named columns to binary form, storing the rest as NULLs. * It's allowed for the column list to be NIL. */ if (cstate->convert_selectively) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); cstate->convert_selectively = true; if (defel->arg == NULL || IsA(defel->arg, List)) cstate->convert_select = (List *) defel->arg; else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("argument to option \"%s\" must be a list of column names", defel->defname))); } else if (strcmp(defel->defname, "encoding") == 0) { if (cstate->file_encoding >= 0) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); cstate->file_encoding = pg_char_to_encoding(defGetString(defel)); if (cstate->file_encoding < 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("argument to option \"%s\" must be a valid encoding name", defel->defname))); } else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("option \"%s\" not recognized", defel->defname))); } /* * Check for incompatible options (must do these two before inserting * defaults) */ if (cstate->binary && cstate->delim) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("cannot specify DELIMITER in BINARY mode"))); if (cstate->binary && cstate->null_print) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("cannot specify NULL in BINARY mode"))); /* Set defaults for omitted options */ if (!cstate->delim) cstate->delim = cstate->csv_mode ? "," : "\t"; if (!cstate->null_print) cstate->null_print = cstate->csv_mode ? "" : "\\N"; cstate->null_print_len = strlen(cstate->null_print); if (cstate->csv_mode) { if (!cstate->quote) cstate->quote = "\""; if (!cstate->escape) cstate->escape = cstate->quote; } /* Only single-byte delimiter strings are supported. */ if (strlen(cstate->delim) != 1) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY delimiter must be a single one-byte character"))); /* Disallow end-of-line characters */ if (strchr(cstate->delim, '\r') != NULL || strchr(cstate->delim, '\n') != NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("COPY delimiter cannot be newline or carriage return"))); if (strchr(cstate->null_print, '\r') != NULL || strchr(cstate->null_print, '\n') != NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("COPY null representation cannot use newline or carriage return"))); /* * Disallow unsafe delimiter characters in non-CSV mode. We can't allow * backslash because it would be ambiguous. We can't allow the other * cases because data characters matching the delimiter must be * backslashed, and certain backslash combinations are interpreted * non-literally by COPY IN. Disallowing all lower case ASCII letters is * more than strictly necessary, but seems best for consistency and * future-proofing. Likewise we disallow all digits though only octal * digits are actually dangerous. */ if (!cstate->csv_mode && strchr("\\.abcdefghijklmnopqrstuvwxyz0123456789", cstate->delim[0]) != NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("COPY delimiter cannot be \"%s\"", cstate->delim))); /* Check header */ if (!cstate->csv_mode && cstate->header_line) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY HEADER available only in CSV mode"))); /* Check quote */ if (!cstate->csv_mode && cstate->quote != NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY quote available only in CSV mode"))); if (cstate->csv_mode && strlen(cstate->quote) != 1) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY quote must be a single one-byte character"))); if (cstate->csv_mode && cstate->delim[0] == cstate->quote[0]) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("COPY delimiter and quote must be different"))); /* Check escape */ if (!cstate->csv_mode && cstate->escape != NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY escape available only in CSV mode"))); if (cstate->csv_mode && strlen(cstate->escape) != 1) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY escape must be a single one-byte character"))); /* Check force_quote */ if (!cstate->csv_mode && (cstate->force_quote || cstate->force_quote_all)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY force quote available only in CSV mode"))); if ((cstate->force_quote || cstate->force_quote_all) && is_from) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY force quote only available using COPY TO"))); /* Check force_notnull */ if (!cstate->csv_mode && cstate->force_notnull != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY force not null available only in CSV mode"))); if (cstate->force_notnull != NIL && !is_from) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY force not null only available using COPY FROM"))); /* Don't allow the delimiter to appear in the null string. */ if (strchr(cstate->null_print, cstate->delim[0]) != NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY delimiter must not appear in the NULL specification"))); /* Don't allow the CSV quote char to appear in the null string. */ if (cstate->csv_mode && strchr(cstate->null_print, cstate->quote[0]) != NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("CSV quote character must not appear in the NULL specification"))); }