Обсуждение: pl/pgSQL, get diagnostics and big data

Поиск
Список
Период
Сортировка

pl/pgSQL, get diagnostics and big data

От
Andreas 'ads' Scherbaum
Дата:
Hello,

one of our customers approached us and complained, that GET DIAGNOSTICS
row_count returns invalid results if the number of rows is > 2^31. It's
a bit complicated to test for this case, so I set up a separate instance
with this patch, and inserted 2^32+x rows into a table. Internally,
row_count it's a signed integer, the resulting number is negative:

diagnostics=# select testfunc_pg((2^31 + 50000)::bigint);
  testfunc_pg
-------------
  -2147433648
(1 row)


Going over 2^32 wraps around:

diagnostics=# select testfunc_pg((2^32 + 50000)::bigint);
  testfunc_pg
-------------
        50000
(1 row)



Attached patch expands the row_count to 64 bit.

diagnostics=# select testfunc_pg((2^32 + 50000)::bigint);
  testfunc_pg
-------------
   4295017296
(1 row)


I hope, I covered all the places which count the result set.


Regards,

--
                Andreas 'ads' Scherbaum
German PostgreSQL User Group
European PostgreSQL User Group - Board of Directors
Volunteer Regional Contact, Germany - PostgreSQL Project

Вложения

Re: pl/pgSQL, get diagnostics and big data

От
Andreas 'ads' Scherbaum
Дата:
On 01.02.2016 21:24, Andreas 'ads' Scherbaum wrote:
>
> Attached patch expands the row_count to 64 bit.

Remembering an issue we had recently with clear text patches, attached
is a gzipped version as well.


Regards,

--
                Andreas 'ads' Scherbaum
German PostgreSQL User Group
European PostgreSQL User Group - Board of Directors
Volunteer Regional Contact, Germany - PostgreSQL Project

Вложения

Re: pl/pgSQL, get diagnostics and big data

От
Christian Ullrich
Дата:
* Andreas 'ads' Scherbaum wrote:

> one of our customers approached us and complained, that GET DIAGNOSTICS
> row_count returns invalid results if the number of rows is > 2^31. It's

> Attached patch expands the row_count to 64 bit.
>
> diagnostics=# select testfunc_pg((2^32 + 50000)::bigint);
>   testfunc_pg
> -------------
>    4295017296
> (1 row)

This is my first patch review, but I have to get my feet wet at some 
point, and this is a nice, small patch to do that.

Following the checklist from the wiki:

- Is the patch in context format: Yes.

- Does it apply cleanly to master: Yes.

- Does it include reasonable tests, doc patches, etc.: No. While  it would be nice if it had some, a test that inserts
2^32rows  will take a while and can hardly be called reasonable.
 


The patch is designed to expand the size of the "affected records" count 
in the command tag from 32 to 64 bits.

- Does it do that: Yes.

- Do we want that: Yes, because it is motivated by reports from users  who have queries like that in real life.

- Do we already have it: No.

- Does it follow SQL spec or community-agreed behavior: This is not  covered by the SQL standard and there has not, to
myknowledge, been  any discussion on this point on -hackers. It is, however, the  obvious approach to solving the
specificissue.
 

- Does it include pg_dump support: n/a

- Are there dangers: Existing applications and client libraries must  support the increased maximum size (up to nine
additionaldigits) and  maximum value. libpq apparently does not parse the command tag, only  stores it as a string for
retrievalby PQcmdStatus(), so it is not  affected in terms of parsing the value, and for storage, it uses a
64-characterbuffer, which will overflow if the command name part of  the tag exceeds 32 characters (63 - 19 [row count]
-10 [OID] - 2  [spaces]). The longest command name I can think of is "REFRESH  MATERIALIZED VIEW" which, at 25
characters,stays comfortably below  this limit, and does not include a row count anyway.
 

- Have all the bases been covered: The patch changes all locations  where the command tag is formatted, and where the
recordcount is  retrieved by PL/pgSQL.
 

- Does the patch follow the coding guidelines: I believe so.

- Are there portability issues/Will it work on Windows/BSD etc.:
  No, it will not work correctly on Windows when built with MSVC,  although it may work with MinGW.
  +++ postgresql-9.5.0/src/backend/tcop/pquery.c  @@ -195,7 +195,7 @@   {     case CMD_SELECT:
snprintf(completionTag,COMPLETION_TAG_BUFSIZE,  -             "SELECT %u", queryDesc->estate->es_processed);  +
   "SELECT %lu", queryDesc->estate->es_processed);
 

  %lu formats unsigned long. "long" is problematic in terms of  portability, because sizeof(long) is different
everywhere.It is 32  bits on Windows and on 32-bit *nix, and 64 bits on 64-bit *nix.
 
  I added the following line to the INSERT formatting in pquery.c:
queryDesc->estate->es_processed += 471147114711LL;
  This number is 0x6DB28E70D7; so inserting one row should return  "INSERT 0 2995679448" (0xB28E70D8):
postgres=# insert into t1 values (0);INSERT 0 2995679448
  To fix this, I think it will be enough to change the format strings to  use "%zu" instead of "%lu". pg_snprintf() is
selectedby configure if  the platform's snprintf() does not support the "z" conversion. I tried  this, and it appears
towork:
 
postgres=# insert into t1 values (0);INSERT 0 471147114712
  I have looked for other uses of "%lu", and found none that may cause  the same issue; apparently they are all used
withvalues that clearly  have 32-bit type; actually, most of them are used to format error  codes in Windows-specific
code.

- Are the comments sufficient and accurate: Yes.

- Does it do what it says, correctly: Yes, except for the Windows thing.

- Does it produce compiler warnings: No. First, pg_snprintf() does not  use the system implementation, and second, a
warning(C4477) for  this kind of format string mismatch was only added in VS2015, which  is not officially supported
(itworks for me).
 

- Can you make it crash: No. The problematic argument always appears  last in the sprintf() calls, so the format string
issueshould not  be exploitable.
 

I did not run the regression tests or do the "performance" sections 
after I found the Windows issue. I do not think it will negatively 
affect performance, though.

In all, if replacing four "l"s with "z"s is indeed enough, I think this 
patch is an appropriate solution for solving the underlying issue.

-- 
Christian




Re: pl/pgSQL, get diagnostics and big data

От
Christian Ullrich
Дата:
The following review has been posted through the commitfest application:
make installcheck-world:  not tested
Implements feature:       tested, failed
Spec compliant:           not tested
Documentation:            not tested

* Andreas 'ads' Scherbaum wrote:

> one of our customers approached us and complained, that GET DIAGNOSTICS
> row_count returns invalid results if the number of rows is > 2^31. It's

> Attached patch expands the row_count to 64 bit.
>
> diagnostics=# select testfunc_pg((2^32 + 50000)::bigint);
>   testfunc_pg
> -------------
>    4295017296
> (1 row)

This is my first patch review, but I have to get my feet wet at some point, and this is a nice, small patch to do
that.

Following the checklist from the wiki:

- Is the patch in context format: Yes.

- Does it apply cleanly to master: Yes.

- Does it include reasonable tests, doc patches, etc.: No. While it would be nice if it had some, a test that inserts
2^32rows will take a while and can hardly be called reasonable.
 


The patch is designed to expand the size of the "affected records" count in the command tag from 32 to 64 bits.

- Does it do that: Yes.

- Do we want that: Yes, because it is motivated by reports from users who have queries like that in real life.

- Do we already have it: No.

- Does it follow SQL spec or community-agreed behavior: This is not covered by the SQL standard and there has not, to
myknowledge, been any discussion on this point on -hackers. It is, however, the obvious approach to solving the
specificissue.
 

- Does it include pg_dump support: n/a

- Are there dangers: Existing applications and client libraries must support the increased maximum size (up to nine
additionaldigits) and maximum value. libpq apparently does not parse the command tag, only stores it as a string for
retrievalby PQcmdStatus(), so it is not affected in terms of parsing the value, and for storage, it uses a 64-character
buffer,which will overflow if the command name part of the tag exceeds 32 characters (63 - 19 [row count] - 10 [OID] -
2[spaces]). The longest command name I can think of is "REFRESH MATERIALIZED VIEW" which, at 25 characters, stays
comfortablybelow this limit, and does not include a row count anyway.
 

- Have all the bases been covered: The patch changes all locations where the command tag is formatted, and where the
recordcount is retrieved by PL/pgSQL.
 

- Does the patch follow the coding guidelines: I believe so.

- Are there portability issues/Will it work on Windows/BSD etc.:
 No, it will not work correctly on Windows when built with MSVC, although it may work with MinGW.
 +++ postgresql-9.5.0/src/backend/tcop/pquery.c @@ -195,7 +195,7 @@  {    case CMD_SELECT:
snprintf(completionTag,COMPLETION_TAG_BUFSIZE, -             "SELECT %u", queryDesc->estate->es_processed); +
 "SELECT %lu", queryDesc->estate->es_processed);
 

 %lu formats unsigned long. "long" is problematic in terms of portability, because sizeof(long) is different
everywhere.It is 32 bits on Windows and on 32-bit *nix, and 64 bits on 64-bit *nix.
 
 I added the following line to the INSERT formatting in pquery.c:
   queryDesc->estate->es_processed += 471147114711LL;
 This number is 0x6DB28E70D7; so inserting one row should return "INSERT 0 2995679448" (0xB28E70D8):
   postgres=# insert into t1 values (0);   INSERT 0 2995679448
 To fix this, I think it will be enough to change the format strings to use "%zu" instead of "%lu". pg_snprintf() is
selectedby configure if the platform's snprintf() does not support the "z" conversion. I tried this, and it appears to
work:
   postgres=# insert into t1 values (0);   INSERT 0 471147114712
 I have looked for other uses of "%lu", and found none that may cause the same issue; apparently they are all used with
valuesthat clearly have 32-bit type; actually, most of them are used to format error codes in Windows-specific code.
 

- Are the comments sufficient and accurate: Yes.

- Does it do what it says, correctly: Yes, except for the Windows thing.

- Does it produce compiler warnings: No. First, pg_snprintf() does not use the system implementation, and second, a
warning(C4477) for this kind of format string mismatch was only added in VS2015, which is not officially supported (it
worksfor me).
 

- Can you make it crash: No. The problematic argument always appears last in the sprintf() calls, so the format string
issueshould not be exploitable.
 

I did not run the regression tests or do the "performance" sections after I found the Windows issue. I do not think it
willnegatively affect performance, though.
 

In all, if replacing four "l"s with "z"s is indeed enough, I think this patch is an appropriate solution for solving
theunderlying issue. 

The new status of this patch is: Waiting on Author

Re: pl/pgSQL, get diagnostics and big data

От
Christian Ullrich
Дата:
Ah, so it turns out I should have used the commitfest tool. My 
apologies; I will send the whole thing through that again. Please 
disregard the earlier message.




Re: pl/pgSQL, get diagnostics and big data

От
Andreas 'ads' Scherbaum
Дата:
Hello,

thanks for reviewing the patch!

