diff options
author | Shigeru Hanada | 2011-02-17 08:13:24 +0000 |
---|---|---|
committer | Shigeru Hanada | 2011-02-17 08:13:24 +0000 |
commit | d9379f2c568fe6aa850a4fc2be7c9644909f73ae (patch) | |
tree | eca8d170bc798053bf89f2c6f45bb3b2aeb70fa5 | |
parent | 0e1a1e1b0e168cb3d8ff4d637747d0ba8f7b8d55 (diff) |
Revert "Apply copy_export-20110207.patch."
This reverts commit 9df6d0d5d14bc453c17dca348aaafe69a1017170.
-rw-r--r-- | src/backend/commands/copy.c | 907 | ||||
-rw-r--r-- | src/include/commands/copy.h | 10 |
2 files changed, 359 insertions, 558 deletions
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 6df0f1eb16..3350ca0b6e 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -93,11 +93,13 @@ typedef struct CopyStateData FILE *copy_file; /* used if copy_dest == COPY_FILE */ StringInfo fe_msgbuf; /* used for all dests during COPY TO, only for * dest == COPY_NEW_FE in COPY FROM */ + bool fe_copy; /* true for all FE copy dests */ bool fe_eof; /* true if detected end of copy data */ EolType eol_type; /* EOL type of input */ int client_encoding; /* remote side's character encoding */ bool need_transcoding; /* client encoding diff from server? */ bool encoding_embeds_ascii; /* ASCII can be non-first byte? */ + uint64 processed; /* # of tuples processed */ /* parameters from the COPY command */ Relation rel; /* relation to copy to or from */ @@ -117,18 +119,13 @@ typedef struct CopyStateData bool *force_quote_flags; /* per-column CSV FQ flags */ bool *force_notnull_flags; /* per-column CSV FNN flags */ - /* these are just for error messages, see CopyFromErrorCallback */ + /* these are just for error messages, see copy_in_error_callback */ const char *cur_relname; /* table name for error messages */ int cur_lineno; /* line number for error messages */ const char *cur_attname; /* current att for error messages */ const char *cur_attval; /* current att value for error messages */ /* - * Working state for COPY TO/FROM - */ - MemoryContext copycontext; /* per-copy execution context */ - - /* * Working state for COPY TO */ FmgrInfo *out_functions; /* lookup info for output functions */ @@ -170,28 +167,15 @@ typedef struct CopyStateData char *raw_buf; int raw_buf_index; /* next byte to process */ int raw_buf_len; /* total # of bytes stored */ - - /* - * The definition of input functions and default expressions are stored - * in these variables. - */ - EState *estate; - AttrNumber num_defaults; - bool file_has_oids; - FmgrInfo oid_in_function; - Oid oid_typioparam; - FmgrInfo *in_functions; /* array of input functions for each attrs */ - Oid *typioparams; /* array of element types for in_functions */ - int *defmap; /* array of default att numbers */ - ExprState **defexprs; /* array of default att expressions */ } CopyStateData; +typedef CopyStateData *CopyState; + /* DestReceiver for COPY (SELECT) TO */ typedef struct { DestReceiver pub; /* publicly-known function pointers */ CopyState cstate; /* CopyStateData for the command */ - uint64 processed; /* # of tuples processed */ } DR_copy; @@ -264,17 +248,11 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0"; /* non-export function prototypes */ -static CopyState BeginCopy(bool is_from, Relation rel, Node *raw_query, - const char *queryString, List *attnamelist, List *options); -static void EndCopy(CopyState cstate); -static CopyState BeginCopyTo(Relation rel, Node *query, const char *queryString, - const char *filename, List *attnamelist, List *options); -static void EndCopyTo(CopyState cstate); -static uint64 DoCopyTo(CopyState cstate); -static uint64 CopyTo(CopyState cstate); +static void DoCopyTo(CopyState cstate); +static void CopyTo(CopyState cstate); static void CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls); -static uint64 CopyFrom(CopyState cstate); +static void CopyFrom(CopyState cstate); static bool CopyReadLine(CopyState cstate); static bool CopyReadLineText(CopyState cstate); static int CopyReadAttributesText(CopyState cstate); @@ -746,125 +724,22 @@ DoCopy(const CopyStmt *stmt, const char *queryString) CopyState cstate; bool is_from = stmt->is_from; bool pipe = (stmt->filename == NULL); - Relation rel; - uint64 processed; - - /* Disallow file COPY except to superusers. */ - if (!pipe && !superuser()) - 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)); - - rte = makeNode(RangeTblEntry); - rte->rtekind = RTE_RELATION; - rte->relid = RelationGetRelid(rel); - 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); - - rel = NULL; - } - - if (is_from) - { - /* check read-only transaction */ - if (XactReadOnly && rel->rd_backend != MyBackendId) - PreventCommandIfReadOnly("COPY FROM"); - - cstate = BeginCopyFrom(rel, stmt->filename, - stmt->attlist, stmt->options); - processed = CopyFrom(cstate); /* copy from file to database */ - EndCopyFrom(cstate); - } - else - { - cstate = BeginCopyTo(rel, stmt->query, queryString, stmt->filename, - 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 processed; -} - -/* - * Common setup routines used by BeginCopyFrom and BeginCopyTo. - */ -static CopyState -BeginCopy(bool is_from, - Relation rel, - Node *raw_query, - const char *queryString, - List *attnamelist, - List *options) -{ - CopyState cstate; + List *attnamelist = stmt->attlist; List *force_quote = NIL; List *force_notnull = NIL; bool force_quote_all = false; bool format_specified = false; + AclMode required_access = (is_from ? ACL_INSERT : ACL_SELECT); ListCell *option; TupleDesc tupDesc; int num_phys_attrs; - MemoryContext oldcontext; + uint64 processed; /* Allocate workspace and zero all fields */ cstate = (CopyStateData *) palloc0(sizeof(CopyStateData)); - /* - * We allocate everything used by a cstate in a new memory context. - * This would avoid memory leaks repeated uses of COPY in a query. - */ - cstate->copycontext = AllocSetContextCreate(CurrentMemoryContext, - "COPY", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - - oldcontext = MemoryContextSwitchTo(cstate->copycontext); - /* Extract options from the statement node tree */ - foreach(option, options) + foreach(option, stmt->options) { DefElem *defel = (DefElem *) lfirst(option); @@ -1105,14 +980,51 @@ BeginCopy(bool is_from, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("CSV quote character must not appear in the NULL specification"))); - if (rel) + /* Disallow file COPY except to superusers. */ + if (!pipe && !superuser()) + 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) { - Assert(!raw_query); + RangeTblEntry *rte; + List *attnums; + ListCell *cur; - cstate->rel = rel; + Assert(!stmt->query); + cstate->queryDesc = NULL; + + /* Open and lock the relation, using the appropriate lock type. */ + cstate->rel = heap_openrv(stmt->relation, + (is_from ? RowExclusiveLock : AccessShareLock)); tupDesc = RelationGetDescr(cstate->rel); + /* Check relation permissions. */ + rte = makeNode(RangeTblEntry); + rte->rtekind = RTE_RELATION; + rte->relid = RelationGetRelid(cstate->rel); + rte->requiredPerms = required_access; + + attnums = CopyGetAttnums(tupDesc, cstate->rel, attnamelist); + 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); + + /* check read-only transaction */ + if (XactReadOnly && is_from && cstate->rel->rd_backend != MyBackendId) + PreventCommandIfReadOnly("COPY FROM"); + /* Don't allow COPY w/ OIDs to or from a table without them */ if (cstate->oids && !cstate->rel->rd_rel->relhasoids) ereport(ERROR, @@ -1146,7 +1058,7 @@ BeginCopy(bool is_from, * function and is executed repeatedly. (See also the same hack in * DECLARE CURSOR and PREPARE.) XXX FIXME someday. */ - rewritten = pg_analyze_and_rewrite((Node *) copyObject(raw_query), + rewritten = pg_analyze_and_rewrite((Node *) copyObject(stmt->query), queryString, NULL, 0); /* We don't expect more or less than one result query */ @@ -1248,6 +1160,14 @@ BeginCopy(bool is_from, } } + /* 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; + cstate->processed = 0; + /* * Set up encoding conversion info. Even if the client and server * encodings are the same, we must apply pg_client_to_server() to validate @@ -1261,75 +1181,84 @@ BeginCopy(bool is_from, cstate->encoding_embeds_ascii = PG_ENCODING_IS_CLIENT_ONLY(cstate->client_encoding); cstate->copy_dest = COPY_FILE; /* default */ + cstate->filename = stmt->filename; - MemoryContextSwitchTo(oldcontext); + if (is_from) + CopyFrom(cstate); /* copy from file to database */ + else + DoCopyTo(cstate); /* copy from database to file */ - return cstate; -} + /* + * Close the relation or query. 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 (cstate->rel) + heap_close(cstate->rel, (is_from ? NoLock : AccessShareLock)); + else + { + /* Close down the query and free resources. */ + ExecutorEnd(cstate->queryDesc); + FreeQueryDesc(cstate->queryDesc); + PopActiveSnapshot(); + } -/* - * Release resources allocated in a cstate. - */ -static void -EndCopy(CopyState cstate) -{ - if (cstate->filename != NULL && FreeFile(cstate->copy_file)) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not close file \"%s\": %m", - cstate->filename))); + /* Clean up storage (probably not really necessary) */ + processed = cstate->processed; - MemoryContextDelete(cstate->copycontext); + pfree(cstate->attribute_buf.data); + pfree(cstate->line_buf.data); + pfree(cstate->raw_buf); pfree(cstate); + + return processed; } + /* - * Setup CopyState to read tuples from a table or a query for COPY TO. + * This intermediate routine exists mainly to localize the effects of setjmp + * so we don't need to plaster a lot of variables with "volatile". */ -static CopyState -BeginCopyTo(Relation rel, - Node *query, - const char *queryString, - const char *filename, - List *attnamelist, - List *options) +static void +DoCopyTo(CopyState cstate) { - CopyState cstate; - bool pipe = (filename == NULL); - MemoryContext oldcontext; + bool pipe = (cstate->filename == NULL); - if (rel != NULL && rel->rd_rel->relkind != RELKIND_RELATION) + if (cstate->rel) { - if (rel->rd_rel->relkind == RELKIND_VIEW) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy from view \"%s\"", - RelationGetRelationName(rel)), - errhint("Try the COPY (SELECT ...) TO variant."))); - else if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy from foreign table \"%s\"", - RelationGetRelationName(rel)), - errhint("Try the COPY (SELECT ...) TO variant."))); - else if (rel->rd_rel->relkind == RELKIND_SEQUENCE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy from sequence \"%s\"", - RelationGetRelationName(rel)))); - else - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy from non-table relation \"%s\"", - RelationGetRelationName(rel)))); + if (cstate->rel->rd_rel->relkind != RELKIND_RELATION) + { + if (cstate->rel->rd_rel->relkind == RELKIND_VIEW) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy from view \"%s\"", + RelationGetRelationName(cstate->rel)), + errhint("Try the COPY (SELECT ...) TO variant."))); + else if (cstate->rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy from foreign table \"%s\"", + RelationGetRelationName(cstate->rel)), + errhint("Try the COPY (SELECT ...) TO variant."))); + else if (cstate->rel->rd_rel->relkind == RELKIND_SEQUENCE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy from sequence \"%s\"", + RelationGetRelationName(cstate->rel)))); + else + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy from non-table relation \"%s\"", + RelationGetRelationName(cstate->rel)))); + } } - cstate = BeginCopy(false, rel, query, queryString, attnamelist, options); - oldcontext = MemoryContextSwitchTo(cstate->copycontext); - if (pipe) { - if (whereToSendOutput != DestRemote) + if (whereToSendOutput == DestRemote) + cstate->fe_copy = true; + else cstate->copy_file = stdout; } else @@ -1341,12 +1270,11 @@ BeginCopyTo(Relation rel, * Prevent write to relative path ... too easy to shoot oneself in the * foot by overwriting a database file ... */ - if (!is_absolute_path(filename)) + if (!is_absolute_path(cstate->filename)) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("relative path not allowed for COPY to file"))); - cstate->filename = pstrdup(filename); oumask = umask(S_IWGRP | S_IWOTH); cstate->copy_file = AllocateFile(cstate->filename, PG_BINARY_W); umask(oumask); @@ -1364,30 +1292,14 @@ BeginCopyTo(Relation rel, errmsg("\"%s\" is a directory", cstate->filename))); } - MemoryContextSwitchTo(oldcontext); - - return cstate; -} - -/* - * This intermediate routine exists mainly to localize the effects of setjmp - * so we don't need to plaster a lot of variables with "volatile". - */ -static uint64 -DoCopyTo(CopyState cstate) -{ - bool pipe = (cstate->filename == NULL); - bool fe_copy = (pipe && whereToSendOutput == DestRemote); - uint64 processed; - PG_TRY(); { - if (fe_copy) + if (cstate->fe_copy) SendCopyBegin(cstate); - processed = CopyTo(cstate); + CopyTo(cstate); - if (fe_copy) + if (cstate->fe_copy) SendCopyEnd(cstate); } PG_CATCH(); @@ -1402,38 +1314,26 @@ DoCopyTo(CopyState cstate) } PG_END_TRY(); - return processed; -} - -/* - * Clean up storage and release resources for COPY TO. - */ -static void -EndCopyTo(CopyState cstate) -{ - if (cstate->queryDesc != NULL) + if (!pipe) { - /* Close down the query and free resources. */ - ExecutorEnd(cstate->queryDesc); - FreeQueryDesc(cstate->queryDesc); - PopActiveSnapshot(); + if (FreeFile(cstate->copy_file)) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not close file \"%s\": %m", + cstate->filename))); } - - /* Clean up storage */ - EndCopy(cstate); } /* * Copy from relation or query TO file. */ -static uint64 +static void CopyTo(CopyState cstate) { TupleDesc tupDesc; int num_phys_attrs; Form_pg_attribute *attr; ListCell *cur; - uint64 processed; if (cstate->rel) tupDesc = RelationGetDescr(cstate->rel); @@ -1539,7 +1439,6 @@ CopyTo(CopyState cstate) scandesc = heap_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL); - processed = 0; while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL) { CHECK_FOR_INTERRUPTS(); @@ -1549,19 +1448,14 @@ CopyTo(CopyState cstate) /* Format and send the data */ CopyOneRowTo(cstate, HeapTupleGetOid(tuple), values, nulls); - processed++; } heap_endscan(scandesc); - - pfree(values); - pfree(nulls); } else { /* run the plan --- the dest receiver will send tuples */ ExecutorRun(cstate->queryDesc, ForwardScanDirection, 0L); - processed = ((DR_copy *) cstate->queryDesc->dest)->processed; } if (cstate->binary) @@ -1573,8 +1467,6 @@ CopyTo(CopyState cstate) } MemoryContextDelete(cstate->rowcontext); - - return processed; } /* @@ -1666,16 +1558,16 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) CopySendEndOfRow(cstate); MemoryContextSwitchTo(oldcontext); + + cstate->processed++; } /* * error context callback for COPY FROM - * - * The argument for the error context must be CopyState. */ -void -CopyFromErrorCallback(void *arg) +static void +copy_in_error_callback(void *arg) { CopyState cstate = (CopyState) arg; @@ -1683,11 +1575,11 @@ CopyFromErrorCallback(void *arg) { /* can't usefully display the data */ if (cstate->cur_attname) - errcontext("relation %s, line %d, column %s", + errcontext("COPY %s, line %d, column %s", cstate->cur_relname, cstate->cur_lineno, cstate->cur_attname); else - errcontext("relation %s, line %d", + errcontext("COPY %s, line %d", cstate->cur_relname, cstate->cur_lineno); } else @@ -1698,7 +1590,7 @@ CopyFromErrorCallback(void *arg) char *attval; attval = limit_printout_length(cstate->cur_attval); - errcontext("relation %s, line %d, column %s: \"%s\"", + errcontext("COPY %s, line %d, column %s: \"%s\"", cstate->cur_relname, cstate->cur_lineno, cstate->cur_attname, attval); pfree(attval); @@ -1706,7 +1598,7 @@ CopyFromErrorCallback(void *arg) else if (cstate->cur_attname) { /* error is relevant to a particular column, value is NULL */ - errcontext("relation %s, line %d, column %s: null input", + errcontext("COPY %s, line %d, column %s: null input", cstate->cur_relname, cstate->cur_lineno, cstate->cur_attname); } @@ -1718,7 +1610,7 @@ CopyFromErrorCallback(void *arg) char *lineval; lineval = limit_printout_length(cstate->line_buf.data); - errcontext("relation %s, line %d: \"%s\"", + errcontext("COPY %s, line %d: \"%s\"", cstate->cur_relname, cstate->cur_lineno, lineval); pfree(lineval); } @@ -1732,7 +1624,7 @@ CopyFromErrorCallback(void *arg) * regurgitate it without conversion. So we have to punt and * just report the line number. */ - errcontext("relation %s, line %d", + errcontext("COPY %s, line %d", cstate->cur_relname, cstate->cur_lineno); } } @@ -1777,22 +1669,41 @@ limit_printout_length(const char *str) /* * Copy FROM file to relation. */ -static uint64 +static void CopyFrom(CopyState cstate) { + bool pipe = (cstate->filename == NULL); HeapTuple tuple; TupleDesc tupDesc; + Form_pg_attribute *attr; + AttrNumber num_phys_attrs, + attr_count, + num_defaults; + FmgrInfo *in_functions; + FmgrInfo oid_in_function; + Oid *typioparams; + Oid oid_typioparam; + int attnum; + int i; + Oid in_func_oid; Datum *values; bool *nulls; + int nfields; + char **field_strings; + bool done = false; + bool isnull; ResultRelInfo *resultRelInfo; - EState *estate = cstate->estate; /* for ExecConstraints() */ + EState *estate = CreateExecutorState(); /* for ExecConstraints() */ TupleTableSlot *slot; + bool file_has_oids; + int *defmap; + ExprState **defexprs; /* array of default att expressions */ + ExprContext *econtext; /* used for ExecEvalExpr for default atts */ MemoryContext oldcontext = CurrentMemoryContext; ErrorContextCallback errcontext; CommandId mycid = GetCurrentCommandId(true); int hi_options = 0; /* start with default heap_insert options */ BulkInsertState bistate; - uint64 processed = 0; Assert(cstate->rel); @@ -1820,8 +1731,6 @@ CopyFrom(CopyState cstate) RelationGetRelationName(cstate->rel)))); } - tupDesc = RelationGetDescr(cstate->rel); - /*---------- * Check to see if we can avoid writing WAL * @@ -1857,6 +1766,38 @@ CopyFrom(CopyState cstate) hi_options |= HEAP_INSERT_SKIP_WAL; } + if (pipe) + { + if (whereToSendOutput == DestRemote) + ReceiveCopyBegin(cstate); + else + cstate->copy_file = stdin; + } + 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))); + } + + tupDesc = RelationGetDescr(cstate->rel); + attr = tupDesc->attrs; + num_phys_attrs = tupDesc->natts; + attr_count = list_length(cstate->attnumlist); + num_defaults = 0; + /* * We need a ResultRelInfo so we can use the regular executor's * index-entry-making machinery. (There used to be a huge amount of code @@ -1885,187 +1826,7 @@ CopyFrom(CopyState cstate) slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - /* Prepare to catch AFTER triggers. */ - AfterTriggerBeginQuery(); - - /* - * Check BEFORE STATEMENT insertion triggers. It's debateable whether we - * should do this for COPY, since it's not really an "INSERT" statement as - * such. However, executing these triggers maintains consistency with the - * EACH ROW triggers that we already fire on COPY. - */ - ExecBSInsertTriggers(estate, resultRelInfo); - - values = (Datum *) palloc(tupDesc->natts * sizeof(Datum)); - nulls = (bool *) palloc(tupDesc->natts * sizeof(bool)); - - bistate = GetBulkInsertState(); - - /* Set up callback to identify error line number */ - errcontext.callback = CopyFromErrorCallback; - errcontext.arg = (void *) cstate; - errcontext.previous = error_context_stack; - error_context_stack = &errcontext; - - for (;;) - { - bool skip_tuple; - Oid loaded_oid = InvalidOid; - - CHECK_FOR_INTERRUPTS(); - - /* Reset the per-tuple exprcontext */ - ResetPerTupleExprContext(estate); - - /* Switch into its memory context */ - MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); - - if (!NextCopyFrom(cstate, values, nulls, &loaded_oid)) - break; - - /* And now we can form the input tuple. */ - tuple = heap_form_tuple(tupDesc, values, nulls); - - if (loaded_oid != InvalidOid) - HeapTupleSetOid(tuple, loaded_oid); - - /* Triggers and stuff need to be invoked in query context. */ - MemoryContextSwitchTo(oldcontext); - - skip_tuple = false; - - /* BEFORE ROW INSERT Triggers */ - if (resultRelInfo->ri_TrigDesc && - resultRelInfo->ri_TrigDesc->trig_insert_before_row) - { - HeapTuple newtuple; - - newtuple = ExecBRInsertTriggers(estate, resultRelInfo, tuple); - - if (newtuple == NULL) /* "do nothing" */ - skip_tuple = true; - else if (newtuple != tuple) /* modified by Trigger(s) */ - { - heap_freetuple(tuple); - tuple = newtuple; - } - } - - if (!skip_tuple) - { - List *recheckIndexes = NIL; - - /* Place tuple in tuple slot */ - ExecStoreTuple(tuple, slot, InvalidBuffer, false); - - /* Check the constraints of the tuple */ - if (cstate->rel->rd_att->constr) - ExecConstraints(resultRelInfo, slot, estate); - - /* OK, store the tuple and create index entries for it */ - heap_insert(cstate->rel, tuple, mycid, hi_options, bistate); - - if (resultRelInfo->ri_NumIndices > 0) - recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), - estate); - - /* AFTER ROW INSERT Triggers */ - ExecARInsertTriggers(estate, resultRelInfo, tuple, - recheckIndexes); - - list_free(recheckIndexes); - - /* - * We count only tuples not suppressed by a BEFORE INSERT trigger; - * this is the same definition used by execMain.c for counting - * tuples inserted by an INSERT command. - */ - processed++; - } - } - - /* Done, clean up */ - error_context_stack = errcontext.previous; - - FreeBulkInsertState(bistate); - - MemoryContextSwitchTo(oldcontext); - - /* Execute AFTER STATEMENT insertion triggers */ - ExecASInsertTriggers(estate, resultRelInfo); - - /* Handle queued AFTER triggers */ - AfterTriggerEndQuery(estate); - - pfree(values); - pfree(nulls); - - ExecResetTupleTable(estate->es_tupleTable, false); - - ExecCloseIndices(resultRelInfo); - - /* - * If we skipped writing WAL, then we need to sync the heap (but not - * indexes since those use WAL anyway) - */ - if (hi_options & HEAP_INSERT_SKIP_WAL) - heap_sync(cstate->rel); - - return processed; -} - -/* - * CopyGetAttnums - build an integer list of attnums to be copied - * - * The input attnamelist is either the user-specified column list, - * or NIL if there was none (in which case we want all the non-dropped - * columns). - * - * rel can be NULL ... it's only used for error reports. - */ -CopyState -BeginCopyFrom(Relation rel, - const char *filename, - List *attnamelist, - List *options) -{ - 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; - EState *estate = CreateExecutorState(); /* for ExecPrepareExpr() */ - int *defmap; - ExprState **defexprs; - MemoryContext oldcontext; - - 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; + econtext = GetPerTupleExprContext(estate); /* * Pick up the required catalog information for each attribute in the @@ -2110,46 +1871,19 @@ BeginCopyFrom(Relation rel, } } - /* We keep those variables in cstate. */ - cstate->estate = estate; - cstate->in_functions = in_functions; - cstate->typioparams = typioparams; - cstate->defmap = defmap; - cstate->defexprs = defexprs; - cstate->num_defaults = num_defaults; - - if (pipe) - { - if (whereToSendOutput == DestRemote) - ReceiveCopyBegin(cstate); - else - cstate->copy_file = stdin; - } - else - { - struct stat st; - - cstate->filename = pstrdup(filename); - 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))); + /* Prepare to catch AFTER triggers. */ + AfterTriggerBeginQuery(); - 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))); - } + /* + * Check BEFORE STATEMENT insertion triggers. It's debateable whether we + * should do this for COPY, since it's not really an "INSERT" statement as + * such. However, executing these triggers maintains consistency with the + * EACH ROW triggers that we already fire on COPY. + */ + ExecBSInsertTriggers(estate, resultRelInfo); if (!cstate->binary) - { - /* must rely on user to tell us... */ - cstate->file_has_oids = cstate->oids; - } + file_has_oids = cstate->oids; /* must rely on user to tell us... */ else { /* Read and verify binary header */ @@ -2167,7 +1901,7 @@ BeginCopyFrom(Relation rel, ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("invalid COPY file header (missing flags)"))); - cstate->file_has_oids = (tmp & (1 << 16)) != 0; + file_has_oids = (tmp & (1 << 16)) != 0; tmp &= ~(1 << 16); if ((tmp >> 16) != 0) ereport(ERROR, @@ -2189,71 +1923,62 @@ BeginCopyFrom(Relation rel, } } - if (cstate->file_has_oids && cstate->binary) + if (file_has_oids && cstate->binary) { getTypeBinaryInputInfo(OIDOID, - &in_func_oid, &cstate->oid_typioparam); - fmgr_info(in_func_oid, &cstate->oid_in_function); + &in_func_oid, &oid_typioparam); + fmgr_info(in_func_oid, &oid_in_function); } + values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); + nulls = (bool *) palloc(num_phys_attrs * sizeof(bool)); + /* create workspace for CopyReadAttributes results */ - if (!cstate->binary) + nfields = file_has_oids ? (attr_count + 1) : attr_count; + 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); + /* 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; - return cstate; -} + bistate = GetBulkInsertState(); -/* - * Read next tuple from file for COPY FROM. Return false if no more tuples. - * - * values and nulls arrays must be the same length as columns of the - * relation passed to BeginCopyFrom. Oid of the tuple is returned with - * tupleOid separately. - */ -bool -NextCopyFrom(CopyState cstate, Datum *values, bool *nulls, Oid *tupleOid) -{ - 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; - char **field_strings; - bool isnull; - bool file_has_oids = cstate->file_has_oids; - int *defmap = cstate->defmap; - ExprState **defexprs = cstate->defexprs; - ExprContext *econtext; /* used for ExecEvalExpr for default atts */ + /* Set up callback to identify error line number */ + errcontext.callback = copy_in_error_callback; + errcontext.arg = (void *) cstate; + errcontext.previous = error_context_stack; + error_context_stack = &errcontext; /* on input just throw the header line away */ - if (cstate->cur_lineno == 0 && cstate->header_line) + if (cstate->header_line) { cstate->cur_lineno++; - if (CopyReadLine(cstate)) - return false; /* done */ + done = CopyReadLine(cstate); } - 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; + while (!done) + { + bool skip_tuple; + Oid loaded_oid = InvalidOid; + + CHECK_FOR_INTERRUPTS(); - /* XXX: Indentation is not adjusted to keep the patch small. */ cstate->cur_lineno++; + /* Reset the per-tuple exprcontext */ + ResetPerTupleExprContext(estate); + + /* Switch into its memory context */ + MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); + /* Initialize all values for row to NULL */ MemSet(values, 0, num_phys_attrs * sizeof(Datum)); MemSet(nulls, true, num_phys_attrs * sizeof(bool)); @@ -2264,7 +1989,6 @@ NextCopyFrom(CopyState cstate, Datum *values, bool *nulls, Oid *tupleOid) int fldct; int fieldno; char *string; - bool done; /* Actually read the line into memory here */ done = CopyReadLine(cstate); @@ -2275,7 +1999,7 @@ NextCopyFrom(CopyState cstate, Datum *values, bool *nulls, Oid *tupleOid) * EOF, ie, process the line and then exit loop on next iteration. */ if (done && cstate->line_buf.len == 0) - return false; + break; /* Parse the line into de-escaped field values */ if (cstate->csv_mode) @@ -2305,13 +2029,13 @@ NextCopyFrom(CopyState cstate, Datum *values, bool *nulls, Oid *tupleOid) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("null OID in COPY data"))); - else if (cstate->oids && tupleOid != NULL) + else { cstate->cur_attname = "oid"; cstate->cur_attval = string; - *tupleOid = DatumGetObjectId(DirectFunctionCall1(oidin, - CStringGetDatum(string))); - if (*tupleOid == InvalidOid) + loaded_oid = DatumGetObjectId(DirectFunctionCall1(oidin, + CStringGetDatum(string))); + if (loaded_oid == InvalidOid) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("invalid OID in COPY data"))); @@ -2363,7 +2087,8 @@ NextCopyFrom(CopyState cstate, Datum *values, bool *nulls, Oid *tupleOid) if (!CopyGetInt16(cstate, &fld_count)) { /* EOF detected (end of file, or protocol-level EOF) */ - return false; + done = true; + break; } if (fld_count == -1) @@ -2387,7 +2112,8 @@ NextCopyFrom(CopyState cstate, Datum *values, bool *nulls, Oid *tupleOid) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("received copy data after EOF marker"))); - return false; + done = true; + break; } if (fld_count != attr_count) @@ -2398,14 +2124,12 @@ NextCopyFrom(CopyState cstate, Datum *values, bool *nulls, Oid *tupleOid) if (file_has_oids) { - Oid loaded_oid; - cstate->cur_attname = "oid"; loaded_oid = DatumGetObjectId(CopyReadBinaryAttribute(cstate, 0, - &cstate->oid_in_function, - cstate->oid_typioparam, + &oid_in_function, + oid_typioparam, -1, &isnull)); if (isnull || loaded_oid == InvalidOid) @@ -2413,8 +2137,6 @@ NextCopyFrom(CopyState cstate, Datum *values, bool *nulls, Oid *tupleOid) (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; @@ -2440,27 +2162,117 @@ NextCopyFrom(CopyState cstate, Datum *values, bool *nulls, Oid *tupleOid) * provided by the input data. Anything not processed here or above * will remain NULL. */ - econtext = GetPerTupleExprContext(cstate->estate); for (i = 0; i < num_defaults; i++) { values[defmap[i]] = ExecEvalExpr(defexprs[i], econtext, &nulls[defmap[i]], NULL); } - /* XXX: End of only-indentation changes. */ - return true; -} + /* And now we can form the input tuple. */ + tuple = heap_form_tuple(tupDesc, values, nulls); -/* - * Clean up storage and release resources for COPY FROM. - */ -void -EndCopyFrom(CopyState cstate) -{ - FreeExecutorState(cstate->estate); + if (cstate->oids && file_has_oids) + HeapTupleSetOid(tuple, loaded_oid); + + /* Triggers and stuff need to be invoked in query context. */ + MemoryContextSwitchTo(oldcontext); + + skip_tuple = false; + + /* BEFORE ROW INSERT Triggers */ + if (resultRelInfo->ri_TrigDesc && + resultRelInfo->ri_TrigDesc->trig_insert_before_row) + { + HeapTuple newtuple; + + newtuple = ExecBRInsertTriggers(estate, resultRelInfo, tuple); - /* Clean up storage */ - EndCopy(cstate); + if (newtuple == NULL) /* "do nothing" */ + skip_tuple = true; + else if (newtuple != tuple) /* modified by Trigger(s) */ + { + heap_freetuple(tuple); + tuple = newtuple; + } + } + + if (!skip_tuple) + { + List *recheckIndexes = NIL; + + /* Place tuple in tuple slot */ + ExecStoreTuple(tuple, slot, InvalidBuffer, false); + + /* Check the constraints of the tuple */ + if (cstate->rel->rd_att->constr) + ExecConstraints(resultRelInfo, slot, estate); + + /* OK, store the tuple and create index entries for it */ + heap_insert(cstate->rel, tuple, mycid, hi_options, bistate); + + if (resultRelInfo->ri_NumIndices > 0) + recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), + estate); + + /* AFTER ROW INSERT Triggers */ + ExecARInsertTriggers(estate, resultRelInfo, tuple, + recheckIndexes); + + list_free(recheckIndexes); + + /* + * We count only tuples not suppressed by a BEFORE INSERT trigger; + * this is the same definition used by execMain.c for counting + * tuples inserted by an INSERT command. + */ + cstate->processed++; + } + } + + /* Done, clean up */ + error_context_stack = errcontext.previous; + + FreeBulkInsertState(bistate); + + MemoryContextSwitchTo(oldcontext); + + /* Execute AFTER STATEMENT insertion triggers */ + ExecASInsertTriggers(estate, resultRelInfo); + + /* Handle queued AFTER triggers */ + AfterTriggerEndQuery(estate); + + pfree(values); + pfree(nulls); + if (! cstate->binary) + pfree(cstate->raw_fields); + + pfree(in_functions); + pfree(typioparams); + pfree(defmap); + pfree(defexprs); + + ExecResetTupleTable(estate->es_tupleTable, false); + + ExecCloseIndices(resultRelInfo); + + FreeExecutorState(estate); + + if (!pipe) + { + if (FreeFile(cstate->copy_file)) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not close file \"%s\": %m", + cstate->filename))); + } + + /* + * If we skipped writing WAL, then we need to sync the heap (but not + * indexes since those use WAL anyway) + */ + if (hi_options & HEAP_INSERT_SKIP_WAL) + heap_sync(cstate->rel); } @@ -3725,7 +3537,6 @@ copy_dest_receive(TupleTableSlot *slot, DestReceiver *self) /* And send the data */ CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull); - myState->processed++; } /* diff --git a/src/include/commands/copy.h b/src/include/commands/copy.h index 8340e3d798..9e2bbe8d8e 100644 --- a/src/include/commands/copy.h +++ b/src/include/commands/copy.h @@ -14,22 +14,12 @@ #ifndef COPY_H #define COPY_H -#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "tcop/dest.h" -typedef struct CopyStateData *CopyState; - extern uint64 DoCopy(const CopyStmt *stmt, const char *queryString); -extern CopyState BeginCopyFrom(Relation rel, const char *filename, - List *attnamelist, List *options); -extern void EndCopyFrom(CopyState cstate); -extern bool NextCopyFrom(CopyState cstate, - Datum *values, bool *nulls, Oid *tupleOid); -extern void CopyFromErrorCallback(void *arg); - extern DestReceiver *CreateCopyDestReceiver(void); #endif /* COPY_H */ |