*** a/src/backend/executor/spi.c
--- b/src/backend/executor/spi.c
***************
*** 1939,1944 **** _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
--- 1939,1954 ----
  					else
  						res = SPI_OK_UTILITY;
  				}
+ 				else if (IsA(stmt, CopyStmt))
+ 				{
+ 					/*
+ 					 * usually utility statements doesn't return a number
+ 					 * of processed rows, but COPY does it.
+ 					 */
+ 					Assert(strncmp(completionTag, "COPY  ", 5) == 0);
+ 					_SPI_current->processed = strtoul(completionTag + 5,
+ 													  NULL, 10);
+ 				}
  				else
  					res = SPI_OK_UTILITY;
  			}
*** a/src/test/regress/input/copy.source
--- b/src/test/regress/input/copy.source
***************
*** 106,108 **** this is just a line full of junk that would error out if parsed
--- 106,122 ----
  \.
  
  copy copytest3 to stdout csv header;
+ 
+ -- test of taking number of processed rows via SPI interface
+ do $$
+ declare r int;
+ begin
+   copy copytest2 to '@abs_builddir@/results/copytest.csv' csv;
+   get diagnostics r = row_count;
+   raise notice 'exported % rows', r;
+   truncate copytest2;
+   copy copytest2 from '@abs_builddir@/results/copytest.csv' csv;
+   get diagnostics r = row_count;
+   raise notice 'imported % rows', r;
+ end;
+ $$ language plpgsql;
*** a/src/test/regress/output/copy.source
--- b/src/test/regress/output/copy.source
***************
*** 71,73 **** copy copytest3 to stdout csv header;
--- 71,88 ----
  c1,"col with , comma","col with "" quote"
  1,a,1
  2,b,2
+ -- test of taking number of processed rows via SPI interface
+ do $$
+ declare r int;
+ begin
+   copy copytest2 to '@abs_builddir@/results/copytest.csv' csv;
+   get diagnostics r = row_count;
+   raise notice 'exported % rows', r;
+   truncate copytest2;
+   copy copytest2 from '@abs_builddir@/results/copytest.csv' csv;
+   get diagnostics r = row_count;
+   raise notice 'imported % rows', r;
+ end;
+ $$ language plpgsql;
+ NOTICE:  exported 4 rows
+ NOTICE:  imported 4 rows
