I prepared a patch for "Have COPY return the number of rows
loaded/unloaded?" TODO. (Sorry for disturbing list with such a simple
topic, but per warning from Bruce Momjian, I send my proposal to -hackers
first.)

I used the "appending related information to commandTag" method which is
used for INSERT/UPDATE/DELETE/FETCH commands too. Furthermore, I edited
libpq to make PQcmdTuples() interpret affected rows from cmdStatus value
for COPY command. (Changes don't cause any compatibility problems for API
and seems like work with triggers too.)

One of the problems related with the used concept is trying to encapsulate
processed number of rows within an uint32 variable. This causes an internal
limit for counting COPY when we think it can process billions of rows. I
couldn't find a solution for this. (Maybe, two uint32 can be used to store
row count.) But other processed row counters (like INSERT/UPDATE) uses
uint32 too.

What's your suggestions and comments?


Regards.
Index: src/backend/commands/copy.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/commands/copy.c,v
retrieving revision 1.255
diff -u -r1.255 copy.c
--- src/backend/commands/copy.c 22 Nov 2005 18:17:08 -0000      1.255
+++ src/backend/commands/copy.c 12 Dec 2005 17:18:44 -0000
@@ -102,6 +102,7 @@
        int                     client_encoding;        /* remote side's 
character encoding */
        bool            need_transcoding;               /* client encoding diff 
from server? */
        bool            client_only_encoding;   /* encoding not valid on 
server? */
+       uint32          processed;                              /* # of tuples 
processed */
 
        /* parameters from the COPY command */
        Relation        rel;                    /* relation to copy to or from 
*/
@@ -646,7 +647,7 @@
  * Do not allow the copy if user doesn't have proper permission to access
  * the table.
  */
-void
+uint32
 DoCopy(const CopyStmt *stmt)
 {
        CopyState       cstate;
@@ -660,6 +661,7 @@
        AclMode         required_access = (is_from ? ACL_INSERT : ACL_SELECT);
        AclResult       aclresult;
        ListCell   *option;
+       uint32          processed;
 
        /* Allocate workspace and zero all fields */
        cstate = (CopyStateData *) palloc0(sizeof(CopyStateData));
@@ -935,7 +937,7 @@
        initStringInfo(&cstate->line_buf);
        cstate->line_buf_converted = false;
        cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
-       cstate->raw_buf_index = cstate->raw_buf_len = 0;
+       cstate->raw_buf_index = cstate->raw_buf_len = cstate->processed = 0;
 
        /* Set up encoding conversion info */
        cstate->client_encoding = pg_get_client_encoding();
@@ -1080,7 +1082,10 @@
        pfree(cstate->attribute_buf.data);
        pfree(cstate->line_buf.data);
        pfree(cstate->raw_buf);
+
+       processed = cstate->processed;
        pfree(cstate);
+       return processed;
 }
 
 
@@ -1310,6 +1315,8 @@
                                                                 
VARSIZE(outputbytes) - VARHDRSZ);
                                }
                        }
+
+                       cstate->processed++;
                }
 
                CopySendEndOfRow(cstate);
@@ -1916,6 +1923,8 @@
 
                        /* AFTER ROW INSERT Triggers */
                        ExecARInsertTriggers(estate, resultRelInfo, tuple);
+
+                       cstate->processed++;
                }
        }
 
Index: src/backend/tcop/utility.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/tcop/utility.c,v
retrieving revision 1.250
diff -u -r1.250 utility.c
--- src/backend/tcop/utility.c  29 Nov 2005 01:25:49 -0000      1.250
+++ src/backend/tcop/utility.c  12 Dec 2005 17:18:45 -0000
@@ -640,7 +640,12 @@
                        break;
 
                case T_CopyStmt:
-                       DoCopy((CopyStmt *) parsetree);
+                       {
+                               uint32  processed = DoCopy((CopyStmt *) 
parsetree);
+                               
+                               snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
+                                                "COPY %u", processed);
+                       }
                        break;
 
                case T_PrepareStmt:
Index: src/include/commands/copy.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/commands/copy.h,v
retrieving revision 1.25
diff -u -r1.25 copy.h
--- src/include/commands/copy.h 31 Dec 2004 22:03:28 -0000      1.25
+++ src/include/commands/copy.h 12 Dec 2005 17:19:07 -0000
@@ -17,6 +17,6 @@
 #include "nodes/parsenodes.h"
 
 
-extern void DoCopy(const CopyStmt *stmt);
+extern uint32 DoCopy(const CopyStmt *stmt);
 
 #endif   /* COPY_H */
Index: src/interfaces/libpq/fe-exec.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v
retrieving revision 1.177
diff -u -r1.177 fe-exec.c
--- src/interfaces/libpq/fe-exec.c      22 Nov 2005 18:17:32 -0000      1.177
+++ src/interfaces/libpq/fe-exec.c      12 Dec 2005 17:18:48 -0000
@@ -2177,7 +2177,7 @@
 char *
 PQcmdTuples(PGresult *res)
 {
-       char       *p;
+       char       *p, *c;
 
        if (!res)
                return "";
@@ -2195,7 +2195,8 @@
                p = res->cmdStatus + 6;
        else if (strncmp(res->cmdStatus, "FETCH ", 6) == 0)
                p = res->cmdStatus + 5;
-       else if (strncmp(res->cmdStatus, "MOVE ", 5) == 0)
+       else if (strncmp(res->cmdStatus, "MOVE ", 5) == 0 ||
+                        strncmp(res->cmdStatus, "COPY ", 5) == 0)
                p = res->cmdStatus + 4;
        else
                return "";
@@ -2203,14 +2204,19 @@
        p++;
 
        if (*p == 0)
-       {
-               pqInternalNotice(&res->noticeHooks,
-                                                "could not interpret result 
from server: %s",
-                                                res->cmdStatus);
-               return "";
-       }
+               goto error;
 
-       return p;
+       /* check if we have an int */
+       for (c = p; isdigit((int) *c); ++c)
+               ;
+       if (*c == 0)
+               return p;
+
+error:
+       pqInternalNotice(&res->noticeHooks,
+                                        "could not interpret result from 
server: %s",
+                                        res->cmdStatus);
+       return "";
 }
 
 /*
---------------------------(end of broadcast)---------------------------
TIP 9: In versions below 8.0, the planner will ignore your desire to
       choose an index scan if your joining column's datatypes do not
       match

Reply via email to