@@ -864,8 +864,8 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, uint64 *processed)
864864 * statement.
865865 *
866866 * In the case that columns are specified in the attribute list,
867- * create a ColumnRef and ResTarget for each column and add them to
868- * the target list for the resulting SELECT statement.
867+ * create a ColumnRef and ResTarget for each column and add them
868+ *to the target list for the resulting SELECT statement.
869869 */
870870if (!stmt -> attlist )
871871{
@@ -2269,13 +2269,21 @@ CopyFrom(CopyState cstate)
22692269
22702270Assert (cstate -> rel );
22712271
2272- if (cstate -> rel -> rd_rel -> relkind != RELKIND_RELATION )
2272+ /*
2273+ * The target must be a plain relation or have an INSTEAD OF INSERT row
2274+ * trigger. (Currently, such triggers are only allowed on views, so we
2275+ * only hint about them in the view case.)
2276+ */
2277+ if (cstate -> rel -> rd_rel -> relkind != RELKIND_RELATION &&
2278+ !(cstate -> rel -> trigdesc &&
2279+ cstate -> rel -> trigdesc -> trig_insert_instead_row ))
22732280{
22742281if (cstate -> rel -> rd_rel -> relkind == RELKIND_VIEW )
22752282ereport (ERROR ,
22762283(errcode (ERRCODE_WRONG_OBJECT_TYPE ),
22772284errmsg ("cannot copy to view \"%s\"" ,
2278- RelationGetRelationName (cstate -> rel ))));
2285+ RelationGetRelationName (cstate -> rel )),
2286+ errhint ("To enable copying to a view, provide an INSTEAD OF INSERT trigger." )));
22792287else if (cstate -> rel -> rd_rel -> relkind == RELKIND_MATVIEW )
22802288ereport (ERROR ,
22812289(errcode (ERRCODE_WRONG_OBJECT_TYPE ),
@@ -2496,52 +2504,64 @@ CopyFrom(CopyState cstate)
24962504
24972505if (!skip_tuple )
24982506{
2499- /* Check the constraints of the tuple */
2500- if (cstate -> rel -> rd_att -> constr )
2501- ExecConstraints (resultRelInfo ,slot ,estate );
2502-
2503- if (useHeapMultiInsert )
2507+ if (resultRelInfo -> ri_TrigDesc &&
2508+ resultRelInfo -> ri_TrigDesc -> trig_insert_instead_row )
25042509{
2505- /* Add this tuple to the tuple buffer */
2506- if (nBufferedTuples == 0 )
2507- firstBufferedLineNo = cstate -> cur_lineno ;
2508- bufferedTuples [nBufferedTuples ++ ]= tuple ;
2509- bufferedTuplesSize += tuple -> t_len ;
2510-
2511- /*
2512- * If the buffer filled up, flush it. Also flush if the total
2513- * size of all the tuples in the buffer becomes large, to
2514- * avoid using large amounts of memory for the buffers when
2515- * the tuples are exceptionally wide.
2516- */
2517- if (nBufferedTuples == MAX_BUFFERED_TUPLES ||
2518- bufferedTuplesSize > 65535 )
2519- {
2520- CopyFromInsertBatch (cstate ,estate ,mycid ,hi_options ,
2521- resultRelInfo ,myslot ,bistate ,
2522- nBufferedTuples ,bufferedTuples ,
2523- firstBufferedLineNo );
2524- nBufferedTuples = 0 ;
2525- bufferedTuplesSize = 0 ;
2526- }
2510+ /* Pass the data to the INSTEAD ROW INSERT trigger */
2511+ ExecIRInsertTriggers (estate ,resultRelInfo ,slot );
25272512}
25282513else
25292514{
2530- List * recheckIndexes = NIL ;
2515+ /* Check the constraints of the tuple */
2516+ if (cstate -> rel -> rd_att -> constr )
2517+ ExecConstraints (resultRelInfo ,slot ,estate );
2518+
2519+ if (useHeapMultiInsert )
2520+ {
2521+ /* Add this tuple to the tuple buffer */
2522+ if (nBufferedTuples == 0 )
2523+ firstBufferedLineNo = cstate -> cur_lineno ;
2524+ bufferedTuples [nBufferedTuples ++ ]= tuple ;
2525+ bufferedTuplesSize += tuple -> t_len ;
2526+
2527+ /*
2528+ * If the buffer filled up, flush it. Also flush if the
2529+ * total size of all the tuples in the buffer becomes
2530+ * large, to avoid using large amounts of memory for the
2531+ * buffer when the tuples are exceptionally wide.
2532+ */
2533+ if (nBufferedTuples == MAX_BUFFERED_TUPLES ||
2534+ bufferedTuplesSize > 65535 )
2535+ {
2536+ CopyFromInsertBatch (cstate ,estate ,mycid ,hi_options ,
2537+ resultRelInfo ,myslot ,bistate ,
2538+ nBufferedTuples ,bufferedTuples ,
2539+ firstBufferedLineNo );
2540+ nBufferedTuples = 0 ;
2541+ bufferedTuplesSize = 0 ;
2542+ }
2543+ }
2544+ else
2545+ {
2546+ List * recheckIndexes = NIL ;
25312547
2532- /* OK, store the tuple and create index entries for it */
2533- heap_insert (cstate -> rel ,tuple ,mycid ,hi_options ,bistate );
2548+ /* OK, store the tuple and create index entries for it */
2549+ heap_insert (cstate -> rel ,tuple ,mycid ,hi_options ,bistate );
25342550
2535- if (resultRelInfo -> ri_NumIndices > 0 )
2536- recheckIndexes = ExecInsertIndexTuples (slot ,& (tuple -> t_self ),
2537- estate , false,NULL ,
2538- NIL );
2551+ if (resultRelInfo -> ri_NumIndices > 0 )
2552+ recheckIndexes = ExecInsertIndexTuples (slot ,
2553+ & (tuple -> t_self ),
2554+ estate ,
2555+ false,
2556+ NULL ,
2557+ NIL );
25392558
2540- /* AFTER ROW INSERT Triggers */
2541- ExecARInsertTriggers (estate ,resultRelInfo ,tuple ,
2542- recheckIndexes );
2559+ /* AFTER ROW INSERT Triggers */
2560+ ExecARInsertTriggers (estate ,resultRelInfo ,tuple ,
2561+ recheckIndexes );
25432562
2544- list_free (recheckIndexes );
2563+ list_free (recheckIndexes );
2564+ }
25452565}
25462566
25472567/*