On 09.02.2016 20:32, Christian Ullrich wrote:
>
> - Are there portability issues/Will it work on Windows/BSD etc.:
>
>    No, it will not work correctly on Windows when built with MSVC,
>    although it may work with MinGW.
>
>    +++ postgresql-9.5.0/src/backend/tcop/pquery.c
>    @@ -195,7 +195,7 @@
>     {
>       case CMD_SELECT:
>           snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
>    -             "SELECT %u", queryDesc->estate->es_processed);
>    +             "SELECT %lu", queryDesc->estate->es_processed);
>
>
>    %lu formats unsigned long. "long" is problematic in terms of
>    portability, because sizeof(long) is different everywhere. It is 32
>    bits on Windows and on 32-bit *nix, and 64 bits on 64-bit *nix.
>
>    I added the following line to the INSERT formatting in pquery.c:
>
>      queryDesc->estate->es_processed += 471147114711LL;
>
>    This number is 0x6DB28E70D7; so inserting one row should return
>    "INSERT 0 2995679448" (0xB28E70D8):
>
>      postgres=# insert into t1 values (0);
>      INSERT 0 2995679448
>
>    To fix this, I think it will be enough to change the format strings to
>    use "%zu" instead of "%lu". pg_snprintf() is selected by configure if
>    the platform's snprintf() does not support the "z" conversion. I tried
>    this, and it appears to work:
>
>      postgres=# insert into t1 values (0);
>      INSERT 0 471147114712
>
>    I have looked for other uses of "%lu", and found none that may cause
>    the same issue; apparently they are all used with values that clearly
>    have 32-bit type; actually, most of them are used to format error
>    codes in Windows-specific code.

Attached is a new version of the patch, with %lu replaced by %zu.
I re-ran all the tests, especially the long test with 2^32+x rows, and
it produces the same result as before.


Regards,

--
                Andreas 'ads' Scherbaum
German PostgreSQL User Group
European PostgreSQL User Group - Board of Directors
Volunteer Regional Contact, Germany - PostgreSQL Project

Вложения

Re: pl/pgSQL, get diagnostics and big data

От
Christian Ullrich
Дата:
* Andreas 'ads' Scherbaum wrote:

> Attached is a new version of the patch, with %lu replaced by %zu.
> I re-ran all the tests, especially the long test with 2^32+x rows, and
> it produces the same result as before.

To paraphrase Twain: "Sire, the Board finds this patch perfect in all 
the requirements and qualifications for inclusion into core, and doth 
hold his case open for decision after due examination by his committer."

The Windows issue is corrected, and all regression tests pass on Windows 
and FreeBSD. I can find no further fault with this patch.

Sorry it took so long, my other PostgreSQL issue happened just when I 
was going to test the updated patch.

-- 
Christian




Re: pl/pgSQL, get diagnostics and big data

От
Tom Lane
Дата:
"Andreas 'ads' Scherbaum" <adsmail@wars-nicht.de> writes:
> On 09.02.2016 20:32, Christian Ullrich wrote:
>> To fix this, I think it will be enough to change the format strings to
>> use "%zu" instead of "%lu".

> Attached is a new version of the patch, with %lu replaced by %zu.

Nonono ... that just moves the portability problem to a different set of
platforms.  "%z" means size_t, and sizeof(size_t) is not any more fixed
than sizeof(long).  The right thing to use is UINT64_FORMAT.

+                                  /* Int64GetDatum() instead of UInt64GetDatum(),
+                                     because there is no UInt64GetDatum() */
+                                  Int64GetDatum(estate->eval_processed),

Although in practice you'd get the same conversion anyway, I'm thinking
it's time to fix that omission rather than just averting our eyes.  The
use of int64 data isn't decreasing as time goes on.

These are small enough changes that there's no need for a new patch
submission.  I'll take it from here, unless I find bigger issues.
        regards, tom lane



Re: pl/pgSQL, get diagnostics and big data

От
Tom Lane
Дата:
I wrote:
> I'll take it from here, unless I find bigger issues.

Hmm ... so the more I pulled on this string, the more stuff I found.
The attached updated patch fixes several additional significant
areas:

* call_cntr and max_calls in FuncCallContext are now uint64

* Result widths for PortalRunFetch and allied routines are now uint64

* count arguments to ExecutorRun and related routines are now uint64

* Maximum number of tuples in a SPITupleTable is now uint64

* Management of portal (cursor) positions fixed.  I promoted portalPos to
uint64, and got rid of posOverflow, which seems not especially necessary
if we're using 64-bit counters.

* A whole lot of places that were comparing integer-width variables
to SPI_processed, and would therefore go into infinite loops with
result sizes above 2G tuples, now use uint64 counters instead.

However, I drew the line at changing FetchStmt.howMany, which means that
the count-limit inputs to PortalRunFetch et al didn't change; they're
still signed long.  Changing that would require messing with the lexer
which I did not feel like doing.  So the maximum offset/position in a
FETCH or MOVE is (still) platform-dependent.  I'm not sure that this is
worth changing, as you will get a clean syntax error if you try to exceed
the limit.

BTW, if anyone thinks the above changes represent too much API churn,
speak up.  This is definitely going to cause some pain for extensions.

There's a fair amount of work yet to do:

1. I found two places (marked XXX in this patch) that are using strtoul()
to parse a tuple count back out of a command tag.  That won't do anymore.
pg_stat_statements has a messy hack for the same problem (look for
HAVE_STRTOULL), which is probably what we want to do, but not by
copy-and-pasting #ifdef HAVE_STRTOULL into multiple places.  I'd be
inclined to provide a utility function "pg_strtouint64" or some such
to encapsulate that.  (numutils.c might be a good place for it.)

2. As I was just complaining to -hackers, plpython plperl and pltcl
all now contain attempts to pass uint64 values (from SPI_processed)
into language-specific functions.  We need to figure out whether
that will overflow and whether it's worth doing something about.

3. This patch still needs a lot of review as I may have missed
something.

So I'm bouncing this back to Waiting on Author.

            regards, tom lane

diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c
index 76d1831..6708d81 100644
*** a/contrib/auto_explain/auto_explain.c
--- b/contrib/auto_explain/auto_explain.c
*************** void        _PG_fini(void);
*** 61,67 ****
  static void explain_ExecutorStart(QueryDesc *queryDesc, int eflags);
  static void explain_ExecutorRun(QueryDesc *queryDesc,
                      ScanDirection direction,
!                     long count);
  static void explain_ExecutorFinish(QueryDesc *queryDesc);
  static void explain_ExecutorEnd(QueryDesc *queryDesc);

--- 61,67 ----
  static void explain_ExecutorStart(QueryDesc *queryDesc, int eflags);
  static void explain_ExecutorRun(QueryDesc *queryDesc,
                      ScanDirection direction,
!                     uint64 count);
  static void explain_ExecutorFinish(QueryDesc *queryDesc);
  static void explain_ExecutorEnd(QueryDesc *queryDesc);

