*** a/doc/src/sgml/spi.sgml --- b/doc/src/sgml/spi.sgml *************** *** 397,403 **** typedef struct tupdesc is a row descriptor which you can pass to SPI functions dealing with rows. tuptabcxt, alloced, and free are internal ! fields not intended for use by SPI callers. --- 397,406 ---- tupdesc is a row descriptor which you can pass to SPI functions dealing with rows. tuptabcxt, alloced, and free are internal ! fields not intended for use by SPI callers. SPI_processed ! can be non zero, althoug SPI_tuptable is NULL. It is ! possible when CREATE TABLE AS SELECT or COPY ! statements was executed. *** a/src/backend/executor/spi.c --- b/src/backend/executor/spi.c *************** *** 1922,1934 **** _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI, _SPI_current->processed = _SPI_current->tuptable->alloced - _SPI_current->tuptable->free; ! /* ! * CREATE TABLE AS is a messy special case for historical ! * reasons. We must set _SPI_current->processed even though ! * the tuples weren't returned to the caller, and we must ! * return a special result code if the statement was spelled ! * SELECT INTO. ! */ if (IsA(stmt, CreateTableAsStmt)) { Assert(strncmp(completionTag, "SELECT ", 7) == 0); --- 1922,1928 ---- _SPI_current->processed = _SPI_current->tuptable->alloced - _SPI_current->tuptable->free; ! /* Update "processed" when stmt doesn't returns tuples */ if (IsA(stmt, CreateTableAsStmt)) { Assert(strncmp(completionTag, "SELECT ", 7) == 0); *************** *** 1939,1944 **** _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI, --- 1933,1949 ---- 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); + res = SPI_OK_UTILITY; + } 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