Hello
There is not possible to get a number of processed rows when COPY is
evaluated via SPI. Client can use a tag, but SPI doesn't use a tag.
I propose a small change a ProcessUtility to return a processed rows.
Note: I found a small inconsistency between SPI and Utility interface.
SPI still use a 4 byte unsign int for storing a number of processed
rows. Utility use a 8bytes unsign int.
Motivation:
postgres=# \sf fx
CREATE OR REPLACE FUNCTION public.fx(tablename text, filename text)
RETURNS integer
LANGUAGE plpgsql
AS $function$
declare r int;
begin
execute format('COPY %s FROM %s', quote_ident(tablename),
quote_literal(filename));
get diagnostics r = row_count;
return r;
end;
$function$
Regards
Pavel Stehule
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 398bc40..a7c2b8f 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -600,6 +600,7 @@ postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
es->qd->params,
false, /* not top level */
es->qd->dest,
+ NULL,
NULL);
result = true; /* never stops early */
}
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 688279c..21cabcc 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1838,6 +1838,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
{
Node *stmt = (Node *) lfirst(lc2);
bool canSetTag;
+ bool isCopyStmt = false;
DestReceiver *dest;
_SPI_current->processed = 0;
@@ -1857,6 +1858,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
{
CopyStmt *cstmt = (CopyStmt *) stmt;
+ isCopyStmt = true;
if (cstmt->filename == NULL)
{
my_res = SPI_ERROR_COPY;
@@ -1911,16 +1913,23 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
}
else
{
+ uint32 processed;
+
ProcessUtility(stmt,
plansource->query_string,
paramLI,
false, /* not top level */
dest,
- NULL);
+ NULL,
+ &processed);
/* Update "processed" if stmt returned tuples */
+
if (_SPI_current->tuptable)
_SPI_current->processed = _SPI_current->tuptable->alloced -
_SPI_current->tuptable->free;
+ else if (canSetTag && isCopyStmt)
+ _SPI_current->processed = processed;
+
res = SPI_OK_UTILITY;
}
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 466727b..1a861ee 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -1184,7 +1184,8 @@ PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
portal->portalParams,
isTopLevel,
dest,
- completionTag);
+ completionTag,
+ NULL);
/* Some utility statements may change context on us */
MemoryContextSwitchTo(PortalGetHeapMemory(portal));
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 0749227..35db28c 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -319,6 +319,9 @@ CheckRestrictedOperation(const char *cmdname)
* completionTag is only set nonempty if we want to return a nondefault status.
*
* completionTag may be NULL if caller doesn't want a status string.
+ *
+ * processed may be NULL if caller doesn't want a number of processed rows
+ * by COPY statement
*/
void
ProcessUtility(Node *parsetree,
@@ -326,7 +329,8 @@ ProcessUtility(Node *parsetree,
ParamListInfo params,
bool isTopLevel,
DestReceiver *dest,
- char *completionTag)
+ char *completionTag,
+ uint32 *processed)
{
Assert(queryString != NULL); /* required as of 8.4 */
@@ -337,10 +341,10 @@ ProcessUtility(Node *parsetree,
*/
if (ProcessUtility_hook)
(*ProcessUtility_hook) (parsetree, queryString, params,
- isTopLevel, dest, completionTag);
+ isTopLevel, dest, completionTag, processed);
else
standard_ProcessUtility(parsetree, queryString, params,
- isTopLevel, dest, completionTag);
+ isTopLevel, dest, completionTag, processed);
}
void
@@ -349,7 +353,8 @@ standard_ProcessUtility(Node *parsetree,
ParamListInfo params,
bool isTopLevel,
DestReceiver *dest,
- char *completionTag)
+ char *completionTag,
+ uint32 *processed)
{
check_xact_readonly(parsetree);
@@ -571,6 +576,7 @@ standard_ProcessUtility(Node *parsetree,
params,
false,
None_Receiver,
+ NULL,
NULL);
}
@@ -716,12 +722,14 @@ standard_ProcessUtility(Node *parsetree,
case T_CopyStmt:
{
- uint64 processed;
+ uint64 _processed;
- processed = DoCopy((CopyStmt *) parsetree, queryString);
+ _processed = DoCopy((CopyStmt *) parsetree, queryString);
if (completionTag)
snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
- "COPY " UINT64_FORMAT, processed);
+ "COPY " UINT64_FORMAT, _processed);
+ if (processed != NULL)
+ *processed = (uint32) _processed;
}
break;
@@ -782,6 +790,7 @@ standard_ProcessUtility(Node *parsetree,
params,
false,
None_Receiver,
+ NULL,
NULL);
}
diff --git a/src/include/tcop/utility.h b/src/include/tcop/utility.h
index c21857a..86fad4b 100644
--- a/src/include/tcop/utility.h
+++ b/src/include/tcop/utility.h
@@ -20,15 +20,16 @@
/* Hook for plugins to get control in ProcessUtility() */
typedef void (*ProcessUtility_hook_type) (Node *parsetree,
const char *queryString, ParamListInfo params, bool isTopLevel,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, char *completionTag,
+ uint32 *processed);
extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook;
extern void ProcessUtility(Node *parsetree, const char *queryString,
ParamListInfo params, bool isTopLevel,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, char *completionTag, uint32 *processed);
extern void standard_ProcessUtility(Node *parsetree, const char *queryString,
ParamListInfo params, bool isTopLevel,
- DestReceiver *dest, char *completionTag);
+ DestReceiver *dest, char *completionTag, uint32 *processed);
extern bool UtilityReturnsTuples(Node *parsetree);
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers