ToDo: allow to get a number of processed rows by COPY statement
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
Attachments:
copy_processed_rows.difftext/x-patch; charset=US-ASCII; name=copy_processed_rows.diffDownload
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);
Pavel Stehule <pavel.stehule@gmail.com> wrote:
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.
Please add this to the open CommitFest:
https://commitfest.postgresql.org/action/commitfest_view/open
-Kevin
What ever happened to this patch? I don't see it on any of the
commit-fests, though someone was asked for it to be added:
http://archives.postgresql.org/pgsql-hackers/2011-10/msg00381.php
---------------------------------------------------------------------------
On Tue, Oct 4, 2011 at 12:22:19PM +0200, Pavel Stehule wrote:
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 (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ It's impossible for everything to be true. +
Hello
I can reassign it again
Regards
Pavel
2012/8/16 Bruce Momjian <bruce@momjian.us>:
Show quoted text
What ever happened to this patch? I don't see it on any of the
commit-fests, though someone was asked for it to be added:http://archives.postgresql.org/pgsql-hackers/2011-10/msg00381.php
---------------------------------------------------------------------------
On Tue, Oct 4, 2011 at 12:22:19PM +0200, Pavel Stehule wrote:
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 (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com+ It's impossible for everything to be true. +
Hello
here is updated patch
postgres=# copy omega to '/tmp/xxx';
COPY 60
postgres=# do $$ declare r int;
begin
copy omega from '/tmp/xxx'; get diagnostics r = row_count; raise
notice '>>> %', r;
end;
$$ language plpgsql;
NOTICE: >>> 60
DO
Regards
Pavel
2012/8/16 Bruce Momjian <bruce@momjian.us>:
Show quoted text
What ever happened to this patch? I don't see it on any of the
commit-fests, though someone was asked for it to be added:http://archives.postgresql.org/pgsql-hackers/2011-10/msg00381.php
---------------------------------------------------------------------------
On Tue, Oct 4, 2011 at 12:22:19PM +0200, Pavel Stehule wrote:
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 (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com+ It's impossible for everything to be true. +
Attachments:
copy-processed.diffapplication/octet-stream; name=copy-processed.diffDownload
*** a/src/backend/commands/extension.c
--- b/src/backend/commands/extension.c
***************
*** 751,757 **** execute_sql_string(const char *sql, const char *filename)
NULL,
dest,
NULL,
! PROCESS_UTILITY_QUERY);
}
PopActiveSnapshot();
--- 751,758 ----
NULL,
dest,
NULL,
! PROCESS_UTILITY_QUERY,
! NULL);
}
PopActiveSnapshot();
*** a/src/backend/commands/schemacmds.c
--- b/src/backend/commands/schemacmds.c
***************
*** 134,140 **** CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
NULL,
None_Receiver,
NULL,
! PROCESS_UTILITY_SUBCOMMAND);
/* make sure later steps can see the object created here */
CommandCounterIncrement();
}
--- 134,141 ----
NULL,
None_Receiver,
NULL,
! PROCESS_UTILITY_SUBCOMMAND,
! NULL);
/* make sure later steps can see the object created here */
CommandCounterIncrement();
}
*** a/src/backend/commands/trigger.c
--- b/src/backend/commands/trigger.c
***************
*** 1011,1017 **** ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
/* ... and execute it */
ProcessUtility((Node *) atstmt,
"(generated ALTER TABLE ADD FOREIGN KEY command)",
! NULL, None_Receiver, NULL, PROCESS_UTILITY_GENERATED);
/* Remove the matched item from the list */
info_list = list_delete_ptr(info_list, info);
--- 1011,1018 ----
/* ... and execute it */
ProcessUtility((Node *) atstmt,
"(generated ALTER TABLE ADD FOREIGN KEY command)",
! NULL, None_Receiver, NULL, PROCESS_UTILITY_GENERATED,
! NULL);
/* Remove the matched item from the list */
info_list = list_delete_ptr(info_list, info);
*** a/src/backend/executor/functions.c
--- b/src/backend/executor/functions.c
***************
*** 785,791 **** postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
es->qd->params,
es->qd->dest,
NULL,
! PROCESS_UTILITY_QUERY);
result = true; /* never stops early */
}
else
--- 785,792 ----
es->qd->params,
es->qd->dest,
NULL,
! PROCESS_UTILITY_QUERY,
! NULL);
result = true; /* never stops early */
}
else
*** a/src/backend/executor/spi.c
--- b/src/backend/executor/spi.c
***************
*** 1908,1920 **** _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
else
{
char completionTag[COMPLETION_TAG_BUFSIZE];
ProcessUtility(stmt,
plansource->query_string,
paramLI,
dest,
completionTag,
! PROCESS_UTILITY_QUERY);
/* Update "processed" if stmt returned tuples */
if (_SPI_current->tuptable)
--- 1908,1924 ----
else
{
char completionTag[COMPLETION_TAG_BUFSIZE];
+ uint64 processed;
ProcessUtility(stmt,
plansource->query_string,
paramLI,
dest,
completionTag,
! PROCESS_UTILITY_QUERY,
! &processed);
!
! _SPI_current->processed = (uint32) processed;
/* Update "processed" if stmt returned tuples */
if (_SPI_current->tuptable)
*** a/src/backend/tcop/pquery.c
--- b/src/backend/tcop/pquery.c
***************
*** 44,53 **** static uint32 RunFromStore(Portal portal, ScanDirection direction, long count,
static long PortalRunSelect(Portal portal, bool forward, long count,
DestReceiver *dest);
static void PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
! DestReceiver *dest, char *completionTag);
static void PortalRunMulti(Portal portal, bool isTopLevel,
DestReceiver *dest, DestReceiver *altdest,
! char *completionTag);
static long DoPortalRunFetch(Portal portal,
FetchDirection fdirection,
long count,
--- 44,54 ----
static long PortalRunSelect(Portal portal, bool forward, long count,
DestReceiver *dest);
static void PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
! DestReceiver *dest, char *completionTag,
! uint64 *processed);
static void PortalRunMulti(Portal portal, bool isTopLevel,
DestReceiver *dest, DestReceiver *altdest,
! char *completionTag, uint64 *processed);
static long DoPortalRunFetch(Portal portal,
FetchDirection fdirection,
long count,
***************
*** 813,819 **** PortalRun(Portal portal, long count, bool isTopLevel,
case PORTAL_MULTI_QUERY:
PortalRunMulti(portal, isTopLevel,
! dest, altdest, completionTag);
/* Prevent portal's commands from being re-executed */
MarkPortalDone(portal);
--- 814,821 ----
case PORTAL_MULTI_QUERY:
PortalRunMulti(portal, isTopLevel,
! dest, altdest, completionTag,
! &portal->processed);
/* Prevent portal's commands from being re-executed */
MarkPortalDone(portal);
***************
*** 1053,1064 **** FillPortalStore(Portal portal, bool isTopLevel)
* tuplestore. Auxiliary query outputs are discarded.
*/
PortalRunMulti(portal, isTopLevel,
! treceiver, None_Receiver, completionTag);
break;
case PORTAL_UTIL_SELECT:
PortalRunUtility(portal, (Node *) linitial(portal->stmts),
! isTopLevel, treceiver, completionTag);
break;
default:
--- 1055,1068 ----
* tuplestore. Auxiliary query outputs are discarded.
*/
PortalRunMulti(portal, isTopLevel,
! treceiver, None_Receiver, completionTag,
! &portal->processed);
break;
case PORTAL_UTIL_SELECT:
PortalRunUtility(portal, (Node *) linitial(portal->stmts),
! isTopLevel, treceiver, completionTag,
! &portal->processed);
break;
default:
***************
*** 1148,1154 **** RunFromStore(Portal portal, ScanDirection direction, long count,
*/
static void
PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
! DestReceiver *dest, char *completionTag)
{
bool active_snapshot_set;
--- 1152,1159 ----
*/
static void
PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
! DestReceiver *dest, char *completionTag,
! uint64 *processed)
{
bool active_snapshot_set;
***************
*** 1189,1195 **** PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
dest,
completionTag,
isTopLevel ?
! PROCESS_UTILITY_TOPLEVEL : PROCESS_UTILITY_QUERY);
/* Some utility statements may change context on us */
MemoryContextSwitchTo(PortalGetHeapMemory(portal));
--- 1194,1201 ----
dest,
completionTag,
isTopLevel ?
! PROCESS_UTILITY_TOPLEVEL : PROCESS_UTILITY_QUERY,
! processed);
/* Some utility statements may change context on us */
MemoryContextSwitchTo(PortalGetHeapMemory(portal));
***************
*** 1213,1219 **** PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
static void
PortalRunMulti(Portal portal, bool isTopLevel,
DestReceiver *dest, DestReceiver *altdest,
! char *completionTag)
{
bool active_snapshot_set = false;
ListCell *stmtlist_item;
--- 1219,1226 ----
static void
PortalRunMulti(Portal portal, bool isTopLevel,
DestReceiver *dest, DestReceiver *altdest,
! char *completionTag,
! uint64 *processed)
{
bool active_snapshot_set = false;
ListCell *stmtlist_item;
***************
*** 1316,1329 **** PortalRunMulti(Portal portal, bool isTopLevel,
Assert(!active_snapshot_set);
/* statement can set tag string */
PortalRunUtility(portal, stmt, isTopLevel,
! dest, completionTag);
}
else
{
Assert(IsA(stmt, NotifyStmt));
/* stmt added by rewrite cannot set tag */
PortalRunUtility(portal, stmt, isTopLevel,
! altdest, NULL);
}
}
--- 1323,1338 ----
Assert(!active_snapshot_set);
/* statement can set tag string */
PortalRunUtility(portal, stmt, isTopLevel,
! dest, completionTag,
! processed);
}
else
{
Assert(IsA(stmt, NotifyStmt));
/* stmt added by rewrite cannot set tag */
PortalRunUtility(portal, stmt, isTopLevel,
! altdest, NULL,
! processed);
}
}
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 322,328 **** ProcessUtility(Node *parsetree,
ParamListInfo params,
DestReceiver *dest,
char *completionTag,
! ProcessUtilityContext context)
{
Assert(queryString != NULL); /* required as of 8.4 */
--- 322,329 ----
ParamListInfo params,
DestReceiver *dest,
char *completionTag,
! ProcessUtilityContext context,
! uint64 *processed)
{
Assert(queryString != NULL); /* required as of 8.4 */
***************
*** 333,342 **** ProcessUtility(Node *parsetree,
*/
if (ProcessUtility_hook)
(*ProcessUtility_hook) (parsetree, queryString, params,
! dest, completionTag, context);
else
standard_ProcessUtility(parsetree, queryString, params,
! dest, completionTag, context);
}
void
--- 334,345 ----
*/
if (ProcessUtility_hook)
(*ProcessUtility_hook) (parsetree, queryString, params,
! dest, completionTag, context,
! processed);
else
standard_ProcessUtility(parsetree, queryString, params,
! dest, completionTag, context,
! processed);
}
void
***************
*** 345,351 **** standard_ProcessUtility(Node *parsetree,
ParamListInfo params,
DestReceiver *dest,
char *completionTag,
! ProcessUtilityContext context)
{
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
bool isCompleteQuery = (context <= PROCESS_UTILITY_QUERY);
--- 348,355 ----
ParamListInfo params,
DestReceiver *dest,
char *completionTag,
! ProcessUtilityContext context,
! uint64 *processed)
{
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
bool isCompleteQuery = (context <= PROCESS_UTILITY_QUERY);
***************
*** 355,360 **** standard_ProcessUtility(Node *parsetree,
--- 359,367 ----
if (completionTag)
completionTag[0] = '\0';
+ if (processed != NULL)
+ *processed = 0;
+
switch (nodeTag(parsetree))
{
/*
***************
*** 575,581 **** standard_ProcessUtility(Node *parsetree,
params,
None_Receiver,
NULL,
! PROCESS_UTILITY_GENERATED);
}
/* Need CCI between commands */
--- 582,589 ----
params,
None_Receiver,
NULL,
! PROCESS_UTILITY_GENERATED,
! processed);
}
/* Need CCI between commands */
***************
*** 705,716 **** standard_ProcessUtility(Node *parsetree,
case T_CopyStmt:
{
! uint64 processed;
! processed = DoCopy((CopyStmt *) parsetree, queryString);
if (completionTag)
snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
! "COPY " UINT64_FORMAT, processed);
}
break;
--- 713,727 ----
case T_CopyStmt:
{
! uint64 processed_rows;
! processed_rows = DoCopy((CopyStmt *) parsetree, queryString);
if (completionTag)
snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
! "COPY " UINT64_FORMAT, processed_rows);
!
! if (processed != NULL)
! *processed = processed_rows;
}
break;
***************
*** 812,818 **** standard_ProcessUtility(Node *parsetree,
params,
None_Receiver,
NULL,
! PROCESS_UTILITY_GENERATED);
}
/* Need CCI between commands */
--- 823,830 ----
params,
None_Receiver,
NULL,
! PROCESS_UTILITY_GENERATED,
! processed);
}
/* Need CCI between commands */
*** a/src/include/tcop/utility.h
--- b/src/include/tcop/utility.h
***************
*** 28,42 **** typedef enum
typedef void (*ProcessUtility_hook_type) (Node *parsetree,
const char *queryString, ParamListInfo params,
DestReceiver *dest, char *completionTag,
! ProcessUtilityContext context);
extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook;
extern void ProcessUtility(Node *parsetree, const char *queryString,
ParamListInfo params, DestReceiver *dest, char *completionTag,
! ProcessUtilityContext context);
extern void standard_ProcessUtility(Node *parsetree, const char *queryString,
ParamListInfo params, DestReceiver *dest,
! char *completionTag, ProcessUtilityContext context);
extern bool UtilityReturnsTuples(Node *parsetree);
--- 28,45 ----
typedef void (*ProcessUtility_hook_type) (Node *parsetree,
const char *queryString, ParamListInfo params,
DestReceiver *dest, char *completionTag,
! ProcessUtilityContext context,
! uint64 *processed);
extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook;
extern void ProcessUtility(Node *parsetree, const char *queryString,
ParamListInfo params, DestReceiver *dest, char *completionTag,
! ProcessUtilityContext context,
! uint64 *processed);
extern void standard_ProcessUtility(Node *parsetree, const char *queryString,
ParamListInfo params, DestReceiver *dest,
! char *completionTag, ProcessUtilityContext context,
! uint64 *processed);
extern bool UtilityReturnsTuples(Node *parsetree);
*** a/src/include/utils/portal.h
--- b/src/include/utils/portal.h
***************
*** 130,135 **** typedef struct PortalData
--- 130,136 ----
const char *commandTag; /* command tag for original query */
List *stmts; /* PlannedStmts and/or utility statements */
CachedPlan *cplan; /* CachedPlan, if stmts are from one */
+ uint64 processed; /* returned rows */
ParamListInfo portalParams; /* params to pass to query */
*** 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,112 ----
\.
copy copytest3 to stdout csv header;
+
+ -- copy should to return processed rows
+ do $$
+
On 16.08.2012 14:43, Pavel Stehule wrote:
Hello
here is updated patch
The patch seems to be truncated, it ends with:
*** 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,112 ----
\.
copy copytest3 to stdout csv header;
+
+ -- copy should to return processed rows
+ do $$
+
I believe the code changes are OK, but regression test changes are
missing. Can you resend the full patch, please?
- Heikki
On 16.08.2012 14:43, Pavel Stehule wrote:
Hello
here is updated patch
[Review of Patch]
Basic stuff:
------------
- Patch applies OK. but offset difference in line numbers.
- Compiles with errors in contrib [pg_stat_statements, sepgsql] modules
- Regression failed; one test-case in COPY due to incomplete test-case
attached patch. same as reported by Heikki
Details of compilation errors and regression failure:
------------------------------------------------------
PATH : postgres/src/contrib/pg_stat_statements
gcc -g -ggdb3 -Wall -Wmissing-prototypes -Wpointer-arith
-Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute
-Wformat-security -fno-strict-aliasing -fwrapv -fpic -I. -I.
-I../../src/include -D_GNU_SOURCE -c -o pg_stat_statements.o
pg_stat_statements.c
pg_stat_statements.c: In function â_PG_initâ:
pg_stat_statements.c:363: warning: assignment from incompatible pointer type
pg_stat_statements.c: In function âpgss_ProcessUtilityâ:
pg_stat_statements.c:823: error: too few arguments to function
âprev_ProcessUtilityâ
pg_stat_statements.c:826: error: too few arguments to function
âstandard_ProcessUtilityâ
pg_stat_statements.c:884: error: too few arguments to function
âprev_ProcessUtilityâ
pg_stat_statements.c:887: error: too few arguments to function
âstandard_ProcessUtilityâ
make: *** [pg_stat_statements.o] Error 1
PATH : postgres/src/contrib/sepgsql
gcc -g -ggdb3 -Wall -Wmissing-prototypes -Wpointer-arith
-Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute
-Wformat-security -fno-strict-aliasing -fwrapv -fpic -I. -I.
-I../../src/include -D_GNU_SOURCE -c -o hooks.o hooks.c
In file included from hooks.c:26:
sepgsql.h:17:29: error: selinux/selinux.h: No such file or directory
sepgsql.h:18:25: error: selinux/avc.h: No such file or directory
In file included from hooks.c:26:
sepgsql.h:239: warning: âstruct av_decisionâ declared inside parameter list
sepgsql.h:239: warning: its scope is only this definition or declaration,
which is probably not what you want
hooks.c: In function âsepgsql_utility_commandâ:
hooks.c:331: error: too few arguments to function ânext_ProcessUtility_hookâ
hooks.c:334: error: too few arguments to function âstandard_ProcessUtilityâ
hooks.c: In function â_PG_initâ:
hooks.c:365: warning: implicit declaration of function âis_selinux_enabledâ
hooks.c:426: warning: assignment from incompatible pointer type
make: *** [hooks.o] Error 1
REGRESSION TEST:
------------------
make installcheck
test copy ... FAILED
========================
1 of 132 tests failed.
========================
~/postgres/src/test/regress> cat regression.diffs
*** /home/postgres/code/src/test/regress/expected/copy.out 2012-09-18
19:57:02.000000000 +0530
--- /home/postgres/code/src/test/regress/results/copy.out 2012-09-18
19:57:18.000000000 +0530
***************
*** 71,73 ****
--- 71,80 ----
c1,"col with , comma","col with "" quote"
1,a,1
2,b,2
+ -- copy should to return processed rows
+ do $$
+
+ ERROR: unterminated dollar-quoted string at or near "$$
+ "
+ LINE 1: do $$
+ ^
What it does:
--------------
Modification to get the number of processed rows evaluated via SPI. The
changes are to add extra parameter in ProcessUtility to get the number of
rows processed by COPY command.
Code Review Comments:
---------------------
1. New parameter is added to ProcessUtility_hook_type function
but the functions which get assigned to these functions like
sepgsql_utility_command, pgss_ProcessUtility, prototype & definition is
not modified.
2. Why to add the new parameter if completionTag hold the number of
processed tuple information; can be extracted
from it as follows:
_SPI_current->processed = strtoul(completionTag + 7,
NULL, 10);
3. Why new variable added in portal [portal->processed] this is not used in
code ?
Testing:
------------
The following test is carried out on the patch.
1. Normal SQL command COPY FROM / TO is tested.
2. SQL command COPY FROM / TO is tested from plpgsql.
Test cases are attached in Testcases_Copy_Processed.txt
With Regards,
Amit Kapila.
Attachments:
Import Notes
Resolved by subject fallback