diff --git a/doc/src/sgml/ref/copy.sgml b/doc/src/sgml/ref/copy.sgml index 07e2f45..4d13478 100644 --- a/doc/src/sgml/ref/copy.sgml +++ b/doc/src/sgml/ref/copy.sgml @@ -399,6 +399,11 @@ COPY count with views. However, you can write COPY (SELECT * FROM viewname) TO .... + + + COPY FROM can only be used with plain tables and views + with INSTEAD of INSERT triggers. + COPY only deals with the specific table named; diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index b4140eb..df4b169 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -2269,13 +2269,23 @@ CopyFrom(CopyState cstate) Assert(cstate->rel); - if (cstate->rel->rd_rel->relkind != RELKIND_RELATION) + /* + * Check whether the target RELKIND is not a RELATION and raise an error + * if the relation doesn't contains any INSTEAD of triggers. Creation of + * INSTEAD triggers are possible only on VIEWs, check CreateTrigger + * function in trigger.c file. + */ + if (cstate->rel->rd_rel->relkind != RELKIND_RELATION + && (!cstate->rel->trigdesc || + !cstate->rel->trigdesc->trig_insert_instead_row)) { if (cstate->rel->rd_rel->relkind == RELKIND_VIEW) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot copy to view \"%s\"", - RelationGetRelationName(cstate->rel)))); + RelationGetRelationName(cstate->rel)), + errhint("To enable copy to view, provide" + " an INSTEAD OF INSERT trigger"))); else if (cstate->rel->rd_rel->relkind == RELKIND_MATVIEW) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), @@ -2496,52 +2506,61 @@ CopyFrom(CopyState cstate) if (!skip_tuple) { - /* Check the constraints of the tuple */ - if (cstate->rel->rd_att->constr) - ExecConstraints(resultRelInfo, slot, estate); - - if (useHeapMultiInsert) + /* INSTEAD ROW INSERT Triggers */ + if (resultRelInfo->ri_TrigDesc && + resultRelInfo->ri_TrigDesc->trig_insert_instead_row) { - /* Add this tuple to the tuple buffer */ - if (nBufferedTuples == 0) - firstBufferedLineNo = cstate->cur_lineno; - bufferedTuples[nBufferedTuples++] = tuple; - bufferedTuplesSize += tuple->t_len; - - /* - * If the buffer filled up, flush it. Also flush if the total - * size of all the tuples in the buffer becomes large, to - * avoid using large amounts of memory for the buffers when - * the tuples are exceptionally wide. - */ - if (nBufferedTuples == MAX_BUFFERED_TUPLES || - bufferedTuplesSize > 65535) - { - CopyFromInsertBatch(cstate, estate, mycid, hi_options, - resultRelInfo, myslot, bistate, - nBufferedTuples, bufferedTuples, - firstBufferedLineNo); - nBufferedTuples = 0; - bufferedTuplesSize = 0; - } + ExecIRInsertTriggers(estate, resultRelInfo, slot); } else { - List *recheckIndexes = NIL; + /* Check the constraints of the tuple */ + if (cstate->rel->rd_att->constr) + ExecConstraints(resultRelInfo, slot, estate); + + if (useHeapMultiInsert) + { + /* Add this tuple to the tuple buffer */ + if (nBufferedTuples == 0) + firstBufferedLineNo = cstate->cur_lineno; + bufferedTuples[nBufferedTuples++] = tuple; + bufferedTuplesSize += tuple->t_len; + + /* + * If the buffer filled up, flush it. Also flush if the + * total size of all the tuples in the buffer becomes + * large, to avoid using large amounts of memory for the + * buffers when the tuples are exceptionally wide. + */ + if (nBufferedTuples == MAX_BUFFERED_TUPLES || + bufferedTuplesSize > 65535) + { + CopyFromInsertBatch(cstate, estate, mycid, hi_options, + resultRelInfo, myslot, bistate, + nBufferedTuples, bufferedTuples, + firstBufferedLineNo); + nBufferedTuples = 0; + bufferedTuplesSize = 0; + } + } + else + { + List *recheckIndexes = NIL; - /* OK, store the tuple and create index entries for it */ - heap_insert(cstate->rel, tuple, mycid, hi_options, bistate); + /* 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), + if (resultRelInfo->ri_NumIndices > 0) + recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false, NULL, - NIL); + NIL); - /* AFTER ROW INSERT Triggers */ - ExecARInsertTriggers(estate, resultRelInfo, tuple, - recheckIndexes); + /* AFTER ROW INSERT Triggers */ + ExecARInsertTriggers(estate, resultRelInfo, tuple, + recheckIndexes); - list_free(recheckIndexes); + list_free(recheckIndexes); + } } /*