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