? src/Makefile.global ? src/cscope.out ? src/backend/cscope.out ? src/backend/postgres ? src/backend/catalog/postgres.bki ? src/backend/catalog/postgres.description ? src/backend/commands/cscope.out ? src/backend/executor/cscope.out ? src/backend/libpq/cscope.out ? src/backend/utils/mb/conversion_procs/conversion_create.sql ? src/backend/utils/mb/conversion_procs/ascii_and_mic/libascii_and_mic.so.0.0 ? src/backend/utils/mb/conversion_procs/cyrillic_and_mic/libcyrillic_and_mic.so.0.0 ? src/backend/utils/mb/conversion_procs/euc_cn_and_mic/libeuc_cn_and_mic.so.0.0 ? src/backend/utils/mb/conversion_procs/euc_jp_and_sjis/libeuc_jp_and_sjis.so.0.0 ? src/backend/utils/mb/conversion_procs/euc_kr_and_mic/libeuc_kr_and_mic.so.0.0 ? src/backend/utils/mb/conversion_procs/euc_tw_and_big5/libeuc_tw_and_big5.so.0.0 ? src/backend/utils/mb/conversion_procs/latin2_and_win1250/liblatin2_and_win1250.so.0.0 ? src/backend/utils/mb/conversion_procs/latin_and_mic/liblatin_and_mic.so.0.0 ? src/backend/utils/mb/conversion_procs/utf8_and_ascii/libutf8_and_ascii.so.0.0 ? src/backend/utils/mb/conversion_procs/utf8_and_big5/libutf8_and_big5.so.0.0 ? src/backend/utils/mb/conversion_procs/utf8_and_cyrillic/libutf8_and_cyrillic.so.0.0 ? src/backend/utils/mb/conversion_procs/utf8_and_euc_cn/libutf8_and_euc_cn.so.0.0 ? src/backend/utils/mb/conversion_procs/utf8_and_euc_jp/libutf8_and_euc_jp.so.0.0 ? src/backend/utils/mb/conversion_procs/utf8_and_euc_kr/libutf8_and_euc_kr.so.0.0 ? src/backend/utils/mb/conversion_procs/utf8_and_euc_tw/libutf8_and_euc_tw.so.0.0 ? src/backend/utils/mb/conversion_procs/utf8_and_gb18030/libutf8_and_gb18030.so.0.0 ? src/backend/utils/mb/conversion_procs/utf8_and_gbk/libutf8_and_gbk.so.0.0 ? src/backend/utils/mb/conversion_procs/utf8_and_iso8859/libutf8_and_iso8859.so.0.0 ? src/backend/utils/mb/conversion_procs/utf8_and_iso8859_1/libutf8_and_iso8859_1.so.0.0 ? src/backend/utils/mb/conversion_procs/utf8_and_johab/libutf8_and_johab.so.0.0 ? src/backend/utils/mb/conversion_procs/utf8_and_sjis/libutf8_and_sjis.so.0.0 ? src/backend/utils/mb/conversion_procs/utf8_and_uhc/libutf8_and_uhc.so.0.0 ? src/backend/utils/mb/conversion_procs/utf8_and_win1250/libutf8_and_win1250.so.0.0 ? src/backend/utils/mb/conversion_procs/utf8_and_win1252/libutf8_and_win1252.so.0.0 ? src/backend/utils/mb/conversion_procs/utf8_and_win1256/libutf8_and_win1256.so.0.0 ? src/backend/utils/mb/conversion_procs/utf8_and_win1258/libutf8_and_win1258.so.0.0 ? src/backend/utils/mb/conversion_procs/utf8_and_win874/libutf8_and_win874.so.0.0 ? src/bin/initdb/initdb ? src/bin/ipcclean/ipcclean ? src/bin/pg_config/pg_config ? src/bin/pg_controldata/pg_controldata ? src/bin/pg_ctl/pg_ctl ? src/bin/pg_dump/pg_dump ? src/bin/pg_dump/pg_dumpall ? src/bin/pg_dump/pg_restore ? src/bin/pg_resetxlog/pg_resetxlog ? src/bin/psql/cscope.out ? src/bin/psql/psql ? src/bin/scripts/clusterdb ? src/bin/scripts/createdb ? src/bin/scripts/createlang ? src/bin/scripts/createuser ? src/bin/scripts/dropdb ? src/bin/scripts/droplang ? src/bin/scripts/dropuser ? src/bin/scripts/reindexdb ? src/bin/scripts/vacuumdb ? src/include/pg_config.h ? src/include/stamp-h ? src/interfaces/ecpg/compatlib/libecpg_compat.so.2.2 ? src/interfaces/ecpg/ecpglib/libecpg.so.5.3 ? src/interfaces/ecpg/pgtypeslib/libpgtypes.so.2.2 ? src/interfaces/ecpg/preproc/ecpg ? src/interfaces/libpq/cscope.out ? src/interfaces/libpq/libpq.so.4.2 ? src/pl/plpgsql/src/libplpgsql.so.1.0 ? src/port/pg_config_paths.h ? src/test/regress/libregress.so.0.0 ? src/test/regress/pg_regress ? src/test/regress/expected/constraints.out ? src/test/regress/expected/copy.out ? src/test/regress/expected/create_function_1.out ? src/test/regress/expected/create_function_2.out ? src/test/regress/expected/misc.out ? src/test/regress/expected/tablespace.out ? src/test/regress/sql/constraints.sql ? src/test/regress/sql/copy.sql ? src/test/regress/sql/create_function_1.sql ? src/test/regress/sql/create_function_2.sql ? src/test/regress/sql/misc.sql ? src/test/regress/sql/tablespace.sql ? src/timezone/zic Index: src/backend/commands/copy.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/commands/copy.c,v retrieving revision 1.255 diff -u -c -r1.255 copy.c *** src/backend/commands/copy.c 22 Nov 2005 18:17:08 -0000 1.255 --- src/backend/commands/copy.c 18 Dec 2005 09:48:27 -0000 *************** *** 102,107 **** --- 102,108 ---- 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? */ + uint64 processed; /* # of tuples processed */ /* parameters from the COPY command */ Relation rel; /* relation to copy to or from */ *************** *** 646,652 **** * Do not allow the copy if user doesn't have proper permission to access * the table. */ ! void DoCopy(const CopyStmt *stmt) { CopyState cstate; --- 647,653 ---- * Do not allow the copy if user doesn't have proper permission to access * the table. */ ! uint64 DoCopy(const CopyStmt *stmt) { CopyState cstate; *************** *** 660,665 **** --- 661,667 ---- AclMode required_access = (is_from ? ACL_INSERT : ACL_SELECT); AclResult aclresult; ListCell *option; + uint64 processed; /* Allocate workspace and zero all fields */ cstate = (CopyStateData *) palloc0(sizeof(CopyStateData)); *************** *** 935,941 **** 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; /* Set up encoding conversion info */ cstate->client_encoding = pg_get_client_encoding(); --- 937,943 ---- 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 = cstate->processed = 0; /* Set up encoding conversion info */ cstate->client_encoding = pg_get_client_encoding(); *************** *** 1080,1086 **** --- 1082,1091 ---- pfree(cstate->attribute_buf.data); pfree(cstate->line_buf.data); pfree(cstate->raw_buf); + + processed = cstate->processed; pfree(cstate); + return processed; } *************** *** 1310,1315 **** --- 1315,1322 ---- VARSIZE(outputbytes) - VARHDRSZ); } } + + cstate->processed++; } CopySendEndOfRow(cstate); *************** *** 1916,1921 **** --- 1923,1930 ---- /* 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 -c -r1.250 utility.c *** src/backend/tcop/utility.c 29 Nov 2005 01:25:49 -0000 1.250 --- src/backend/tcop/utility.c 18 Dec 2005 09:48:29 -0000 *************** *** 640,646 **** break; case T_CopyStmt: ! DoCopy((CopyStmt *) parsetree); break; case T_PrepareStmt: --- 640,653 ---- break; case T_CopyStmt: ! { ! uint64 processed = DoCopy((CopyStmt *) parsetree); ! char buf[21]; ! ! snprintf(buf, sizeof(buf), UINT64_FORMAT, processed); ! snprintf(completionTag, COMPLETION_TAG_BUFSIZE, ! "COPY %s", buf); ! } break; case T_PrepareStmt: Index: src/bin/psql/common.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/bin/psql/common.c,v retrieving revision 1.111 diff -u -c -r1.111 common.c *** src/bin/psql/common.c 22 Nov 2005 18:17:29 -0000 1.111 --- src/bin/psql/common.c 18 Dec 2005 09:48:31 -0000 *************** *** 844,857 **** * Returns true if the query executed successfully, false otherwise. */ static bool ! ProcessCopyResult(PGresult *results) { ! bool success = false; ! if (!results) return false; ! switch (PQresultStatus(results)) { case PGRES_TUPLES_OK: case PGRES_COMMAND_OK: --- 844,859 ---- * Returns true if the query executed successfully, false otherwise. */ static bool ! ProcessCopyResult(PGresult **results) { ! bool success, req_getres; ! if (!*results) return false; ! success = req_getres = false; ! ! switch (PQresultStatus(*results)) { case PGRES_TUPLES_OK: case PGRES_COMMAND_OK: *************** *** 861,871 **** break; case PGRES_COPY_OUT: ! success = handleCopyOut(pset.db, pset.queryFout); break; case PGRES_COPY_IN: ! success = handleCopyIn(pset.db, pset.cur_cmd_source); break; default: --- 863,873 ---- break; case PGRES_COPY_OUT: ! req_getres = success = handleCopyOut(pset.db, pset.queryFout); break; case PGRES_COPY_IN: ! req_getres = success = handleCopyIn(pset.db, pset.cur_cmd_source); break; default: *************** *** 875,880 **** --- 877,894 ---- /* may need this to recover from conn loss during COPY */ if (!CheckConnection()) return false; + + /* + * We need a last PQgetResult() call to learn whether COPY + * succeded or not. + */ + if (req_getres) + { + PQclear(*results); + *results = PQgetResult(pset.db); + if (PQresultStatus(*results) != PGRES_COMMAND_OK) + success = false; + } return success; } *************** *** 1058,1065 **** results = PQexec(pset.db, query); /* these operations are included in the timing result: */ ! OK = (AcceptResult(results, query) && ProcessCopyResult(results)); ! if (pset.timing) GETTIMEOFDAY(&after); --- 1072,1079 ---- results = PQexec(pset.db, query); /* these operations are included in the timing result: */ ! OK = (AcceptResult(results, query) && ProcessCopyResult(&results)); ! if (pset.timing) GETTIMEOFDAY(&after); Index: src/bin/psql/copy.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/bin/psql/copy.c,v retrieving revision 1.58 diff -u -c -r1.58 copy.c *** src/bin/psql/copy.c 15 Oct 2005 02:49:40 -0000 1.58 --- src/bin/psql/copy.c 18 Dec 2005 09:48:32 -0000 *************** *** 585,670 **** return success; } ! #define COPYBUFSIZ 8192 /* size doesn't matter */ ! /* * handleCopyOut * receives data as a result of a COPY ... TO stdout command - * - * If you want to use COPY TO in your application, this is the code to steal :) - * - * conn should be a database connection that you just called COPY TO on - * (and which gave you PGRES_COPY_OUT back); - * copystream is the file stream you want the output to go to */ bool handleCopyOut(PGconn *conn, FILE *copystream) { ! bool copydone = false; /* haven't started yet */ ! char copybuf[COPYBUFSIZ]; ! int ret; ! while (!copydone) { ! ret = PQgetline(conn, copybuf, COPYBUFSIZ); ! if (copybuf[0] == '\\' && ! copybuf[1] == '.' && ! copybuf[2] == '\0') { ! copydone = true; /* we're at the end */ } ! else { ! fputs(copybuf, copystream); ! switch (ret) ! { ! case EOF: ! copydone = true; ! /* FALLTHROUGH */ ! case 0: ! fputc('\n', copystream); ! break; ! case 1: ! break; ! } } } fflush(copystream); - ret = !PQendcopy(conn); ResetCancelConn(); ! return ret; } - - /* * handleCopyIn * receives data as a result of a COPY ... FROM stdin command - * - * Again, if you want to use COPY FROM in your application, copy this. - * - * conn should be a database connection that you just called COPY FROM on - * (and which gave you PGRES_COPY_IN back); - * copystream is the file stream you want the input to come from */ - bool handleCopyIn(PGconn *conn, FILE *copystream) { ! const char *prompt; ! bool copydone = false; ! bool firstload; ! bool linedone; ! bool saw_cr = false; ! char copybuf[COPYBUFSIZ]; ! char *s; ! int bufleft; ! int c = 0; ! int ret; ! unsigned int linecount = 0; /* Prompt if interactive input */ if (isatty(fileno(copystream))) --- 585,659 ---- return success; } + /* + * Function routines for handling COPY IN/OUT input/output. + * + * If you want to use COPY TO in your application, this is the + * code to steal ;) + * + * conn should be a database connection that you just issued COPY FROM/TO + * and copystream is the file stream for input/output to read/go. + * + * PGRES_COPY_IN and PGRES_COPY_OUT results will be untouched, so you + * should make a PQgetResult() call at the end to learn whether COPY + * succeeded or not. (This will bring COPY command's commandTag string too.) + */ ! /* Buffer size for a single line during COPY IN/OUT */ ! #define COPYBUFSIZ 8192 /* * handleCopyOut * receives data as a result of a COPY ... TO stdout command */ bool handleCopyOut(PGconn *conn, FILE *copystream) { ! char *buf; ! int ret; ! bool res = true; ! for (;;) { ! ret = PQgetCopyData(conn, &buf, 0); ! /* Buffer is filled by libpq */ ! if (ret > 0) { ! fputs(buf, copystream); ! PQfreemem(buf); } ! ! /* copy done */ ! else if (ret == -1) ! break; ! ! /* oops */ ! else if (ret == -2) { ! res = false; ! break; } } + fflush(copystream); ResetCancelConn(); ! ! return res; } /* * handleCopyIn * receives data as a result of a COPY ... FROM stdin command */ bool handleCopyIn(PGconn *conn, FILE *copystream) { ! const char *prompt; ! char buf[COPYBUFSIZ]; ! unsigned int l; ! bool first, saw_cr; ! int ret; /* Prompt if interactive input */ if (isatty(fileno(copystream))) *************** *** 677,747 **** else prompt = NULL; ! while (!copydone) ! { /* for each input line ... */ if (prompt) { fputs(prompt, stdout); fflush(stdout); } ! firstload = true; ! linedone = false; ! while (!linedone) ! { /* for each bufferload in line ... */ ! /* Fetch string until \n, EOF, or buffer full */ ! s = copybuf; ! for (bufleft = COPYBUFSIZ - 1; bufleft > 0; bufleft--) ! { ! c = getc(copystream); ! if (c == EOF) ! { ! linedone = true; ! break; ! } ! *s++ = c; ! if (c == '\n') ! { ! linedone = true; ! break; ! } ! if (c == '\r') ! saw_cr = true; ! } ! *s = '\0'; ! /* EOF with empty line-so-far? */ ! if (c == EOF && s == copybuf && firstload) ! { ! /* ! * We are guessing a little bit as to the right line-ending ! * here... ! */ ! if (saw_cr) ! PQputline(conn, "\\.\r\n"); ! else ! PQputline(conn, "\\.\n"); ! copydone = true; ! if (pset.cur_cmd_interactive) ! puts("\\."); ! break; ! } ! /* No, so pass the data to the backend */ ! PQputline(conn, copybuf); ! /* Check for line consisting only of \. */ ! if (firstload) ! { ! if (strcmp(copybuf, "\\.\n") == 0 || ! strcmp(copybuf, "\\.\r\n") == 0) ! { ! copydone = true; ! break; ! } ! firstload = false; ! } } ! linecount++; } ! ret = !PQendcopy(conn); ! pset.lineno += linecount; ! return ret; } --- 666,714 ---- else prompt = NULL; ! saw_cr = false; ! first = true; ! for (;;) ! { if (prompt) { fputs(prompt, stdout); fflush(stdout); } ! fgets(buf, COPYBUFSIZ, copystream); ! l = strlen(buf); ! ! if (buf[0] == '\0') ! break; ! else if (strcmp(buf, "\\.\n") == 0 || strcmp(buf, "\\.\r\n") == 0) ! { ! if (buf[2] == '\r') ! saw_cr = true; ! break; ! } ! /* ! * \r will checked just in the first and ! * (as coded above) the last \. line ! */ ! if (first) ! { ! if (strchr(buf, '\r')) ! saw_cr = true; ! first = false; } ! ! if (PQputCopyData(conn, buf, l) < 0) ! return false; ! ! pset.lineno++; } ! ! ret = (saw_cr) ! ? PQputCopyData(conn, "\\.\r\n", 4) ! : PQputCopyData(conn, "\\.\n", 3); ! if (ret < 0) ! return false; ! ! return (PQputCopyEnd(conn, NULL) < 0) ? false : true; } Index: src/include/commands/copy.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/commands/copy.h,v retrieving revision 1.25 diff -u -c -r1.25 copy.h *** src/include/commands/copy.h 31 Dec 2004 22:03:28 -0000 1.25 --- src/include/commands/copy.h 18 Dec 2005 09:48:32 -0000 *************** *** 16,22 **** #include "nodes/parsenodes.h" ! ! extern void DoCopy(const CopyStmt *stmt); #endif /* COPY_H */ --- 16,21 ---- #include "nodes/parsenodes.h" ! extern uint64 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 -c -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 18 Dec 2005 09:48:36 -0000 *************** *** 2177,2183 **** char * PQcmdTuples(PGresult *res) { ! char *p; if (!res) return ""; --- 2177,2183 ---- char * PQcmdTuples(PGresult *res) { ! char *p, *c; if (!res) return ""; *************** *** 2195,2201 **** p = res->cmdStatus + 6; else if (strncmp(res->cmdStatus, "FETCH ", 6) == 0) p = res->cmdStatus + 5; ! else if (strncmp(res->cmdStatus, "MOVE ", 5) == 0) p = res->cmdStatus + 4; else return ""; --- 2195,2202 ---- p = res->cmdStatus + 6; else if (strncmp(res->cmdStatus, "FETCH ", 6) == 0) p = res->cmdStatus + 5; ! else if (strncmp(res->cmdStatus, "MOVE ", 5) == 0 || ! strncmp(res->cmdStatus, "COPY ", 5) == 0) p = res->cmdStatus + 4; else return ""; *************** *** 2203,2216 **** p++; if (*p == 0) ! { ! pqInternalNotice(&res->noticeHooks, ! "could not interpret result from server: %s", ! res->cmdStatus); ! return ""; ! } ! ! return p; } /* --- 2204,2222 ---- p++; if (*p == 0) ! goto interpret_error; ! ! /* check if we have an int */ ! for (c = p; isdigit((int) *c); ++c) ! ; ! if (*c == 0) ! return p; ! ! interpret_error: ! pqInternalNotice(&res->noticeHooks, ! "could not interpret result from server: %s", ! res->cmdStatus); ! return ""; } /* Index: doc/src/sgml/libpq.sgml =================================================================== RCS file: /projects/cvsroot/pgsql/doc/src/sgml/libpq.sgml,v retrieving revision 1.199 diff -u -c -r1.199 libpq.sgml *** doc/src/sgml/libpq.sgml 4 Nov 2005 23:14:00 -0000 1.199 --- doc/src/sgml/libpq.sgml 18 Dec 2005 09:49:00 -0000 *************** *** 2129,2144 **** affected by the SQL statement that generated the PGresult. This function can only be used following the execution of an INSERT, ! UPDATE, DELETE, MOVE, or ! FETCH statement, or an EXECUTE of a ! prepared query that contains a INSERT, ! UPDATE, or DELETE statement. If the ! command that generated the PGresult was ! anything else, PQcmdTuples returns the empty ! string. The caller should not free the return value ! directly. It will be freed when the associated ! PGresult handle is passed to ! PQclear. --- 2129,2143 ---- affected by the SQL statement that generated the PGresult. This function can only be used following the execution of an INSERT, ! UPDATE, DELETE, MOVE, ! FETCH, or COPY statement, or an ! EXECUTE of a prepared query that contains a ! INSERT, UPDATE, DELETE, or ! COPY statement. If the command that generated the ! PGresult was anything else, PQcmdTuples ! returns the empty string. The caller should not free the return ! value directly. It will be freed when the associated ! PGresult handle is passed to PQclear.