*************** explain_ExecutorStart(QueryDesc *queryDe
*** 257,263 ****
   * ExecutorRun hook: all we need do is track nesting depth
   */
  static void
! explain_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long count)
  {
      nesting_level++;
      PG_TRY();
--- 257,263 ----
   * ExecutorRun hook: all we need do is track nesting depth
   */
  static void
! explain_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count)
  {
      nesting_level++;
      PG_TRY();
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 9ce60e6..29e12cd 100644
*** a/contrib/pg_stat_statements/pg_stat_statements.c
--- b/contrib/pg_stat_statements/pg_stat_statements.c
*************** static void pgss_post_parse_analyze(Pars
*** 289,295 ****
  static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags);
  static void pgss_ExecutorRun(QueryDesc *queryDesc,
                   ScanDirection direction,
!                  long count);
  static void pgss_ExecutorFinish(QueryDesc *queryDesc);
  static void pgss_ExecutorEnd(QueryDesc *queryDesc);
  static void pgss_ProcessUtility(Node *parsetree, const char *queryString,
--- 289,295 ----
  static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags);
  static void pgss_ExecutorRun(QueryDesc *queryDesc,
                   ScanDirection direction,
!                  uint64 count);
  static void pgss_ExecutorFinish(QueryDesc *queryDesc);
  static void pgss_ExecutorEnd(QueryDesc *queryDesc);
  static void pgss_ProcessUtility(Node *parsetree, const char *queryString,
*************** pgss_ExecutorStart(QueryDesc *queryDesc,
*** 866,872 ****
   * ExecutorRun hook: all we need do is track nesting depth
   */
  static void
! pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long count)
  {
      nested_level++;
      PG_TRY();
--- 866,872 ----
   * ExecutorRun hook: all we need do is track nesting depth
   */
  static void
! pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count)
  {
      nested_level++;
      PG_TRY();
diff --git a/contrib/spi/refint.c b/contrib/spi/refint.c
index 2602210..01dd717 100644
*** a/contrib/spi/refint.c
--- b/contrib/spi/refint.c
*************** check_foreign_key(PG_FUNCTION_ARGS)
*** 593,599 ****
          else
          {
  #ifdef REFINT_VERBOSE
!             elog(NOTICE, "%s: %d tuple(s) of %s are %s",
                   trigger->tgname, SPI_processed, relname,
                   (action == 'c') ? "deleted" : "set to null");
  #endif
--- 593,599 ----
          else
          {
  #ifdef REFINT_VERBOSE
!             elog(NOTICE, "%s: " UINT64_FORMAT " tuple(s) of %s are %s",
                   trigger->tgname, SPI_processed, relname,
                   (action == 'c') ? "deleted" : "set to null");
  #endif
diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index 1ea4a63..787c02d 100644
*** a/contrib/tablefunc/tablefunc.c
--- b/contrib/tablefunc/tablefunc.c
*************** typedef struct
*** 120,126 ****
  typedef struct crosstab_cat_desc
  {
      char       *catname;        /* full category name */
!     int            attidx;            /* zero based */
  } crosstab_cat_desc;

  #define MAX_CATNAME_LEN            NAMEDATALEN
--- 120,126 ----
  typedef struct crosstab_cat_desc
  {
      char       *catname;        /* full category name */
!     uint64        attidx;            /* zero based */
  } crosstab_cat_desc;

  #define MAX_CATNAME_LEN            NAMEDATALEN
*************** Datum
*** 174,181 ****
  normal_rand(PG_FUNCTION_ARGS)
  {
      FuncCallContext *funcctx;
!     int            call_cntr;
!     int            max_calls;
      normal_rand_fctx *fctx;
      float8        mean;
      float8        stddev;
--- 174,181 ----
  normal_rand(PG_FUNCTION_ARGS)
  {
      FuncCallContext *funcctx;
!     uint64        call_cntr;
!     uint64        max_calls;
      normal_rand_fctx *fctx;
      float8        mean;
      float8        stddev;
*************** crosstab(PG_FUNCTION_ARGS)
*** 352,359 ****
      ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
      Tuplestorestate *tupstore;
      TupleDesc    tupdesc;
!     int            call_cntr;
!     int            max_calls;
      AttInMetadata *attinmeta;
      SPITupleTable *spi_tuptable;
      TupleDesc    spi_tupdesc;
--- 352,359 ----
      ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
      Tuplestorestate *tupstore;
      TupleDesc    tupdesc;
!     uint64        call_cntr;
!     uint64        max_calls;
      AttInMetadata *attinmeta;
      SPITupleTable *spi_tuptable;
      TupleDesc    spi_tupdesc;
*************** crosstab(PG_FUNCTION_ARGS)
*** 364,370 ****
      MemoryContext per_query_ctx;
      MemoryContext oldcontext;
      int            ret;
!     int            proc;

      /* check to see if caller supports us returning a tuplestore */
      if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
--- 364,370 ----
      MemoryContext per_query_ctx;
      MemoryContext oldcontext;
      int            ret;
!     uint64        proc;

      /* check to see if caller supports us returning a tuplestore */
      if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
*************** crosstab(PG_FUNCTION_ARGS)
*** 389,395 ****
      proc = SPI_processed;

      /* If no qualifying tuples, fall out early */
!     if (ret != SPI_OK_SELECT || proc <= 0)
      {
          SPI_finish();
          rsinfo->isDone = ExprEndResult;
--- 389,395 ----
      proc = SPI_processed;

      /* If no qualifying tuples, fall out early */
!     if (ret != SPI_OK_SELECT || proc == 0)
      {
          SPI_finish();
          rsinfo->isDone = ExprEndResult;
*************** load_categories_hash(char *cats_sql, Mem
*** 708,714 ****
      HTAB       *crosstab_hash;
      HASHCTL        ctl;
      int            ret;
!     int            proc;
      MemoryContext SPIcontext;

      /* initialize the category hash table */
--- 708,714 ----
      HTAB       *crosstab_hash;
      HASHCTL        ctl;
      int            ret;
!     uint64        proc;
      MemoryContext SPIcontext;

      /* initialize the category hash table */
*************** load_categories_hash(char *cats_sql, Mem
*** 740,746 ****
      {
          SPITupleTable *spi_tuptable = SPI_tuptable;
          TupleDesc    spi_tupdesc = spi_tuptable->tupdesc;
!         int            i;

          /*
           * The provided categories SQL query must always return one column:
--- 740,746 ----
      {
          SPITupleTable *spi_tuptable = SPI_tuptable;
          TupleDesc    spi_tupdesc = spi_tuptable->tupdesc;
!         uint64        i;

          /*
           * The provided categories SQL query must always return one column:
*************** get_crosstab_tuplestore(char *sql,
*** 800,806 ****
      char      **values;
      HeapTuple    tuple;
      int            ret;
!     int            proc;

      /* initialize our tuplestore (while still in query context!) */
      tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
--- 800,806 ----
      char      **values;
      HeapTuple    tuple;
      int            ret;
!     uint64        proc;

      /* initialize our tuplestore (while still in query context!) */
      tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
*************** get_crosstab_tuplestore(char *sql,
*** 823,830 ****
          char       *rowid;
          char       *lastrowid = NULL;
          bool        firstpass = true;
!         int            i,
!                     j;
          int            result_ncols;

          if (num_categories == 0)
--- 823,830 ----
          char       *rowid;
          char       *lastrowid = NULL;
          bool        firstpass = true;
!         uint64        i;
!         int            j;
          int            result_ncols;

          if (num_categories == 0)
*************** build_tuplestore_recursively(char *key_f
*** 1220,1226 ****
  {
      TupleDesc    tupdesc = attinmeta->tupdesc;
      int            ret;
!     int            proc;
      int            serial_column;
      StringInfoData sql;
      char      **values;
--- 1220,1226 ----
  {
      TupleDesc    tupdesc = attinmeta->tupdesc;
      int            ret;
!     uint64        proc;
      int            serial_column;
      StringInfoData sql;
      char      **values;
*************** build_tuplestore_recursively(char *key_f
*** 1313,1319 ****
          HeapTuple    spi_tuple;
          SPITupleTable *tuptable = SPI_tuptable;
          TupleDesc    spi_tupdesc = tuptable->tupdesc;
!         int            i;
          StringInfoData branchstr;
          StringInfoData chk_branchstr;
          StringInfoData chk_current_key;
--- 1313,1319 ----
          HeapTuple    spi_tuple;
          SPITupleTable *tuptable = SPI_tuptable;
          TupleDesc    spi_tupdesc = tuptable->tupdesc;
!         uint64        i;
          StringInfoData branchstr;
          StringInfoData chk_branchstr;
          StringInfoData chk_current_key;
diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 655c532..ac28996 100644
*** a/contrib/xml2/xpath.c
--- b/contrib/xml2/xpath.c
*************** xpath_table(PG_FUNCTION_ARGS)
*** 553,560 ****

      int            numpaths;
      int            ret;
!     int            proc;
!     int            i;
      int            j;
      int            rownr;            /* For issuing multiple rows from one original
                                   * document */
--- 553,559 ----

      int            numpaths;
      int            ret;
!     uint64        proc;
      int            j;
      int            rownr;            /* For issuing multiple rows from one original
                                   * document */
*************** xpath_table(PG_FUNCTION_ARGS)
*** 664,670 ****
               query_buf.data);

      proc = SPI_processed;
-     /* elog(DEBUG1,"xpath_table: SPI returned %d rows",proc); */
      tuptable = SPI_tuptable;
      spi_tupdesc = tuptable->tupdesc;

--- 663,668 ----
*************** xpath_table(PG_FUNCTION_ARGS)
*** 692,697 ****
--- 690,697 ----
      PG_TRY();
      {
          /* For each row i.e. document returned from SPI */
+         uint64        i;
+
          for (i = 0; i < proc; i++)
          {
              char       *pkey;
diff --git a/doc/src/sgml/spi.sgml b/doc/src/sgml/spi.sgml
index c099fcf..572154a 100644
*** a/doc/src/sgml/spi.sgml
--- b/doc/src/sgml/spi.sgml
*************** INSERT INTO a SELECT * FROM a;
*** 4116,4129 ****
  PG_MODULE_MAGIC;
  #endif

! int execq(text *sql, int cnt);

! int
  execq(text *sql, int cnt)
  {
      char *command;
      int ret;
!     int proc;

      /* Convert given text object to a C string */
      command = text_to_cstring(sql);
--- 4116,4129 ----
  PG_MODULE_MAGIC;
  #endif

! int64 execq(text *sql, int cnt);

! int64
  execq(text *sql, int cnt)
  {
      char *command;
      int ret;
!     uint64 proc;

      /* Convert given text object to a C string */
      command = text_to_cstring(sql);
*************** execq(text *sql, int cnt)
*** 4141,4151 ****
          TupleDesc tupdesc = SPI_tuptable->tupdesc;
          SPITupleTable *tuptable = SPI_tuptable;
          char buf[8192];
!         int i, j;

          for (j = 0; j < proc; j++)
          {
              HeapTuple tuple = tuptable->vals[j];

              for (i = 1, buf[0] = 0; i <= tupdesc->natts; i++)
                  snprintf(buf + strlen (buf), sizeof(buf) - strlen(buf), " %s%s",
--- 4141,4152 ----
          TupleDesc tupdesc = SPI_tuptable->tupdesc;
          SPITupleTable *tuptable = SPI_tuptable;
          char buf[8192];
!         uint64 j;

          for (j = 0; j < proc; j++)
          {
              HeapTuple tuple = tuptable->vals[j];
+             int i;

              for (i = 1, buf[0] = 0; i <= tupdesc->natts; i++)
                  snprintf(buf + strlen (buf), sizeof(buf) - strlen(buf), " %s%s",
*************** execq(text *sql, int cnt)
*** 4173,4181 ****
     a shared library (details are in <xref linkend="dfunc">.):

  <programlisting>
! CREATE FUNCTION execq(text, integer) RETURNS integer
      AS '<replaceable>filename</replaceable>'
!     LANGUAGE C;
  </programlisting>
    </para>

--- 4174,4182 ----
     a shared library (details are in <xref linkend="dfunc">.):

  <programlisting>
! CREATE FUNCTION execq(text, integer) RETURNS int8
      AS '<replaceable>filename</replaceable>'
!     LANGUAGE C STRICT;
  </programlisting>
    </para>

diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index fcb0331..cb7a145 100644
*** a/src/backend/commands/createas.c
--- b/src/backend/commands/createas.c
*************** ExecCreateTableAs(CreateTableAsStmt *stm
*** 197,203 ****
      /* save the rowcount if we're given a completionTag to fill */
      if (completionTag)
          snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
!                  "SELECT %u", queryDesc->estate->es_processed);

      /* and clean up */
      ExecutorFinish(queryDesc);
--- 197,203 ----
      /* save the rowcount if we're given a completionTag to fill */
      if (completionTag)
          snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
!                  "SELECT " UINT64_FORMAT, queryDesc->estate->es_processed);

      /* and clean up */
      ExecutorFinish(queryDesc);
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 8c045c0..50a54e7 100644
*** a/src/backend/commands/portalcmds.c
--- b/src/backend/commands/portalcmds.c
*************** PerformPortalFetch(FetchStmt *stmt,
*** 148,154 ****
                     char *completionTag)
  {
      Portal        portal;
!     long        nprocessed;

      /*
       * Disallow empty-string cursor name (conflicts with protocol-level
--- 148,154 ----
                     char *completionTag)
  {
      Portal        portal;
!     uint64        nprocessed;

      /*
       * Disallow empty-string cursor name (conflicts with protocol-level
*************** PerformPortalFetch(FetchStmt *stmt,
*** 181,187 ****

      /* Return command status if wanted */
      if (completionTag)
!         snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
                   stmt->ismove ? "MOVE" : "FETCH",
                   nprocessed);
  }
--- 181,187 ----

      /* Return command status if wanted */
      if (completionTag)
!         snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s " UINT64_FORMAT,
                   stmt->ismove ? "MOVE" : "FETCH",
                   nprocessed);
  }
*************** PersistHoldablePortal(Portal portal)
*** 392,411 ****
          if (portal->atEnd)
          {
              /*
!              * We can handle this case even if posOverflow: just force the
!              * tuplestore forward to its end.  The size of the skip request
!              * here is arbitrary.
               */
              while (tuplestore_skiptuples(portal->holdStore, 1000000, true))
                   /* continue */ ;
          }
          else
          {
-             if (portal->posOverflow)    /* oops, cannot trust portalPos */
-                 ereport(ERROR,
-                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-                          errmsg("could not reposition held cursor")));
-
              tuplestore_rescan(portal->holdStore);

              if (!tuplestore_skiptuples(portal->holdStore,
--- 392,405 ----
          if (portal->atEnd)
          {
              /*
!              * Just force the tuplestore forward to its end.  The size of the
!              * skip request here is arbitrary.
               */
              while (tuplestore_skiptuples(portal->holdStore, 1000000, true))
                   /* continue */ ;
          }
          else
          {
              tuplestore_rescan(portal->holdStore);

              if (!tuplestore_skiptuples(portal->holdStore,
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 76f7297..6872562 100644
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
*************** static void ExecutePlan(EState *estate,
*** 79,85 ****
              bool use_parallel_mode,
              CmdType operation,
              bool sendTuples,
!             long numberTuples,
              ScanDirection direction,
              DestReceiver *dest);
  static bool ExecCheckRTEPerms(RangeTblEntry *rte);
--- 79,85 ----
              bool use_parallel_mode,
              CmdType operation,
              bool sendTuples,
!             uint64 numberTuples,
              ScanDirection direction,
              DestReceiver *dest);
  static bool ExecCheckRTEPerms(RangeTblEntry *rte);
*************** standard_ExecutorStart(QueryDesc *queryD
*** 278,284 ****
   */
  void
  ExecutorRun(QueryDesc *queryDesc,
!             ScanDirection direction, long count)
  {
      if (ExecutorRun_hook)
          (*ExecutorRun_hook) (queryDesc, direction, count);
--- 278,284 ----
   */
  void
  ExecutorRun(QueryDesc *queryDesc,
!             ScanDirection direction, uint64 count)
  {
      if (ExecutorRun_hook)
          (*ExecutorRun_hook) (queryDesc, direction, count);
*************** ExecutorRun(QueryDesc *queryDesc,
*** 288,294 ****

  void
  standard_ExecutorRun(QueryDesc *queryDesc,
!                      ScanDirection direction, long count)
  {
      EState       *estate;
      CmdType        operation;
--- 288,294 ----

  void
  standard_ExecutorRun(QueryDesc *queryDesc,
!                      ScanDirection direction, uint64 count)
  {
      EState       *estate;
      CmdType        operation;
*************** ExecutePlan(EState *estate,
*** 1521,1532 ****
              bool use_parallel_mode,
              CmdType operation,
              bool sendTuples,
!             long numberTuples,
              ScanDirection direction,
              DestReceiver *dest)
  {
      TupleTableSlot *slot;
!     long        current_tuple_count;

      /*
       * initialize local variables
--- 1521,1532 ----
              bool use_parallel_mode,
              CmdType operation,
              bool sendTuples,
!             uint64 numberTuples,
              ScanDirection direction,
              DestReceiver *dest)
  {
      TupleTableSlot *slot;
!     uint64        current_tuple_count;

      /*
       * initialize local variables
*************** ExecutePlan(EState *estate,
*** 1542,1548 ****
       * If a tuple count was supplied, we must force the plan to run without
       * parallelism, because we might exit early.
       */
!     if (numberTuples != 0)
          use_parallel_mode = false;

      /*
--- 1542,1548 ----
       * If a tuple count was supplied, we must force the plan to run without
       * parallelism, because we might exit early.
       */
!     if (numberTuples)
          use_parallel_mode = false;

      /*
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index c3cdad4..6e14c9d 100644
*** a/src/backend/executor/functions.c
--- b/src/backend/executor/functions.c
*************** postquel_getnext(execution_state *es, SQ
*** 853,859 ****
      else
      {
          /* Run regular commands to completion unless lazyEval */
!         long        count = (es->lazyEval) ? 1L : 0L;

          ExecutorRun(es->qd, ForwardScanDirection, count);

--- 853,859 ----
      else
      {
          /* Run regular commands to completion unless lazyEval */
!         uint64        count = (es->lazyEval) ? 1 : 0;

          ExecutorRun(es->qd, ForwardScanDirection, count);

*************** postquel_getnext(execution_state *es, SQ
*** 861,867 ****
           * If we requested run to completion OR there was no tuple returned,
           * command must be complete.
           */
!         result = (count == 0L || es->qd->estate->es_processed == 0);
      }

      return result;
--- 861,867 ----
           * If we requested run to completion OR there was no tuple returned,
           * command must be complete.
           */
!         result = (count == 0 || es->qd->estate->es_processed == 0);
      }

      return result;
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 3357c0d..ef8a9c8 100644
*** a/src/backend/executor/spi.c
--- b/src/backend/executor/spi.c
***************
*** 36,42 ****
  #include "utils/typcache.h"


! uint32        SPI_processed = 0;
  Oid            SPI_lastoid = InvalidOid;
  SPITupleTable *SPI_tuptable = NULL;
  int            SPI_result;
--- 36,42 ----
  #include "utils/typcache.h"


! uint64        SPI_processed = 0;
  Oid            SPI_lastoid = InvalidOid;
  SPITupleTable *SPI_tuptable = NULL;
  int            SPI_result;
*************** static void _SPI_prepare_oneshot_plan(co
*** 56,67 ****

  static int _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
                    Snapshot snapshot, Snapshot crosscheck_snapshot,
!                   bool read_only, bool fire_triggers, long tcount);

  static ParamListInfo _SPI_convert_params(int nargs, Oid *argtypes,
                      Datum *Values, const char *Nulls);

! static int    _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, long tcount);

  static void _SPI_error_callback(void *arg);

--- 56,67 ----

  static int _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
                    Snapshot snapshot, Snapshot crosscheck_snapshot,
!                   bool read_only, bool fire_triggers, uint64 tcount);

  static ParamListInfo _SPI_convert_params(int nargs, Oid *argtypes,
                      Datum *Values, const char *Nulls);

! static int    _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount);

  static void _SPI_error_callback(void *arg);

*************** _SPI_prepare_oneshot_plan(const char *sr
*** 1991,2000 ****
  static int
  _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
                    Snapshot snapshot, Snapshot crosscheck_snapshot,
!                   bool read_only, bool fire_triggers, long tcount)
  {
      int            my_res = 0;
!     uint32        my_processed = 0;
      Oid            my_lastoid = InvalidOid;
      SPITupleTable *my_tuptable = NULL;
      int            res = 0;
--- 1991,2000 ----
  static int
  _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
                    Snapshot snapshot, Snapshot crosscheck_snapshot,
!                   bool read_only, bool fire_triggers, uint64 tcount)
  {
      int            my_res = 0;
!     uint64        my_processed = 0;
      Oid            my_lastoid = InvalidOid;
      SPITupleTable *my_tuptable = NULL;
      int            res = 0;
*************** _SPI_execute_plan(SPIPlanPtr plan, Param
*** 2218,2223 ****
--- 2218,2224 ----
                  if (IsA(stmt, CreateTableAsStmt))
                  {
                      Assert(strncmp(completionTag, "SELECT ", 7) == 0);
+                     /* XXX needs fixed ... */
                      _SPI_current->processed = strtoul(completionTag + 7,
                                                        NULL, 10);

*************** _SPI_execute_plan(SPIPlanPtr plan, Param
*** 2231,2236 ****
--- 2232,2238 ----
                  else if (IsA(stmt, CopyStmt))
                  {
                      Assert(strncmp(completionTag, "COPY ", 5) == 0);
+                     /* XXX */
                      _SPI_current->processed = strtoul(completionTag + 5,
                                                        NULL, 10);
                  }
*************** _SPI_convert_params(int nargs, Oid *argt
*** 2348,2354 ****
  }

  static int
! _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, long tcount)
  {
      int            operation = queryDesc->operation;
      int            eflags;
--- 2350,2356 ----
  }

  static int
! _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount)
  {
      int            operation = queryDesc->operation;
      int            eflags;
*************** static void
*** 2460,2466 ****
  _SPI_cursor_operation(Portal portal, FetchDirection direction, long count,
                        DestReceiver *dest)
  {
!     long        nfetched;

      /* Check that the portal is valid */
      if (!PortalIsValid(portal))
--- 2462,2468 ----
  _SPI_cursor_operation(Portal portal, FetchDirection direction, long count,
                        DestReceiver *dest)
  {
!     uint64        nfetched;

      /* Check that the portal is valid */
      if (!PortalIsValid(portal))
*************** _SPI_end_call(bool procmem)
*** 2563,2569 ****
  static bool
  _SPI_checktuples(void)
  {
!     uint32        processed = _SPI_current->processed;
      SPITupleTable *tuptable = _SPI_current->tuptable;
      bool        failed = false;

--- 2565,2571 ----
  static bool
  _SPI_checktuples(void)
  {
!     uint64        processed = _SPI_current->processed;
      SPITupleTable *tuptable = _SPI_current->tuptable;
      bool        failed = false;

diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 6893b0f..cb9ecfa 100644
*** a/src/backend/tcop/pquery.c
--- b/src/backend/tcop/pquery.c
*************** static void ProcessQuery(PlannedStmt *pl
*** 39,54 ****
               DestReceiver *dest,
               char *completionTag);
  static void FillPortalStore(Portal portal, bool isTopLevel);
! static uint32 RunFromStore(Portal portal, ScanDirection direction, long count,
               DestReceiver *dest);
! 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,
                   DestReceiver *dest);
--- 39,54 ----
               DestReceiver *dest,
               char *completionTag);
  static void FillPortalStore(Portal portal, bool isTopLevel);
! static uint64 RunFromStore(Portal portal, ScanDirection direction, uint64 count,
               DestReceiver *dest);
! static uint64 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 uint64 DoPortalRunFetch(Portal portal,
                   FetchDirection fdirection,
                   long count,
                   DestReceiver *dest);
*************** ProcessQuery(PlannedStmt *plan,
*** 195,201 ****
          {
              case CMD_SELECT:
                  snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
!                          "SELECT %u", queryDesc->estate->es_processed);
                  break;
              case CMD_INSERT:
                  if (queryDesc->estate->es_processed == 1)
--- 195,202 ----
          {
              case CMD_SELECT:
                  snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
!                          "SELECT " UINT64_FORMAT,
!                          queryDesc->estate->es_processed);
                  break;
              case CMD_INSERT:
                  if (queryDesc->estate->es_processed == 1)
*************** ProcessQuery(PlannedStmt *plan,
*** 203,217 ****
                  else
                      lastOid = InvalidOid;
                  snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
!                    "INSERT %u %u", lastOid, queryDesc->estate->es_processed);
                  break;
              case CMD_UPDATE:
                  snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
!                          "UPDATE %u", queryDesc->estate->es_processed);
                  break;
              case CMD_DELETE:
                  snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
!                          "DELETE %u", queryDesc->estate->es_processed);
                  break;
              default:
                  strcpy(completionTag, "???");
--- 204,221 ----
                  else
                      lastOid = InvalidOid;
                  snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
!                          "INSERT %u " UINT64_FORMAT,
!                          lastOid, queryDesc->estate->es_processed);
                  break;
              case CMD_UPDATE:
                  snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
!                          "UPDATE " UINT64_FORMAT,
!                          queryDesc->estate->es_processed);
                  break;
              case CMD_DELETE:
                  snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
!                          "DELETE " UINT64_FORMAT,
!                          queryDesc->estate->es_processed);
                  break;
              default:
                  strcpy(completionTag, "???");
*************** PortalStart(Portal portal, ParamListInfo
*** 548,554 ****
                  portal->atStart = true;
                  portal->atEnd = false;    /* allow fetches */
                  portal->portalPos = 0;
-                 portal->posOverflow = false;

                  PopActiveSnapshot();
                  break;
--- 552,557 ----
*************** PortalStart(Portal portal, ParamListInfo
*** 576,582 ****
                  portal->atStart = true;
                  portal->atEnd = false;    /* allow fetches */
                  portal->portalPos = 0;
-                 portal->posOverflow = false;
                  break;

              case PORTAL_UTIL_SELECT:
--- 579,584 ----
*************** PortalStart(Portal portal, ParamListInfo
*** 598,604 ****
                  portal->atStart = true;
                  portal->atEnd = false;    /* allow fetches */
                  portal->portalPos = 0;
-                 portal->posOverflow = false;
                  break;

              case PORTAL_MULTI_QUERY:
--- 600,605 ----
*************** PortalRun(Portal portal, long count, boo
*** 708,714 ****
            char *completionTag)
  {
      bool        result;
!     uint32        nprocessed;
      ResourceOwner saveTopTransactionResourceOwner;
      MemoryContext saveTopTransactionContext;
      Portal        saveActivePortal;
--- 709,715 ----
            char *completionTag)
  {
      bool        result;
!     uint64        nprocessed;
      ResourceOwner saveTopTransactionResourceOwner;
      MemoryContext saveTopTransactionContext;
      Portal        saveActivePortal;
*************** PortalRun(Portal portal, long count, boo
*** 794,800 ****
                  {
                      if (strcmp(portal->commandTag, "SELECT") == 0)
                          snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
!                                  "SELECT %u", nprocessed);
                      else
                          strcpy(completionTag, portal->commandTag);
                  }
--- 795,801 ----
                  {
                      if (strcmp(portal->commandTag, "SELECT") == 0)
                          snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
!                                  "SELECT " UINT64_FORMAT, nprocessed);
                      else
                          strcpy(completionTag, portal->commandTag);
                  }
*************** PortalRun(Portal portal, long count, boo
*** 877,890 ****
   *
   * count <= 0 is interpreted as a no-op: the destination gets started up
   * and shut down, but nothing else happens.  Also, count == FETCH_ALL is
!  * interpreted as "all rows".
   *
   * Caller must already have validated the Portal and done appropriate
   * setup (cf. PortalRun).
   *
   * Returns number of rows processed (suitable for use in result tag)
   */
! static long
  PortalRunSelect(Portal portal,
                  bool forward,
                  long count,
--- 878,891 ----
   *
   * count <= 0 is interpreted as a no-op: the destination gets started up
   * and shut down, but nothing else happens.  Also, count == FETCH_ALL is
!  * interpreted as "all rows".  (cf FetchStmt.howMany)
   *
   * Caller must already have validated the Portal and done appropriate
   * setup (cf. PortalRun).
   *
   * Returns number of rows processed (suitable for use in result tag)
   */
! static uint64
  PortalRunSelect(Portal portal,
                  bool forward,
                  long count,
*************** PortalRunSelect(Portal portal,
*** 892,898 ****
  {
      QueryDesc  *queryDesc;
      ScanDirection direction;
!     uint32        nprocessed;

      /*
       * NB: queryDesc will be NULL if we are fetching from a held cursor or a
--- 893,899 ----
  {
      QueryDesc  *queryDesc;
      ScanDirection direction;
!     uint64        nprocessed;

      /*
       * NB: queryDesc will be NULL if we are fetching from a held cursor or a
*************** PortalRunSelect(Portal portal,
*** 926,932 ****
--- 927,936 ----
      if (forward)
      {
          if (portal->atEnd || count <= 0)
+         {
              direction = NoMovementScanDirection;
+             count = 0;            /* don't pass negative count to executor */
+         }
          else
              direction = ForwardScanDirection;

*************** PortalRunSelect(Portal portal,
*** 935,963 ****
              count = 0;

          if (portal->holdStore)
!             nprocessed = RunFromStore(portal, direction, count, dest);
          else
          {
              PushActiveSnapshot(queryDesc->snapshot);
!             ExecutorRun(queryDesc, direction, count);
              nprocessed = queryDesc->estate->es_processed;
              PopActiveSnapshot();
          }

          if (!ScanDirectionIsNoMovement(direction))
          {
-             long        oldPos;
-
              if (nprocessed > 0)
                  portal->atStart = false;        /* OK to go backward now */
!             if (count == 0 ||
!                 (unsigned long) nprocessed < (unsigned long) count)
                  portal->atEnd = true;    /* we retrieved 'em all */
-             oldPos = portal->portalPos;
              portal->portalPos += nprocessed;
-             /* portalPos doesn't advance when we fall off the end */
-             if (portal->portalPos < oldPos)
-                 portal->posOverflow = true;
          }
      }
      else
--- 939,960 ----
              count = 0;

          if (portal->holdStore)
!             nprocessed = RunFromStore(portal, direction, (uint64) count, dest);
          else
          {
              PushActiveSnapshot(queryDesc->snapshot);
!             ExecutorRun(queryDesc, direction, (uint64) count);
              nprocessed = queryDesc->estate->es_processed;
              PopActiveSnapshot();
          }

          if (!ScanDirectionIsNoMovement(direction))
          {
              if (nprocessed > 0)
                  portal->atStart = false;        /* OK to go backward now */
!             if (count == 0 || nprocessed < (uint64) count)
                  portal->atEnd = true;    /* we retrieved 'em all */
              portal->portalPos += nprocessed;
          }
      }
      else
*************** PortalRunSelect(Portal portal,
*** 969,975 ****
--- 966,975 ----
                       errhint("Declare it with SCROLL option to enable backward scan.")));

          if (portal->atStart || count <= 0)
+         {
              direction = NoMovementScanDirection;
+             count = 0;            /* don't pass negative count to executor */
+         }
          else
              direction = BackwardScanDirection;

*************** PortalRunSelect(Portal portal,
*** 978,988 ****
              count = 0;

          if (portal->holdStore)
!             nprocessed = RunFromStore(portal, direction, count, dest);
          else
          {
              PushActiveSnapshot(queryDesc->snapshot);
!             ExecutorRun(queryDesc, direction, count);
              nprocessed = queryDesc->estate->es_processed;
              PopActiveSnapshot();
          }
--- 978,988 ----
              count = 0;

          if (portal->holdStore)
!             nprocessed = RunFromStore(portal, direction, (uint64) count, dest);
          else
          {
              PushActiveSnapshot(queryDesc->snapshot);
!             ExecutorRun(queryDesc, direction, (uint64) count);
              nprocessed = queryDesc->estate->es_processed;
              PopActiveSnapshot();
          }
*************** PortalRunSelect(Portal portal,
*** 994,1015 ****
                  portal->atEnd = false;    /* OK to go forward now */
                  portal->portalPos++;    /* adjust for endpoint case */
              }
!             if (count == 0 ||
!                 (unsigned long) nprocessed < (unsigned long) count)
              {
                  portal->atStart = true; /* we retrieved 'em all */
                  portal->portalPos = 0;
-                 portal->posOverflow = false;
              }
              else
              {
-                 long        oldPos;
-
-                 oldPos = portal->portalPos;
                  portal->portalPos -= nprocessed;
-                 if (portal->portalPos > oldPos ||
-                     portal->portalPos <= 0)
-                     portal->posOverflow = true;
              }
          }
      }
--- 994,1007 ----
                  portal->atEnd = false;    /* OK to go forward now */
                  portal->portalPos++;    /* adjust for endpoint case */
              }
!             if (count == 0 || nprocessed < (uint64) count)
              {
                  portal->atStart = true; /* we retrieved 'em all */
                  portal->portalPos = 0;
              }
              else
              {
                  portal->portalPos -= nprocessed;
              }
          }
      }
*************** FillPortalStore(Portal portal, bool isTo
*** 1083,1093 ****
   * are run in the caller's memory context (since we have no estate).  Watch
   * out for memory leaks.
   */
! static uint32
! RunFromStore(Portal portal, ScanDirection direction, long count,
               DestReceiver *dest)
  {
!     long        current_tuple_count = 0;
      TupleTableSlot *slot;

      slot = MakeSingleTupleTableSlot(portal->tupDesc);
--- 1075,1085 ----
   * are run in the caller's memory context (since we have no estate).  Watch
   * out for memory leaks.
   */
! static uint64
! RunFromStore(Portal portal, ScanDirection direction, uint64 count,
               DestReceiver *dest)
  {
!     uint64        current_tuple_count = 0;
      TupleTableSlot *slot;

      slot = MakeSingleTupleTableSlot(portal->tupDesc);
*************** RunFromStore(Portal portal, ScanDirectio
*** 1136,1142 ****

      ExecDropSingleTupleTableSlot(slot);

!     return (uint32) current_tuple_count;
  }

  /*
--- 1128,1134 ----

      ExecDropSingleTupleTableSlot(slot);

!     return current_tuple_count;
  }

  /*
*************** PortalRunMulti(Portal portal, bool isTop
*** 1375,1389 ****
   *
   * Note: we presently assume that no callers of this want isTopLevel = true.
   *
   * Returns number of rows processed (suitable for use in result tag)
   */
! long
  PortalRunFetch(Portal portal,
                 FetchDirection fdirection,
                 long count,
                 DestReceiver *dest)
  {
!     long        result;
      Portal        saveActivePortal;
      ResourceOwner saveResourceOwner;
      MemoryContext savePortalContext;
--- 1367,1385 ----
   *
   * Note: we presently assume that no callers of this want isTopLevel = true.
   *
+  * count <= 0 is interpreted as a no-op: the destination gets started up
+  * and shut down, but nothing else happens.  Also, count == FETCH_ALL is
+  * interpreted as "all rows".  (cf FetchStmt.howMany)
+  *
   * Returns number of rows processed (suitable for use in result tag)
   */
! uint64
  PortalRunFetch(Portal portal,
                 FetchDirection fdirection,
                 long count,
                 DestReceiver *dest)
  {
!     uint64        result;
      Portal        saveActivePortal;
      ResourceOwner saveResourceOwner;
      MemoryContext savePortalContext;
*************** PortalRunFetch(Portal portal,
*** 1470,1478 ****
   * DoPortalRunFetch
   *        Guts of PortalRunFetch --- the portal context is already set up
   *
   * Returns number of rows processed (suitable for use in result tag)
   */
! static long
  DoPortalRunFetch(Portal portal,
                   FetchDirection fdirection,
                   long count,
--- 1466,1478 ----
   * DoPortalRunFetch
   *        Guts of PortalRunFetch --- the portal context is already set up
   *
+  * count <= 0 is interpreted as a no-op: the destination gets started up
+  * and shut down, but nothing else happens.  Also, count == FETCH_ALL is
+  * interpreted as "all rows".  (cf FetchStmt.howMany)
+  *
   * Returns number of rows processed (suitable for use in result tag)
   */
! static uint64
  DoPortalRunFetch(Portal portal,
                   FetchDirection fdirection,
                   long count,
*************** DoPortalRunFetch(Portal portal,
*** 1513,1520 ****
                   * we are.  In any case, we arrange to fetch the target row
                   * going forwards.
                   */
!                 if (portal->posOverflow || portal->portalPos == LONG_MAX ||
!                     count - 1 <= portal->portalPos / 2)
                  {
                      DoPortalRewind(portal);
                      if (count > 1)
--- 1513,1519 ----
                   * we are.  In any case, we arrange to fetch the target row
                   * going forwards.
                   */
!                 if ((uint64) (count - 1) <= portal->portalPos / 2)
                  {
                      DoPortalRewind(portal);
                      if (count > 1)
*************** DoPortalRunFetch(Portal portal,
*** 1523,1529 ****
                  }
                  else
                  {
!                     long        pos = portal->portalPos;

                      if (portal->atEnd)
                          pos++;    /* need one extra fetch if off end */
--- 1522,1528 ----
                  }
                  else
                  {
!                     uint64        pos = portal->portalPos;

                      if (portal->atEnd)
                          pos++;    /* need one extra fetch if off end */
*************** DoPortalRunFetch(Portal portal,
*** 1609,1615 ****
          if (dest->mydest == DestNone)
          {
              /* MOVE 0 returns 0/1 based on if FETCH 0 would return a row */
!             return on_row ? 1L : 0L;
          }
          else
          {
--- 1608,1614 ----
          if (dest->mydest == DestNone)
          {
              /* MOVE 0 returns 0/1 based on if FETCH 0 would return a row */
!             return on_row ? 1 : 0;
          }
          else
          {
*************** DoPortalRunFetch(Portal portal,
*** 1635,1646 ****
       */
      if (!forward && count == FETCH_ALL && dest->mydest == DestNone)
      {
!         long        result = portal->portalPos;

          if (result > 0 && !portal->atEnd)
              result--;
          DoPortalRewind(portal);
-         /* result is bogus if pos had overflowed, but it's best we can do */
          return result;
      }

--- 1634,1644 ----
       */
      if (!forward && count == FETCH_ALL && dest->mydest == DestNone)
      {
!         uint64        result = portal->portalPos;

          if (result > 0 && !portal->atEnd)
              result--;
          DoPortalRewind(portal);
          return result;
      }

*************** DoPortalRewind(Portal portal)
*** 1677,1681 ****
      portal->atStart = true;
      portal->atEnd = false;
      portal->portalPos = 0;
-     portal->posOverflow = false;
  }
--- 1675,1678 ----
diff --git a/src/backend/utils/adt/tsquery_rewrite.c b/src/backend/utils/adt/tsquery_rewrite.c
index 0870afd..28f328d 100644
*** a/src/backend/utils/adt/tsquery_rewrite.c
--- b/src/backend/utils/adt/tsquery_rewrite.c
*************** tsquery_rewrite_query(PG_FUNCTION_ARGS)
*** 260,266 ****
      SPIPlanPtr    plan;
      Portal        portal;
      bool        isnull;
-     int            i;

      if (query->size == 0)
      {
--- 260,265 ----
*************** tsquery_rewrite_query(PG_FUNCTION_ARGS)
*** 294,299 ****
--- 293,300 ----

      while (SPI_processed > 0 && tree)
      {
+         uint64        i;
+
          for (i = 0; i < SPI_processed && tree; i++)
          {
              Datum        qdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull);
diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c
index 186b3d3..f6d3fb5 100644
*** a/src/backend/utils/adt/tsvector_op.c
--- b/src/backend/utils/adt/tsvector_op.c
*************** static TSVectorStat *
*** 1682,1688 ****
  ts_stat_sql(MemoryContext persistentContext, text *txt, text *ws)
  {
      char       *query = text_to_cstring(txt);
-     int            i;
      TSVectorStat *stat;
      bool        isnull;
      Portal        portal;
--- 1682,1687 ----
*************** ts_stat_sql(MemoryContext persistentCont
*** 1746,1751 ****
--- 1745,1752 ----

      while (SPI_processed > 0)
      {
+         uint64        i;
+
          for (i = 0; i < SPI_processed; i++)
          {
              Datum        data = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull);
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 56179f8..7ed5bcb 100644
*** a/src/backend/utils/adt/xml.c
--- b/src/backend/utils/adt/xml.c
*************** static const char *map_sql_catalog_to_xm
*** 161,167 ****
  static const char *map_sql_type_to_xml_name(Oid typeoid, int typmod);
  static const char *map_sql_typecoll_to_xmlschema_types(List *tupdesc_list);
  static const char *map_sql_type_to_xmlschema_type(Oid typeoid, int typmod);
! static void SPI_sql_row_to_xmlelement(int rownum, StringInfo result,
                            char *tablename, bool nulls, bool tableforest,
                            const char *targetns, bool top_level);

--- 161,167 ----
  static const char *map_sql_type_to_xml_name(Oid typeoid, int typmod);
  static const char *map_sql_typecoll_to_xmlschema_types(List *tupdesc_list);
  static const char *map_sql_type_to_xmlschema_type(Oid typeoid, int typmod);
! static void SPI_sql_row_to_xmlelement(uint64 rownum, StringInfo result,
                            char *tablename, bool nulls, bool tableforest,
                            const char *targetns, bool top_level);

*************** _SPI_strdup(const char *s)
*** 2260,2266 ****
  static List *
  query_to_oid_list(const char *query)
  {
!     int            i;
      List       *list = NIL;

      SPI_execute(query, true, 0);
--- 2260,2266 ----
  static List *
  query_to_oid_list(const char *query)
  {
!     uint64        i;
      List       *list = NIL;

      SPI_execute(query, true, 0);
*************** cursor_to_xml(PG_FUNCTION_ARGS)
*** 2379,2385 ****

      StringInfoData result;
      Portal        portal;
!     int            i;

      initStringInfo(&result);

--- 2379,2385 ----

      StringInfoData result;
      Portal        portal;
!     uint64        i;

      initStringInfo(&result);

*************** query_to_xml_internal(const char *query,
*** 2454,2460 ****
  {
      StringInfo    result;
      char       *xmltn;
!     int            i;

      if (tablename)
          xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
--- 2454,2460 ----
  {
      StringInfo    result;
      char       *xmltn;
!     uint64        i;

      if (tablename)
          xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
*************** map_sql_type_to_xmlschema_type(Oid typeo
*** 3532,3538 ****
   * SPI cursor.  See also SQL/XML:2008 section 9.10.
   */
  static void
! SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename,
                            bool nulls, bool tableforest,
                            const char *targetns, bool top_level)
  {
--- 3532,3538 ----
   * SPI cursor.  See also SQL/XML:2008 section 9.10.
   */
  static void
! SPI_sql_row_to_xmlelement(uint64 rownum, StringInfo result, char *tablename,
                            bool nulls, bool tableforest,
                            const char *targetns, bool top_level)
  {
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 1a44085..44fac27 100644
*** a/src/include/executor/executor.h
--- b/src/include/executor/executor.h
*************** extern PGDLLIMPORT ExecutorStart_hook_ty
*** 80,86 ****
  /* Hook for plugins to get control in ExecutorRun() */
  typedef void (*ExecutorRun_hook_type) (QueryDesc *queryDesc,
                                                     ScanDirection direction,
!                                                    long count);
  extern PGDLLIMPORT ExecutorRun_hook_type ExecutorRun_hook;

  /* Hook for plugins to get control in ExecutorFinish() */
--- 80,86 ----
  /* Hook for plugins to get control in ExecutorRun() */
  typedef void (*ExecutorRun_hook_type) (QueryDesc *queryDesc,
                                                     ScanDirection direction,
!                                                    uint64 count);
  extern PGDLLIMPORT ExecutorRun_hook_type ExecutorRun_hook;

  /* Hook for plugins to get control in ExecutorFinish() */
*************** extern TupleTableSlot *ExecFilterJunk(Ju
*** 175,183 ****
  extern void ExecutorStart(QueryDesc *queryDesc, int eflags);
  extern void standard_ExecutorStart(QueryDesc *queryDesc, int eflags);
  extern void ExecutorRun(QueryDesc *queryDesc,
!             ScanDirection direction, long count);
  extern void standard_ExecutorRun(QueryDesc *queryDesc,
!                      ScanDirection direction, long count);
  extern void ExecutorFinish(QueryDesc *queryDesc);
  extern void standard_ExecutorFinish(QueryDesc *queryDesc);
  extern void ExecutorEnd(QueryDesc *queryDesc);
--- 175,183 ----
  extern void ExecutorStart(QueryDesc *queryDesc, int eflags);
  extern void standard_ExecutorStart(QueryDesc *queryDesc, int eflags);
  extern void ExecutorRun(QueryDesc *queryDesc,
!             ScanDirection direction, uint64 count);
  extern void standard_ExecutorRun(QueryDesc *queryDesc,
!                      ScanDirection direction, uint64 count);
  extern void ExecutorFinish(QueryDesc *queryDesc);
  extern void standard_ExecutorFinish(QueryDesc *queryDesc);
  extern void ExecutorEnd(QueryDesc *queryDesc);
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index 8c3ca26..1792fb1 100644
*** a/src/include/executor/spi.h
--- b/src/include/executor/spi.h
***************
*** 21,28 ****
  typedef struct SPITupleTable
  {
      MemoryContext tuptabcxt;    /* memory context of result table */
!     uint32        alloced;        /* # of alloced vals */
!     uint32        free;            /* # of free vals */
      TupleDesc    tupdesc;        /* tuple descriptor */
      HeapTuple  *vals;            /* tuples */
      slist_node    next;            /* link for internal bookkeeping */
--- 21,28 ----
  typedef struct SPITupleTable
  {
      MemoryContext tuptabcxt;    /* memory context of result table */
!     uint64        alloced;        /* # of alloced vals */
!     uint64        free;            /* # of free vals */
      TupleDesc    tupdesc;        /* tuple descriptor */
      HeapTuple  *vals;            /* tuples */
      slist_node    next;            /* link for internal bookkeeping */
*************** typedef struct _SPI_plan *SPIPlanPtr;
*** 59,65 ****
  #define SPI_OK_UPDATE_RETURNING 13
  #define SPI_OK_REWRITTEN        14

! extern PGDLLIMPORT uint32 SPI_processed;
  extern PGDLLIMPORT Oid SPI_lastoid;
  extern PGDLLIMPORT SPITupleTable *SPI_tuptable;
  extern PGDLLIMPORT int SPI_result;
--- 59,65 ----
  #define SPI_OK_UPDATE_RETURNING 13
  #define SPI_OK_REWRITTEN        14

! extern PGDLLIMPORT uint64 SPI_processed;
  extern PGDLLIMPORT Oid SPI_lastoid;
  extern PGDLLIMPORT SPITupleTable *SPI_tuptable;
  extern PGDLLIMPORT int SPI_result;
diff --git a/src/include/executor/spi_priv.h b/src/include/executor/spi_priv.h
index 3187230..e8084df 100644
*** a/src/include/executor/spi_priv.h
--- b/src/include/executor/spi_priv.h
***************
*** 21,27 ****
  typedef struct
  {
      /* current results */
!     uint32        processed;        /* by Executor */
      Oid            lastoid;
      SPITupleTable *tuptable;    /* tuptable currently being built */

--- 21,27 ----
  typedef struct
  {
      /* current results */
!     uint64        processed;        /* by Executor */
      Oid            lastoid;
      SPITupleTable *tuptable;    /* tuptable currently being built */

diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index b6ae93f..e73a824 100644
*** a/src/include/funcapi.h
--- b/src/include/funcapi.h
*************** typedef struct FuncCallContext
*** 62,68 ****
       * call_cntr is initialized to 0 for you by SRF_FIRSTCALL_INIT(), and
       * incremented for you every time SRF_RETURN_NEXT() is called.
       */
!     uint32        call_cntr;

      /*
       * OPTIONAL maximum number of calls
--- 62,68 ----
       * call_cntr is initialized to 0 for you by SRF_FIRSTCALL_INIT(), and
       * incremented for you every time SRF_RETURN_NEXT() is called.
       */
!     uint64        call_cntr;

      /*
       * OPTIONAL maximum number of calls
*************** typedef struct FuncCallContext
*** 71,77 ****
       * not set, you must provide alternative means to know when the function
       * is done.
       */
!     uint32        max_calls;

      /*
       * OPTIONAL pointer to result slot
--- 71,77 ----
       * not set, you must provide alternative means to know when the function
       * is done.
       */
!     uint64        max_calls;

      /*
       * OPTIONAL pointer to result slot
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 064a050..d35ec81 100644
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
*************** typedef struct EState
*** 387,393 ****

      List       *es_rowMarks;    /* List of ExecRowMarks */

!     uint32        es_processed;    /* # of tuples processed */
      Oid            es_lastoid;        /* last oid processed (by INSERT) */

      int            es_top_eflags;    /* eflags passed to ExecutorStart */
--- 387,393 ----

      List       *es_rowMarks;    /* List of ExecRowMarks */

!     uint64        es_processed;    /* # of tuples processed */
      Oid            es_lastoid;        /* last oid processed (by INSERT) */

      int            es_top_eflags;    /* eflags passed to ExecutorStart */
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 453147e..cde939b 100644
*** a/src/include/postgres.h
--- b/src/include/postgres.h
*************** extern Datum Int64GetDatum(int64 X);
*** 630,635 ****
--- 630,662 ----
  #endif

  /*
+  * DatumGetUInt64
+  *        Returns 64-bit unsigned integer value of a datum.
+  *
+  * Note: this macro hides whether int64 is pass by value or by reference.
+  */
+
+ #ifdef USE_FLOAT8_BYVAL
+ #define DatumGetUInt64(X) ((uint64) GET_8_BYTES(X))
+ #else
+ #define DatumGetUInt64(X) (* ((uint64 *) DatumGetPointer(X)))
+ #endif
+
+ /*
+  * UInt64GetDatum
+  *        Returns datum representation for a 64-bit unsigned integer.
+  *
+  * Note: if int64 is pass by reference, this function returns a reference
+  * to palloc'd space.
+  */
+
+ #ifdef USE_FLOAT8_BYVAL
+ #define UInt64GetDatum(X) ((Datum) SET_8_BYTES(X))
+ #else
+ #define UInt64GetDatum(X) Int64GetDatum((int64) (X))
+ #endif
+
+ /*
   * DatumGetFloat4
   *        Returns 4-byte floating point value of a datum.
   *
diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h
index 4f1fd13..e04fc43 100644
*** a/src/include/tcop/pquery.h
--- b/src/include/tcop/pquery.h
*************** extern bool PortalRun(Portal portal, lon
*** 37,43 ****
            DestReceiver *dest, DestReceiver *altdest,
            char *completionTag);

! extern long PortalRunFetch(Portal portal,
                 FetchDirection fdirection,
                 long count,
                 DestReceiver *dest);
--- 37,43 ----
            DestReceiver *dest, DestReceiver *altdest,
            char *completionTag);

! extern uint64 PortalRunFetch(Portal portal,
                 FetchDirection fdirection,
                 long count,
                 DestReceiver *dest);
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index 4236215..7250c9c 100644
*** a/src/include/utils/portal.h
--- b/src/include/utils/portal.h
*************** typedef struct PortalData
*** 166,180 ****
       * atStart, atEnd and portalPos indicate the current cursor position.
       * portalPos is zero before the first row, N after fetching N'th row of
       * query.  After we run off the end, portalPos = # of rows in query, and
!      * atEnd is true.  If portalPos overflows, set posOverflow (this causes us
!      * to stop relying on its value for navigation).  Note that atStart
!      * implies portalPos == 0, but not the reverse (portalPos could have
!      * overflowed).
       */
      bool        atStart;
      bool        atEnd;
!     bool        posOverflow;
!     long        portalPos;

      /* Presentation data, primarily used by the pg_cursors system view */
      TimestampTz creation_time;    /* time at which this portal was defined */
--- 166,179 ----
       * atStart, atEnd and portalPos indicate the current cursor position.
       * portalPos is zero before the first row, N after fetching N'th row of
       * query.  After we run off the end, portalPos = # of rows in query, and
!      * atEnd is true.  Note that atStart implies portalPos == 0, but not the
!      * reverse: we might have backed up only as far as the first row, not to
!      * the start.  Also note that various code inspects atStart and atEnd, but
!      * only the portal movement routines should touch portalPos.
       */
      bool        atStart;
      bool        atEnd;
!     uint64        portalPos;

      /* Presentation data, primarily used by the pg_cursors system view */
      TimestampTz creation_time;    /* time at which this portal was defined */
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index cd917ab..e65fe8d 100644
*** a/src/pl/plperl/plperl.c
--- b/src/pl/plperl/plperl.c
*************** static Datum plperl_hash_to_datum(SV *sr
*** 281,287 ****
  static void plperl_init_shared_libs(pTHX);
  static void plperl_trusted_init(void);
  static void plperl_untrusted_init(void);
! static HV  *plperl_spi_execute_fetch_result(SPITupleTable *, int, int);
  static char *hek2cstr(HE *he);
  static SV **hv_store_string(HV *hv, const char *key, SV *val);
  static SV **hv_fetch_string(HV *hv, const char *key);
--- 281,287 ----
  static void plperl_init_shared_libs(pTHX);
  static void plperl_trusted_init(void);
  static void plperl_untrusted_init(void);
! static HV  *plperl_spi_execute_fetch_result(SPITupleTable *, uint64, int);
  static char *hek2cstr(HE *he);
  static SV **hv_store_string(HV *hv, const char *key, SV *val);
  static SV **hv_fetch_string(HV *hv, const char *key);
*************** plperl_ref_from_pg_array(Datum arg, Oid
*** 1472,1478 ****

      hv = newHV();
      (void) hv_store(hv, "array", 5, av, 0);
!     (void) hv_store(hv, "typeoid", 7, newSViv(typid), 0);

      return sv_bless(newRV_noinc((SV *) hv),
                      gv_stashpv("PostgreSQL::InServer::ARRAY", 0));
--- 1472,1478 ----

      hv = newHV();
      (void) hv_store(hv, "array", 5, av, 0);
!     (void) hv_store(hv, "typeoid", 7, newSVuv(typid), 0);

      return sv_bless(newRV_noinc((SV *) hv),
                      gv_stashpv("PostgreSQL::InServer::ARRAY", 0));
*************** plperl_spi_exec(char *query, int limit)
*** 3091,3097 ****


  static HV  *
! plperl_spi_execute_fetch_result(SPITupleTable *tuptable, int processed,
                                  int status)
  {
      HV           *result;
--- 3091,3097 ----


  static HV  *
! plperl_spi_execute_fetch_result(SPITupleTable *tuptable, uint64 processed,
                                  int status)
  {
      HV           *result;
*************** plperl_spi_execute_fetch_result(SPITuple
*** 3103,3115 ****
      hv_store_string(result, "status",
                      cstr2sv(SPI_result_code_string(status)));
      hv_store_string(result, "processed",
!                     newSViv(processed));

      if (status > 0 && tuptable)
      {
          AV           *rows;
          SV           *row;
!         int            i;

          rows = newAV();
          av_extend(rows, processed);
--- 3103,3115 ----
      hv_store_string(result, "status",
                      cstr2sv(SPI_result_code_string(status)));
      hv_store_string(result, "processed",
!                     newSVuv(processed)); /* XXX ok? */

      if (status > 0 && tuptable)
      {
          AV           *rows;
          SV           *row;
!         uint64        i;

          rows = newAV();
          av_extend(rows, processed);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index bd58d5f..b63ecac 100644
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
*************** exec_stmt_getdiag(PLpgSQL_execstate *est
*** 1601,1608 ****
          {
              case PLPGSQL_GETDIAG_ROW_COUNT:
                  exec_assign_value(estate, var,
!                                   UInt32GetDatum(estate->eval_processed),
!                                   false, INT4OID, -1);
                  break;

              case PLPGSQL_GETDIAG_RESULT_OID:
--- 1601,1608 ----
          {
              case PLPGSQL_GETDIAG_ROW_COUNT:
                  exec_assign_value(estate, var,
!                                   UInt64GetDatum(estate->eval_processed),
!                                   false, INT8OID, -1);
                  break;

              case PLPGSQL_GETDIAG_RESULT_OID:
*************** exec_stmt_return_query(PLpgSQL_execstate
*** 2856,2862 ****
                         PLpgSQL_stmt_return_query *stmt)
  {
      Portal        portal;
!     uint32        processed = 0;
      TupleConversionMap *tupmap;

      if (!estate->retisset)
--- 2856,2862 ----
                         PLpgSQL_stmt_return_query *stmt)
  {
      Portal        portal;
!     uint64        processed = 0;
      TupleConversionMap *tupmap;

      if (!estate->retisset)
*************** exec_stmt_return_query(PLpgSQL_execstate
*** 2887,2893 ****

      while (true)
      {
!         int            i;

          SPI_cursor_fetch(portal, true, 50);
          if (SPI_processed == 0)
--- 2887,2893 ----

      while (true)
      {
!         uint64            i;

          SPI_cursor_fetch(portal, true, 50);
          if (SPI_processed == 0)
*************** exec_stmt_execsql(PLpgSQL_execstate *est
*** 3579,3585 ****
      if (stmt->into)
      {
          SPITupleTable *tuptab = SPI_tuptable;
!         uint32        n = SPI_processed;
          PLpgSQL_rec *rec = NULL;
          PLpgSQL_row *row = NULL;

--- 3579,3585 ----
      if (stmt->into)
      {
          SPITupleTable *tuptab = SPI_tuptable;
!         uint64        n = SPI_processed;
          PLpgSQL_rec *rec = NULL;
          PLpgSQL_row *row = NULL;

*************** exec_stmt_dynexecute(PLpgSQL_execstate *
*** 3769,3775 ****
      if (stmt->into)
      {
          SPITupleTable *tuptab = SPI_tuptable;
!         uint32        n = SPI_processed;
          PLpgSQL_rec *rec = NULL;
          PLpgSQL_row *row = NULL;

--- 3769,3775 ----
      if (stmt->into)
      {
          SPITupleTable *tuptab = SPI_tuptable;
!         uint64        n = SPI_processed;
          PLpgSQL_rec *rec = NULL;
          PLpgSQL_row *row = NULL;

*************** exec_stmt_fetch(PLpgSQL_execstate *estat
*** 4043,4049 ****
      SPITupleTable *tuptab;
      Portal        portal;
      char       *curname;
!     uint32        n;

      /* ----------
       * Get the portal of the cursor by name
--- 4043,4049 ----
      SPITupleTable *tuptab;
      Portal        portal;
      char       *curname;
!     uint64        n;

      /* ----------
       * Get the portal of the cursor by name
*************** exec_for_query(PLpgSQL_execstate *estate
*** 5151,5157 ****
      SPITupleTable *tuptab;
      bool        found = false;
      int            rc = PLPGSQL_RC_OK;
!     int            n;

      /*
       * Determine if we assign to a record or a row
--- 5151,5157 ----
      SPITupleTable *tuptab;
      bool        found = false;
      int            rc = PLPGSQL_RC_OK;
!     uint64        n;

      /*
       * Determine if we assign to a record or a row
*************** exec_for_query(PLpgSQL_execstate *estate
*** 5182,5188 ****
       * If the query didn't return any rows, set the target to NULL and fall
       * through with found = false.
       */
!     if (n <= 0)
      {
          exec_move_row(estate, rec, row, NULL, tuptab->tupdesc);
          exec_eval_cleanup(estate);
--- 5182,5188 ----
       * If the query didn't return any rows, set the target to NULL and fall
       * through with found = false.
       */
!     if (n == 0)
      {
          exec_move_row(estate, rec, row, NULL, tuptab->tupdesc);
          exec_eval_cleanup(estate);
*************** exec_for_query(PLpgSQL_execstate *estate
*** 5195,5201 ****
       */
      while (n > 0)
      {
!         int            i;

          for (i = 0; i < n; i++)
          {
--- 5195,5201 ----
       */
      while (n > 0)
      {
!         uint64        i;

          for (i = 0; i < n; i++)
          {
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index a1e900d..2deece4 100644
*** a/src/pl/plpgsql/src/plpgsql.h
--- b/src/pl/plpgsql/src/plpgsql.h
*************** typedef struct PLpgSQL_execstate
*** 820,826 ****

      /* temporary state for results from evaluation of query or expr */
      SPITupleTable *eval_tuptable;
!     uint32        eval_processed;
      Oid            eval_lastoid;
      ExprContext *eval_econtext; /* for executing simple expressions */

--- 820,826 ----

      /* temporary state for results from evaluation of query or expr */
      SPITupleTable *eval_tuptable;
!     uint64        eval_processed;
      Oid            eval_lastoid;
      ExprContext *eval_econtext; /* for executing simple expressions */

diff --git a/src/pl/plpython/plpy_cursorobject.c b/src/pl/plpython/plpy_cursorobject.c
index 103571b..3dd9053 100644
*** a/src/pl/plpython/plpy_cursorobject.c
--- b/src/pl/plpython/plpy_cursorobject.c
*************** PLy_cursor_fetch(PyObject *self, PyObjec
*** 450,456 ****

          if (SPI_processed != 0)
          {
!             int            i;

              Py_DECREF(ret->rows);
              ret->rows = PyList_New(SPI_processed);
--- 450,456 ----

          if (SPI_processed != 0)
          {
!             uint64        i;

              Py_DECREF(ret->rows);
              ret->rows = PyList_New(SPI_processed);
diff --git a/src/pl/plpython/plpy_spi.c b/src/pl/plpython/plpy_spi.c
index 58e78ec..8f9b71d 100644
*** a/src/pl/plpython/plpy_spi.c
--- b/src/pl/plpython/plpy_spi.c
***************
*** 29,35 ****

  static PyObject *PLy_spi_execute_query(char *query, long limit);
  static PyObject *PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit);
! static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status);
  static void PLy_spi_exception_set(PyObject *excclass, ErrorData *edata);


--- 29,36 ----

  static PyObject *PLy_spi_execute_query(char *query, long limit);
  static PyObject *PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit);
! static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *tuptable,
!                                               uint64 rows, int status);
  static void PLy_spi_exception_set(PyObject *excclass, ErrorData *edata);


*************** PLy_spi_execute_query(char *query, long
*** 382,388 ****
  }

  static PyObject *
! PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
  {
      PLyResultObject *result;
      volatile MemoryContext oldcontext;
--- 383,389 ----
  }

  static PyObject *
! PLy_spi_execute_fetch_result(SPITupleTable *tuptable, uint64 rows, int status)
  {
      PLyResultObject *result;
      volatile MemoryContext oldcontext;
*************** PLy_spi_execute_fetch_result(SPITupleTab
*** 399,405 ****
      else if (status > 0 && tuptable != NULL)
      {
          PLyTypeInfo args;
-         int            i;
          MemoryContext cxt;

          Py_DECREF(result->nrows);
--- 400,405 ----
*************** PLy_spi_execute_fetch_result(SPITupleTab
*** 419,424 ****
--- 419,426 ----

              if (rows)
              {
+                 uint64        i;
+
                  Py_DECREF(result->rows);
                  result->rows = PyList_New(rows);

diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 105b618..47ea015 100644
*** a/src/pl/tcl/pltcl.c
--- b/src/pl/tcl/pltcl.c
*************** static int pltcl_process_SPI_result(Tcl_
*** 226,232 ****
                           Tcl_Obj *loop_body,
                           int spi_rc,
                           SPITupleTable *tuptable,
!                          int ntuples);
  static int pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
                    int objc, Tcl_Obj *const objv[]);
  static int pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
--- 226,232 ----
                           Tcl_Obj *loop_body,
                           int spi_rc,
                           SPITupleTable *tuptable,
!                          uint64 ntuples);
  static int pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
                    int objc, Tcl_Obj *const objv[]);
  static int pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
*************** static int pltcl_SPI_lastoid(ClientData
*** 235,241 ****
                    int objc, Tcl_Obj *const objv[]);

  static void pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname,
!                        int tupno, HeapTuple tuple, TupleDesc tupdesc);
  static Tcl_Obj *pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc);


--- 235,241 ----
                    int objc, Tcl_Obj *const objv[]);

  static void pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname,
!                        uint64 tupno, HeapTuple tuple, TupleDesc tupdesc);
  static Tcl_Obj *pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc);


*************** pltcl_init_load_unknown(Tcl_Interp *inte
*** 481,487 ****
      int            tcl_rc;
      Tcl_DString unknown_src;
      char       *part;
!     int            i;
      int            fno;

      /************************************************************
--- 481,487 ----
      int            tcl_rc;
      Tcl_DString unknown_src;
      char       *part;
!     uint64        i;
      int            fno;

      /************************************************************
*************** pltcl_process_SPI_result(Tcl_Interp *int
*** 2007,2016 ****
                           Tcl_Obj *loop_body,
                           int spi_rc,
                           SPITupleTable *tuptable,
!                          int ntuples)
  {
      int            my_rc = TCL_OK;
-     int            i;
      int            loop_rc;
      HeapTuple  *tuples;
      TupleDesc    tupdesc;
--- 2007,2015 ----
                           Tcl_Obj *loop_body,
                           int spi_rc,
                           SPITupleTable *tuptable,
!                          uint64 ntuples)
  {
      int            my_rc = TCL_OK;
      int            loop_rc;
      HeapTuple  *tuples;
      TupleDesc    tupdesc;
*************** pltcl_process_SPI_result(Tcl_Interp *int
*** 2060,2065 ****
--- 2059,2066 ----
                   * There is a loop body - process all tuples and evaluate the
                   * body on each
                   */
+                 uint64        i;
+
                  for (i = 0; i < ntuples; i++)
                  {
                      pltcl_set_tuple_values(interp, arrayname, i,
*************** pltcl_SPI_lastoid(ClientData cdata, Tcl_
*** 2472,2478 ****
   **********************************************************************/
  static void
  pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname,
!                        int tupno, HeapTuple tuple, TupleDesc tupdesc)
  {
      int            i;
      char       *outputstr;
--- 2473,2479 ----
   **********************************************************************/
  static void
  pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname,
!                        uint64 tupno, HeapTuple tuple, TupleDesc tupdesc)
  {
      int            i;
      char       *outputstr;
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index 6367ce7..e7826a4 100644
*** a/src/test/regress/regress.c
--- b/src/test/regress/regress.c
*************** funny_dup17(PG_FUNCTION_ARGS)
*** 362,368 ****
                 *fieldval,
                 *fieldtype;
      char       *when;
!     int            inserted;
      int            selected = 0;
      int            ret;

--- 362,368 ----
                 *fieldval,
                 *fieldtype;
      char       *when;
!     uint64        inserted;
      int            selected = 0;
      int            ret;

*************** funny_dup17(PG_FUNCTION_ARGS)
*** 443,449 ****
                                                                          ))));
      }

!     elog(DEBUG4, "funny_dup17 (fired %s) on level %3d: %d/%d tuples inserted/selected",
           when, *level, inserted, selected);

      SPI_finish();
--- 443,449 ----
                                                                          ))));
      }

!     elog(DEBUG4, "funny_dup17 (fired %s) on level %3d: " UINT64_FORMAT "/%d tuples inserted/selected",
           when, *level, inserted, selected);

      SPI_finish();

Re: pl/pgSQL, get diagnostics and big data

От
Petr Jelinek
Дата:
On 12/03/16 04:30, Tom Lane wrote:
>
> 1. I found two places (marked XXX in this patch) that are using strtoul()
> to parse a tuple count back out of a command tag.  That won't do anymore.
> pg_stat_statements has a messy hack for the same problem (look for
> HAVE_STRTOULL), which is probably what we want to do, but not by
> copy-and-pasting #ifdef HAVE_STRTOULL into multiple places.  I'd be
> inclined to provide a utility function "pg_strtouint64" or some such
> to encapsulate that.  (numutils.c might be a good place for it.)
>

Hmm, I thought that solution is not really portable for 64bit numbers 
and only is allowed in pg_stat_statements because worst case it will cut 
the number to 32bit int and misreport but won't break anything there. 
For example windows IIRC need _strtoui64 for this.

I once wrote (well copy-pasted from BDS + some #define wrappers) 
portable version of that, see the 0004 and bottom of 0003 in 
http://www.postgresql.org/message-id/557D9DED.2080204@2ndquadrant.com (I 
think at minimum what the 0003 does in c.h is needed).

--   Petr Jelinek                  http://www.2ndQuadrant.com/  PostgreSQL Development, 24x7 Support, Training &
Services



Re: pl/pgSQL, get diagnostics and big data

От
Tom Lane
Дата:
Petr Jelinek <petr@2ndquadrant.com> writes:
> On 12/03/16 04:30, Tom Lane wrote:
>> 1. I found two places (marked XXX in this patch) that are using strtoul()
>> to parse a tuple count back out of a command tag.  That won't do anymore.
>> pg_stat_statements has a messy hack for the same problem (look for
>> HAVE_STRTOULL), which is probably what we want to do, but not by
>> copy-and-pasting #ifdef HAVE_STRTOULL into multiple places.  I'd be
>> inclined to provide a utility function "pg_strtouint64" or some such
>> to encapsulate that.  (numutils.c might be a good place for it.)

> Hmm, I thought that solution is not really portable for 64bit numbers 
> and only is allowed in pg_stat_statements because worst case it will cut 
> the number to 32bit int and misreport but won't break anything there. 
> For example windows IIRC need _strtoui64 for this.

OK, we can use _strtoui64() on Windows.

> I once wrote (well copy-pasted from BDS + some #define wrappers) 
> portable version of that, see the 0004 and bottom of 0003 in 
> http://www.postgresql.org/message-id/557D9DED.2080204@2ndquadrant.com (I 
> think at minimum what the 0003 does in c.h is needed).

Meh.  That seems like pretty substantial overkill.  Given that we assume
platforms have working 64-bit support these days, what's the probability
that they don't have an appropriate strtoXXX function?  Or on one that
doesn't, that anyone will ever try to run >4G-tuple results through the
relevant code paths?

For the moment I'm just going to do this:

uint64
pg_strtouint64(const char *str, char **endptr, int base)
{
#ifdef WIN32return _strtoui64(str, endptr, base);
#elif defined(HAVE_STRTOULL) && SIZEOF_LONG < 8return strtoull(str, endptr, base);
#elsereturn strtoul(str, endptr, base);
#endif
}

If there ever seems to be any practical value in improving it, we
can do that later.
        regards, tom lane



Re: pl/pgSQL, get diagnostics and big data

От
Tom Lane
Дата:
I wrote:
> 2. As I was just complaining to -hackers, plpython plperl and pltcl
> all now contain attempts to pass uint64 values (from SPI_processed)
> into language-specific functions.  We need to figure out whether
> that will overflow and whether it's worth doing something about.

I fixed this along the lines suggested by Salvador Fandino, viz convert
to the language's equivalent of "double" if it wouldn't fit in int
or long respectively.  Tcl turns out to have a native int64 type
("WideInt") so that was slightly less messy.

I've pushed this so we can get some buildfarm testing, but it wouldn't
be a bad idea for someone to review the committed patch.  Chasing all
the dependencies was tedious and I'm still not real sure I found them
all.
        regards, tom lane