Widen query numbers-of-tuples-processed counters to uint64.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 12 Mar 2016 21:05:10 +0000 (16:05 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 12 Mar 2016 21:05:29 +0000 (16:05 -0500)
This patch widens SPI_processed, EState's es_processed field, PortalData's
portalPos field, FuncCallContext's call_cntr and max_calls fields,
ExecutorRun's count argument, PortalRunFetch's result, and the max number
of rows in a SPITupleTable to uint64, and deals with (I hope) all the
ensuing fallout.  Some of these values were declared uint32 before, and
others "long".

I also removed PortalData's posOverflow field, since that logic seems
pretty useless given that portalPos is now always 64 bits.

The user-visible results are that command tags for SELECT etc will
correctly report tuple counts larger than 4G, as will plpgsql's GET
GET DIAGNOSTICS ... ROW_COUNT command.  Queries processing more tuples
than that are still not exactly the norm, but they're becoming more
common.

Most values associated with FETCH/MOVE distances, such as PortalRun's count
argument and the count argument of most SPI functions that have one, remain
declared as "long".  It's not clear whether it would be worth promoting
those to int64; but it would definitely be a large dollop of additional
API churn on top of this, and it would only help 32-bit platforms which
seem relatively less likely to see any benefit.

Andreas Scherbaum, reviewed by Christian Ullrich, additional hacking by me

32 files changed:
contrib/auto_explain/auto_explain.c
contrib/pg_stat_statements/pg_stat_statements.c
contrib/spi/refint.c
contrib/tablefunc/tablefunc.c
contrib/xml2/xpath.c
doc/src/sgml/spi.sgml
src/backend/commands/createas.c
src/backend/commands/portalcmds.c
src/backend/executor/execMain.c
src/backend/executor/functions.c
src/backend/executor/spi.c
src/backend/tcop/pquery.c
src/backend/utils/adt/numutils.c
src/backend/utils/adt/tsquery_rewrite.c
src/backend/utils/adt/tsvector_op.c
src/backend/utils/adt/xml.c
src/include/executor/executor.h
src/include/executor/spi.h
src/include/executor/spi_priv.h
src/include/funcapi.h
src/include/nodes/execnodes.h
src/include/postgres.h
src/include/tcop/pquery.h
src/include/utils/builtins.h
src/include/utils/portal.h
src/pl/plperl/plperl.c
src/pl/plpgsql/src/pl_exec.c
src/pl/plpgsql/src/plpgsql.h
src/pl/plpython/plpy_cursorobject.c
src/pl/plpython/plpy_spi.c
src/pl/tcl/pltcl.c
src/test/regress/regress.c

index 76d183156772bde9b8fd4609fc13cabfe21ae4ae..6708d817fba43719663adb17e6a2321a4cd59053 100644 (file)
@@ -61,7 +61,7 @@ void      _PG_fini(void);
 static void explain_ExecutorStart(QueryDesc *queryDesc, int eflags);
 static void explain_ExecutorRun(QueryDesc *queryDesc,
                    ScanDirection direction,
-                   long count);
+                   uint64 count);
 static void explain_ExecutorFinish(QueryDesc *queryDesc);
 static void explain_ExecutorEnd(QueryDesc *queryDesc);
 
@@ -257,7 +257,7 @@ explain_ExecutorStart(QueryDesc *queryDesc, int eflags)
  * ExecutorRun hook: all we need do is track nesting depth
  */
 static void
-explain_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long count)
+explain_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count)
 {
    nesting_level++;
    PG_TRY();
index 9ce60e696c9261ecae80ccb5a374764a1993cf2d..3d9b8e45d9fa7db1420c85073483f0bbb7e26ff5 100644 (file)
@@ -289,7 +289,7 @@ static void pgss_post_parse_analyze(ParseState *pstate, Query *query);
 static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags);
 static void pgss_ExecutorRun(QueryDesc *queryDesc,
                 ScanDirection direction,
-                long count);
+                uint64 count);
 static void pgss_ExecutorFinish(QueryDesc *queryDesc);
 static void pgss_ExecutorEnd(QueryDesc *queryDesc);
 static void pgss_ProcessUtility(Node *parsetree, const char *queryString,
@@ -866,7 +866,7 @@ pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
  * ExecutorRun hook: all we need do is track nesting depth
  */
 static void
-pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long count)
+pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count)
 {
    nested_level++;
    PG_TRY();
@@ -1001,13 +1001,7 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString,
        /* parse command tag to retrieve the number of affected rows. */
        if (completionTag &&
            strncmp(completionTag, "COPY ", 5) == 0)
-       {
-#ifdef HAVE_STRTOULL
-           rows = strtoull(completionTag + 5, NULL, 10);
-#else
-           rows = strtoul(completionTag + 5, NULL, 10);
-#endif
-       }
+           rows = pg_strtouint64(completionTag + 5, NULL, 10);
        else
            rows = 0;
 
index 26022107410abdd795d43f00669fbd937fbe575c..01dd717522c42ecbe46878470bdbaa30864e13d5 100644 (file)
@@ -593,7 +593,7 @@ check_foreign_key(PG_FUNCTION_ARGS)
        else
        {
 #ifdef REFINT_VERBOSE
-           elog(NOTICE, "%s: %d tuple(s) of %s are %s",
+           elog(NOTICE, "%s: " UINT64_FORMAT " tuple(s) of %s are %s",
                 trigger->tgname, SPI_processed, relname,
                 (action == 'c') ? "deleted" : "set to null");
 #endif
index 1ea4a635cd5b07453c8b3ea9dc4b11f22f1e2833..787c02d08fcfd30e3e5914db522976dcef87e41f 100644 (file)
@@ -120,7 +120,7 @@ typedef struct
 typedef struct crosstab_cat_desc
 {
    char       *catname;        /* full category name */
-   int         attidx;         /* zero based */
+   uint64      attidx;         /* zero based */
 } crosstab_cat_desc;
 
 #define MAX_CATNAME_LEN            NAMEDATALEN
@@ -174,8 +174,8 @@ Datum
 normal_rand(PG_FUNCTION_ARGS)
 {
    FuncCallContext *funcctx;
-   int         call_cntr;
-   int         max_calls;
+   uint64      call_cntr;
+   uint64      max_calls;
    normal_rand_fctx *fctx;
    float8      mean;
    float8      stddev;
@@ -352,8 +352,8 @@ crosstab(PG_FUNCTION_ARGS)
    ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
    Tuplestorestate *tupstore;
    TupleDesc   tupdesc;
-   int         call_cntr;
-   int         max_calls;
+   uint64      call_cntr;
+   uint64      max_calls;
    AttInMetadata *attinmeta;
    SPITupleTable *spi_tuptable;
    TupleDesc   spi_tupdesc;
@@ -364,7 +364,7 @@ crosstab(PG_FUNCTION_ARGS)
    MemoryContext per_query_ctx;
    MemoryContext oldcontext;
    int         ret;
-   int         proc;
+   uint64      proc;
 
    /* check to see if caller supports us returning a tuplestore */
    if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
@@ -389,7 +389,7 @@ crosstab(PG_FUNCTION_ARGS)
    proc = SPI_processed;
 
    /* If no qualifying tuples, fall out early */
-   if (ret != SPI_OK_SELECT || proc <= 0)
+   if (ret != SPI_OK_SELECT || proc == 0)
    {
        SPI_finish();
        rsinfo->isDone = ExprEndResult;
@@ -708,7 +708,7 @@ load_categories_hash(char *cats_sql, MemoryContext per_query_ctx)
    HTAB       *crosstab_hash;
    HASHCTL     ctl;
    int         ret;
-   int         proc;
+   uint64      proc;
    MemoryContext SPIcontext;
 
    /* initialize the category hash table */
@@ -740,7 +740,7 @@ load_categories_hash(char *cats_sql, MemoryContext per_query_ctx)
    {
        SPITupleTable *spi_tuptable = SPI_tuptable;
        TupleDesc   spi_tupdesc = spi_tuptable->tupdesc;
-       int         i;
+       uint64      i;
 
        /*
         * The provided categories SQL query must always return one column:
@@ -800,7 +800,7 @@ get_crosstab_tuplestore(char *sql,
    char      **values;
    HeapTuple   tuple;
    int         ret;
-   int         proc;
+   uint64      proc;
 
    /* initialize our tuplestore (while still in query context!) */
    tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
@@ -823,8 +823,8 @@ get_crosstab_tuplestore(char *sql,
        char       *rowid;
        char       *lastrowid = NULL;
        bool        firstpass = true;
-       int         i,
-                   j;
+       uint64      i;
+       int         j;
        int         result_ncols;
 
        if (num_categories == 0)
@@ -1220,7 +1220,7 @@ build_tuplestore_recursively(char *key_fld,
 {
    TupleDesc   tupdesc = attinmeta->tupdesc;
    int         ret;
-   int         proc;
+   uint64      proc;
    int         serial_column;
    StringInfoData sql;
    char      **values;
@@ -1313,7 +1313,7 @@ build_tuplestore_recursively(char *key_fld,
        HeapTuple   spi_tuple;
        SPITupleTable *tuptable = SPI_tuptable;
        TupleDesc   spi_tupdesc = tuptable->tupdesc;
-       int         i;
+       uint64      i;
        StringInfoData branchstr;
        StringInfoData chk_branchstr;
        StringInfoData chk_current_key;
index 655c5322cdf21a7f107c0a86cf3b4dc3626a5fdf..ac28996867b36aec60ca77ba5ba477cad5c005c1 100644 (file)
@@ -553,8 +553,7 @@ xpath_table(PG_FUNCTION_ARGS)
 
    int         numpaths;
    int         ret;
-   int         proc;
-   int         i;
+   uint64      proc;
    int         j;
    int         rownr;          /* For issuing multiple rows from one original
                                 * document */
@@ -664,7 +663,6 @@ xpath_table(PG_FUNCTION_ARGS)
             query_buf.data);
 
    proc = SPI_processed;
-   /* elog(DEBUG1,"xpath_table: SPI returned %d rows",proc); */
    tuptable = SPI_tuptable;
    spi_tupdesc = tuptable->tupdesc;
 
@@ -692,6 +690,8 @@ xpath_table(PG_FUNCTION_ARGS)
    PG_TRY();
    {
        /* For each row i.e. document returned from SPI */
+       uint64      i;
+
        for (i = 0; i < proc; i++)
        {
            char       *pkey;
index c099fcfad3a7ce54a2fa50a46944cab51e8a8bdc..9ae7126ae7f144c0005eb16ca314056de2934232 100644 (file)
@@ -400,8 +400,8 @@ SPI_execute("INSERT INTO foo SELECT * FROM bar RETURNING *", false, 5);
 typedef struct
 {
     MemoryContext tuptabcxt;    /* memory context of result table */
-    uint32      alloced;        /* number of alloced vals */
-    uint32      free;           /* number of free vals */
+    uint64      alloced;        /* number of alloced vals */
+    uint64      free;           /* number of free vals */
     TupleDesc   tupdesc;        /* row descriptor */
     HeapTuple  *vals;           /* rows */
 } SPITupleTable;
@@ -4116,14 +4116,14 @@ INSERT INTO a SELECT * FROM a;
 PG_MODULE_MAGIC;
 #endif
 
-int execq(text *sql, int cnt);
+int64 execq(text *sql, int cnt);
 
-int
+int64
 execq(text *sql, int cnt)
 {
     char *command;
     int ret;
-    int proc;
+    uint64 proc;
 
     /* Convert given text object to a C string */
     command = text_to_cstring(sql);
@@ -4141,11 +4141,12 @@ execq(text *sql, int cnt)
         TupleDesc tupdesc = SPI_tuptable-&gt;tupdesc;
         SPITupleTable *tuptable = SPI_tuptable;
         char buf[8192];
-        int i, j;
+        uint64 j;
 
         for (j = 0; j &lt; proc; j++)
         {
             HeapTuple tuple = tuptable-&gt;vals[j];
+            int i;
 
             for (i = 1, buf[0] = 0; i &lt;= tupdesc-&gt;natts; i++)
                 snprintf(buf + strlen (buf), sizeof(buf) - strlen(buf), " %s%s",
@@ -4173,9 +4174,9 @@ execq(text *sql, int cnt)
    a shared library (details are in <xref linkend="dfunc">.):
 
 <programlisting>
-CREATE FUNCTION execq(text, integer) RETURNS integer
+CREATE FUNCTION execq(text, integer) RETURNS int8
     AS '<replaceable>filename</replaceable>'
-    LANGUAGE C;
+    LANGUAGE C STRICT;
 </programlisting>
   </para>
 
index fcb033130927cc07f4fc60dbcb1ca84629958c95..cb7a145ee5db8be8cdcc7a9572fc352541c3028d 100644 (file)
@@ -197,7 +197,7 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
    /* save the rowcount if we're given a completionTag to fill */
    if (completionTag)
        snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
-                "SELECT %u", queryDesc->estate->es_processed);
+                "SELECT " UINT64_FORMAT, queryDesc->estate->es_processed);
 
    /* and clean up */
    ExecutorFinish(queryDesc);
index 8c045c090b453375c36648384a5ec8edbb06c2c9..50a54e746334245a69ffefbf9df691e618845a1e 100644 (file)
@@ -148,7 +148,7 @@ PerformPortalFetch(FetchStmt *stmt,
                   char *completionTag)
 {
    Portal      portal;
-   long        nprocessed;
+   uint64      nprocessed;
 
    /*
     * Disallow empty-string cursor name (conflicts with protocol-level
@@ -181,7 +181,7 @@ PerformPortalFetch(FetchStmt *stmt,
 
    /* Return command status if wanted */
    if (completionTag)
-       snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
+       snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s " UINT64_FORMAT,
                 stmt->ismove ? "MOVE" : "FETCH",
                 nprocessed);
 }
@@ -392,20 +392,14 @@ PersistHoldablePortal(Portal portal)
        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.
+            * 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,
index 76f7297c077f2bf3e0c9392848933f2b93333eb2..687256279abc7ea6578ad8df1099202b782b6764 100644 (file)
@@ -79,7 +79,7 @@ static void ExecutePlan(EState *estate, PlanState *planstate,
            bool use_parallel_mode,
            CmdType operation,
            bool sendTuples,
-           long numberTuples,
+           uint64 numberTuples,
            ScanDirection direction,
            DestReceiver *dest);
 static bool ExecCheckRTEPerms(RangeTblEntry *rte);
@@ -278,7 +278,7 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
  */
 void
 ExecutorRun(QueryDesc *queryDesc,
-           ScanDirection direction, long count)
+           ScanDirection direction, uint64 count)
 {
    if (ExecutorRun_hook)
        (*ExecutorRun_hook) (queryDesc, direction, count);
@@ -288,7 +288,7 @@ ExecutorRun(QueryDesc *queryDesc,
 
 void
 standard_ExecutorRun(QueryDesc *queryDesc,
-                    ScanDirection direction, long count)
+                    ScanDirection direction, uint64 count)
 {
    EState     *estate;
    CmdType     operation;
@@ -1521,12 +1521,12 @@ ExecutePlan(EState *estate,
            bool use_parallel_mode,
            CmdType operation,
            bool sendTuples,
-           long numberTuples,
+           uint64 numberTuples,
            ScanDirection direction,
            DestReceiver *dest)
 {
    TupleTableSlot *slot;
-   long        current_tuple_count;
+   uint64      current_tuple_count;
 
    /*
     * initialize local variables
@@ -1542,7 +1542,7 @@ ExecutePlan(EState *estate,
     * If a tuple count was supplied, we must force the plan to run without
     * parallelism, because we might exit early.
     */
-   if (numberTuples != 0)
+   if (numberTuples)
        use_parallel_mode = false;
 
    /*
index c3cdad4abf738bb12648bde5a6f2cf2ec1615b30..6e14c9d29677d12d4f72271ff1d03e018f50609e 100644 (file)
@@ -853,7 +853,7 @@ postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
    else
    {
        /* Run regular commands to completion unless lazyEval */
-       long        count = (es->lazyEval) ? 1L : 0L;
+       uint64      count = (es->lazyEval) ? 1 : 0;
 
        ExecutorRun(es->qd, ForwardScanDirection, count);
 
@@ -861,7 +861,7 @@ postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
         * 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);
+       result = (count == 0 || es->qd->estate->es_processed == 0);
    }
 
    return result;
index 3357c0d25241222233dedd53523bb438ccec1b32..3d04c23b4e0cc9ab8e01d433a4936da7fd230291 100644 (file)
@@ -36,7 +36,7 @@
 #include "utils/typcache.h"
 
 
-uint32     SPI_processed = 0;
+uint64     SPI_processed = 0;
 Oid            SPI_lastoid = InvalidOid;
 SPITupleTable *SPI_tuptable = NULL;
 int            SPI_result;
@@ -56,12 +56,12 @@ static void _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan);
 
 static int _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
                  Snapshot snapshot, Snapshot crosscheck_snapshot,
-                 bool read_only, bool fire_triggers, long tcount);
+                 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, long tcount);
+static int _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount);
 
 static void _SPI_error_callback(void *arg);
 
@@ -1991,10 +1991,10 @@ _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan)
 static int
 _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
                  Snapshot snapshot, Snapshot crosscheck_snapshot,
-                 bool read_only, bool fire_triggers, long tcount)
+                 bool read_only, bool fire_triggers, uint64 tcount)
 {
    int         my_res = 0;
-   uint32      my_processed = 0;
+   uint64      my_processed = 0;
    Oid         my_lastoid = InvalidOid;
    SPITupleTable *my_tuptable = NULL;
    int         res = 0;
@@ -2218,8 +2218,8 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
                if (IsA(stmt, CreateTableAsStmt))
                {
                    Assert(strncmp(completionTag, "SELECT ", 7) == 0);
-                   _SPI_current->processed = strtoul(completionTag + 7,
-                                                     NULL, 10);
+                   _SPI_current->processed = pg_strtouint64(completionTag + 7,
+                                                            NULL, 10);
 
                    /*
                     * For historical reasons, if CREATE TABLE AS was spelled
@@ -2231,8 +2231,8 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
                else if (IsA(stmt, CopyStmt))
                {
                    Assert(strncmp(completionTag, "COPY ", 5) == 0);
-                   _SPI_current->processed = strtoul(completionTag + 5,
-                                                     NULL, 10);
+                   _SPI_current->processed = pg_strtouint64(completionTag + 5,
+                                                            NULL, 10);
                }
            }
 
@@ -2348,7 +2348,7 @@ _SPI_convert_params(int nargs, Oid *argtypes,
 }
 
 static int
-_SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, long tcount)
+_SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount)
 {
    int         operation = queryDesc->operation;
    int         eflags;
@@ -2460,7 +2460,7 @@ static void
 _SPI_cursor_operation(Portal portal, FetchDirection direction, long count,
                      DestReceiver *dest)
 {
-   long        nfetched;
+   uint64      nfetched;
 
    /* Check that the portal is valid */
    if (!PortalIsValid(portal))
@@ -2563,7 +2563,7 @@ _SPI_end_call(bool procmem)
 static bool
 _SPI_checktuples(void)
 {
-   uint32      processed = _SPI_current->processed;
+   uint64      processed = _SPI_current->processed;
    SPITupleTable *tuptable = _SPI_current->tuptable;
    bool        failed = false;
 
index 6893b0f4239aa7702e902389402cc280e8e8a775..fcdc4c347c7919ebb4ef7f89c2f17c6f09f4a7e8 100644 (file)
@@ -39,16 +39,16 @@ static void ProcessQuery(PlannedStmt *plan,
             DestReceiver *dest,
             char *completionTag);
 static void FillPortalStore(Portal portal, bool isTopLevel);
-static uint32 RunFromStore(Portal portal, ScanDirection direction, long count,
+static uint64 RunFromStore(Portal portal, ScanDirection direction, uint64 count,
             DestReceiver *dest);
-static long PortalRunSelect(Portal portal, bool forward, long count,
+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 long DoPortalRunFetch(Portal portal,
+static uint64 DoPortalRunFetch(Portal portal,
                 FetchDirection fdirection,
                 long count,
                 DestReceiver *dest);
@@ -195,7 +195,8 @@ ProcessQuery(PlannedStmt *plan,
        {
            case CMD_SELECT:
                snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
-                        "SELECT %u", queryDesc->estate->es_processed);
+                        "SELECT " UINT64_FORMAT,
+                        queryDesc->estate->es_processed);
                break;
            case CMD_INSERT:
                if (queryDesc->estate->es_processed == 1)
@@ -203,15 +204,18 @@ ProcessQuery(PlannedStmt *plan,
                else
                    lastOid = InvalidOid;
                snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
-                  "INSERT %u %u", lastOid, queryDesc->estate->es_processed);
+                        "INSERT %u " UINT64_FORMAT,
+                        lastOid, queryDesc->estate->es_processed);
                break;
            case CMD_UPDATE:
                snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
-                        "UPDATE %u", queryDesc->estate->es_processed);
+                        "UPDATE " UINT64_FORMAT,
+                        queryDesc->estate->es_processed);
                break;
            case CMD_DELETE:
                snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
-                        "DELETE %u", queryDesc->estate->es_processed);
+                        "DELETE " UINT64_FORMAT,
+                        queryDesc->estate->es_processed);
                break;
            default:
                strcpy(completionTag, "???");
@@ -548,7 +552,6 @@ PortalStart(Portal portal, ParamListInfo params,
                portal->atStart = true;
                portal->atEnd = false;  /* allow fetches */
                portal->portalPos = 0;
-               portal->posOverflow = false;
 
                PopActiveSnapshot();
                break;
@@ -576,7 +579,6 @@ PortalStart(Portal portal, ParamListInfo params,
                portal->atStart = true;
                portal->atEnd = false;  /* allow fetches */
                portal->portalPos = 0;
-               portal->posOverflow = false;
                break;
 
            case PORTAL_UTIL_SELECT:
@@ -598,7 +600,6 @@ PortalStart(Portal portal, ParamListInfo params,
                portal->atStart = true;
                portal->atEnd = false;  /* allow fetches */
                portal->portalPos = 0;
-               portal->posOverflow = false;
                break;
 
            case PORTAL_MULTI_QUERY:
@@ -708,7 +709,7 @@ PortalRun(Portal portal, long count, bool isTopLevel,
          char *completionTag)
 {
    bool        result;
-   uint32      nprocessed;
+   uint64      nprocessed;
    ResourceOwner saveTopTransactionResourceOwner;
    MemoryContext saveTopTransactionContext;
    Portal      saveActivePortal;
@@ -794,7 +795,7 @@ PortalRun(Portal portal, long count, bool isTopLevel,
                {
                    if (strcmp(portal->commandTag, "SELECT") == 0)
                        snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
-                                "SELECT %u", nprocessed);
+                                "SELECT " UINT64_FORMAT, nprocessed);
                    else
                        strcpy(completionTag, portal->commandTag);
                }
@@ -877,14 +878,14 @@ PortalRun(Portal portal, long count, bool isTopLevel,
  *
  * 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".
+ * 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 long
+static uint64
 PortalRunSelect(Portal portal,
                bool forward,
                long count,
@@ -892,7 +893,7 @@ PortalRunSelect(Portal portal,
 {
    QueryDesc  *queryDesc;
    ScanDirection direction;
-   uint32      nprocessed;
+   uint64      nprocessed;
 
    /*
     * NB: queryDesc will be NULL if we are fetching from a held cursor or a
@@ -926,7 +927,10 @@ PortalRunSelect(Portal portal,
    if (forward)
    {
        if (portal->atEnd || count <= 0)
+       {
            direction = NoMovementScanDirection;
+           count = 0;          /* don't pass negative count to executor */
+       }
        else
            direction = ForwardScanDirection;
 
@@ -935,29 +939,22 @@ PortalRunSelect(Portal portal,
            count = 0;
 
        if (portal->holdStore)
-           nprocessed = RunFromStore(portal, direction, count, dest);
+           nprocessed = RunFromStore(portal, direction, (uint64) count, dest);
        else
        {
            PushActiveSnapshot(queryDesc->snapshot);
-           ExecutorRun(queryDesc, direction, count);
+           ExecutorRun(queryDesc, direction, (uint64) 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)
+           if (count == 0 || nprocessed < (uint64) 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
@@ -969,7 +966,10 @@ PortalRunSelect(Portal portal,
                     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;
 
@@ -978,11 +978,11 @@ PortalRunSelect(Portal portal,
            count = 0;
 
        if (portal->holdStore)
-           nprocessed = RunFromStore(portal, direction, count, dest);
+           nprocessed = RunFromStore(portal, direction, (uint64) count, dest);
        else
        {
            PushActiveSnapshot(queryDesc->snapshot);
-           ExecutorRun(queryDesc, direction, count);
+           ExecutorRun(queryDesc, direction, (uint64) count);
            nprocessed = queryDesc->estate->es_processed;
            PopActiveSnapshot();
        }
@@ -994,22 +994,14 @@ PortalRunSelect(Portal portal,
                portal->atEnd = false;  /* OK to go forward now */
                portal->portalPos++;    /* adjust for endpoint case */
            }
-           if (count == 0 ||
-               (unsigned long) nprocessed < (unsigned long) count)
+           if (count == 0 || nprocessed < (uint64) 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;
            }
        }
    }
@@ -1083,11 +1075,11 @@ FillPortalStore(Portal portal, bool isTopLevel)
  * 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,
+static uint64
+RunFromStore(Portal portal, ScanDirection direction, uint64 count,
             DestReceiver *dest)
 {
-   long        current_tuple_count = 0;
+   uint64      current_tuple_count = 0;
    TupleTableSlot *slot;
 
    slot = MakeSingleTupleTableSlot(portal->tupDesc);
@@ -1136,7 +1128,7 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
 
    ExecDropSingleTupleTableSlot(slot);
 
-   return (uint32) current_tuple_count;
+   return current_tuple_count;
 }
 
 /*
@@ -1375,15 +1367,19 @@ PortalRunMulti(Portal portal, bool isTopLevel,
  *
  * 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)
  */
-long
+uint64
 PortalRunFetch(Portal portal,
               FetchDirection fdirection,
               long count,
               DestReceiver *dest)
 {
-   long        result;
+   uint64      result;
    Portal      saveActivePortal;
    ResourceOwner saveResourceOwner;
    MemoryContext savePortalContext;
@@ -1470,9 +1466,13 @@ PortalRunFetch(Portal portal,
  * 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 long
+static uint64
 DoPortalRunFetch(Portal portal,
                 FetchDirection fdirection,
                 long count,
@@ -1508,13 +1508,21 @@ DoPortalRunFetch(Portal portal,
            {
                /*
                 * Definition: Rewind to start, advance count-1 rows, return
-                * next row (if any).  In practice, if the goal is less than
-                * halfway back to the start, it's better to scan from where
-                * we are.  In any case, we arrange to fetch the target row
-                * going forwards.
+                * next row (if any).
+                *
+                * In practice, if the goal is less than halfway back to the
+                * start, it's better to scan from where we are.
+                *
+                * Also, if current portalPos is outside the range of "long",
+                * do it the hard way to avoid possible overflow of the count
+                * argument to PortalRunSelect.  We must exclude exactly
+                * LONG_MAX, as well, lest the count look like FETCH_ALL.
+                *
+                * In any case, we arrange to fetch the target row going
+                * forwards.
                 */
-               if (portal->posOverflow || portal->portalPos == LONG_MAX ||
-                   count - 1 <= portal->portalPos / 2)
+               if ((uint64) (count - 1) <= portal->portalPos / 2 ||
+                   portal->portalPos >= (uint64) LONG_MAX)
                {
                    DoPortalRewind(portal);
                    if (count > 1)
@@ -1523,7 +1531,7 @@ DoPortalRunFetch(Portal portal,
                }
                else
                {
-                   long        pos = portal->portalPos;
+                   long        pos = (long) portal->portalPos;
 
                    if (portal->atEnd)
                        pos++;  /* need one extra fetch if off end */
@@ -1609,7 +1617,7 @@ DoPortalRunFetch(Portal portal,
        if (dest->mydest == DestNone)
        {
            /* MOVE 0 returns 0/1 based on if FETCH 0 would return a row */
-           return on_row ? 1L : 0L;
+           return on_row ? 1 : 0;
        }
        else
        {
@@ -1635,12 +1643,11 @@ DoPortalRunFetch(Portal portal,
     */
    if (!forward && count == FETCH_ALL && dest->mydest == DestNone)
    {
-       long        result = portal->portalPos;
+       uint64      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;
    }
 
@@ -1677,5 +1684,4 @@ DoPortalRewind(Portal portal)
    portal->atStart = true;
    portal->atEnd = false;
    portal->portalPos = 0;
-   portal->posOverflow = false;
 }
index 6b105964bd1be1fde83df0eac2cb8f8bffaf03c6..da100f7c0f2d3f0d6635be84d467eecc74de0dd7 100644 (file)
@@ -388,3 +388,25 @@ pg_ltostr(char *str, int32 value)
 
    return end;
 }
+
+/*
+ * pg_strtouint64
+ *     Converts 'str' into an unsigned 64-bit integer.
+ *
+ * This has the identical API to strtoul(3), except that it will handle
+ * 64-bit ints even where "long" is narrower than that.
+ *
+ * For the moment it seems sufficient to assume that the platform has
+ * such a function somewhere; let's not roll our own.
+ */
+uint64
+pg_strtouint64(const char *str, char **endptr, int base)
+{
+#ifdef WIN32
+   return _strtoui64(str, endptr, base);
+#elif defined(HAVE_STRTOULL) && SIZEOF_LONG < 8
+   return strtoull(str, endptr, base);
+#else
+   return strtoul(str, endptr, base);
+#endif
+}
index 0870afd867f510cf90263508bd7ef048fd5be93e..28f328ddb312dabba6c54b1aa13096b27fca36de 100644 (file)
@@ -260,7 +260,6 @@ tsquery_rewrite_query(PG_FUNCTION_ARGS)
    SPIPlanPtr  plan;
    Portal      portal;
    bool        isnull;
-   int         i;
 
    if (query->size == 0)
    {
@@ -294,6 +293,8 @@ tsquery_rewrite_query(PG_FUNCTION_ARGS)
 
    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);
index 186b3d337ad9b26cfc30a432e18ea8743be0bebb..f6d3fb5d7b4a607bc7eb8702c948045b59ac14e2 100644 (file)
@@ -1682,7 +1682,6 @@ static TSVectorStat *
 ts_stat_sql(MemoryContext persistentContext, text *txt, text *ws)
 {
    char       *query = text_to_cstring(txt);
-   int         i;
    TSVectorStat *stat;
    bool        isnull;
    Portal      portal;
@@ -1746,6 +1745,8 @@ ts_stat_sql(MemoryContext persistentContext, text *txt, text *ws)
 
    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);
index 56179f822ed2df51f902b5a88c4aec8ebea84a30..7ed5bcb93dd45bbb3c282913bc55110192371a1e 100644 (file)
@@ -161,7 +161,7 @@ static const char *map_sql_catalog_to_xmlschema_types(List *nspid_list,
 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,
+static void SPI_sql_row_to_xmlelement(uint64 rownum, StringInfo result,
                          char *tablename, bool nulls, bool tableforest,
                          const char *targetns, bool top_level);
 
@@ -2260,7 +2260,7 @@ _SPI_strdup(const char *s)
 static List *
 query_to_oid_list(const char *query)
 {
-   int         i;
+   uint64      i;
    List       *list = NIL;
 
    SPI_execute(query, true, 0);
@@ -2379,7 +2379,7 @@ cursor_to_xml(PG_FUNCTION_ARGS)
 
    StringInfoData result;
    Portal      portal;
-   int         i;
+   uint64      i;
 
    initStringInfo(&result);
 
@@ -2454,7 +2454,7 @@ query_to_xml_internal(const char *query, char *tablename,
 {
    StringInfo  result;
    char       *xmltn;
-   int         i;
+   uint64      i;
 
    if (tablename)
        xmltn = map_sql_identifier_to_xml_name(tablename, true, false);
@@ -3532,7 +3532,7 @@ map_sql_type_to_xmlschema_type(Oid typeoid, int typmod)
  * SPI cursor.  See also SQL/XML:2008 section 9.10.
  */
 static void
-SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename,
+SPI_sql_row_to_xmlelement(uint64 rownum, StringInfo result, char *tablename,
                          bool nulls, bool tableforest,
                          const char *targetns, bool top_level)
 {
index 1a44085a2cee339ab25566d4f6de898639e1976a..44fac278a4957df8466e184ea69248b81313b9bb 100644 (file)
@@ -80,7 +80,7 @@ extern PGDLLIMPORT ExecutorStart_hook_type ExecutorStart_hook;
 /* Hook for plugins to get control in ExecutorRun() */
 typedef void (*ExecutorRun_hook_type) (QueryDesc *queryDesc,
                                                   ScanDirection direction,
-                                                  long count);
+                                                  uint64 count);
 extern PGDLLIMPORT ExecutorRun_hook_type ExecutorRun_hook;
 
 /* Hook for plugins to get control in ExecutorFinish() */
@@ -175,9 +175,9 @@ extern TupleTableSlot *ExecFilterJunk(JunkFilter *junkfilter,
 extern void ExecutorStart(QueryDesc *queryDesc, int eflags);
 extern void standard_ExecutorStart(QueryDesc *queryDesc, int eflags);
 extern void ExecutorRun(QueryDesc *queryDesc,
-           ScanDirection direction, long count);
+           ScanDirection direction, uint64 count);
 extern void standard_ExecutorRun(QueryDesc *queryDesc,
-                    ScanDirection direction, long count);
+                    ScanDirection direction, uint64 count);
 extern void ExecutorFinish(QueryDesc *queryDesc);
 extern void standard_ExecutorFinish(QueryDesc *queryDesc);
 extern void ExecutorEnd(QueryDesc *queryDesc);
index 8c3ca261237f91cedb4c666ee7b36db89bfd514d..1792fb12172c4e1c4d2107a7157ebb569ee7f240 100644 (file)
@@ -21,8 +21,8 @@
 typedef struct SPITupleTable
 {
    MemoryContext tuptabcxt;    /* memory context of result table */
-   uint32      alloced;        /* # of alloced vals */
-   uint32      free;           /* # of free vals */
+   uint64      alloced;        /* # of alloced vals */
+   uint64      free;           /* # of free vals */
    TupleDesc   tupdesc;        /* tuple descriptor */
    HeapTuple  *vals;           /* tuples */
    slist_node  next;           /* link for internal bookkeeping */
@@ -59,7 +59,7 @@ typedef struct _SPI_plan *SPIPlanPtr;
 #define SPI_OK_UPDATE_RETURNING 13
 #define SPI_OK_REWRITTEN       14
 
-extern PGDLLIMPORT uint32 SPI_processed;
+extern PGDLLIMPORT uint64 SPI_processed;
 extern PGDLLIMPORT Oid SPI_lastoid;
 extern PGDLLIMPORT SPITupleTable *SPI_tuptable;
 extern PGDLLIMPORT int SPI_result;
index 3187230c0205b420b6dc613411e42be2539279ad..e8084dff0911677115f16a5dd7d67c30206baa92 100644 (file)
@@ -21,7 +21,7 @@
 typedef struct
 {
    /* current results */
-   uint32      processed;      /* by Executor */
+   uint64      processed;      /* by Executor */
    Oid         lastoid;
    SPITupleTable *tuptable;    /* tuptable currently being built */
 
index b6ae93fe0d0643cd67b417ffa16c3bfa189100b0..e73a82427ca3dd0684e7c24621f74ade731e6432 100644 (file)
@@ -62,7 +62,7 @@ typedef struct FuncCallContext
     * 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;
+   uint64      call_cntr;
 
    /*
     * OPTIONAL maximum number of calls
@@ -71,7 +71,7 @@ typedef struct FuncCallContext
     * not set, you must provide alternative means to know when the function
     * is done.
     */
-   uint32      max_calls;
+   uint64      max_calls;
 
    /*
     * OPTIONAL pointer to result slot
index 064a0509c4d236924e3f6fb73891d9530776109a..d35ec810450f8702d31ca3f53e6eb16600b42edb 100644 (file)
@@ -387,7 +387,7 @@ typedef struct EState
 
    List       *es_rowMarks;    /* List of ExecRowMarks */
 
-   uint32      es_processed;   /* # of tuples processed */
+   uint64      es_processed;   /* # of tuples processed */
    Oid         es_lastoid;     /* last oid processed (by INSERT) */
 
    int         es_top_eflags;  /* eflags passed to ExecutorStart */
index 453147e1f0b8f27da705b76da4437c4adf4edb1b..cde939b7cab257a0df17dc9df64ad6edec3153f2 100644 (file)
@@ -629,6 +629,33 @@ typedef Datum *DatumPtr;
 extern Datum Int64GetDatum(int64 X);
 #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.
index 4f1fd139a3666e588231af1b48c51b9e74b46acc..e04fc4330d92f4f14493efa86619df0d79bfab2b 100644 (file)
@@ -37,7 +37,7 @@ extern bool PortalRun(Portal portal, long count, bool isTopLevel,
          DestReceiver *dest, DestReceiver *altdest,
          char *completionTag);
 
-extern long PortalRunFetch(Portal portal,
+extern uint64 PortalRunFetch(Portal portal,
               FetchDirection fdirection,
               long count,
               DestReceiver *dest);
index 115f8afb45b61a5f9ae4cb6684f9b45c6d22e9bf..59a00bbcbcf8291c92f7c9b13b53861b1be2372c 100644 (file)
@@ -292,6 +292,7 @@ extern void pg_ltoa(int32 l, char *a);
 extern void pg_lltoa(int64 ll, char *a);
 extern char *pg_ltostr_zeropad(char *str, int32 value, int32 minwidth);
 extern char *pg_ltostr(char *str, int32 value);
+extern uint64 pg_strtouint64(const char *str, char **endptr, int base);
 
 /*
  *     Per-opclass comparison functions for new btrees.  These are
index 4236215b79602f6461e828682a95b5d2f2593204..7250c9c1bb38f04015c9f0c970abd4df51a8e5c1 100644 (file)
@@ -166,15 +166,14 @@ typedef struct PortalData
     * 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).
+    * 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;
-   bool        posOverflow;
-   long        portalPos;
+   uint64      portalPos;
 
    /* Presentation data, primarily used by the pg_cursors system view */
    TimestampTz creation_time;  /* time at which this portal was defined */
index cd917ab8e46ddc13a293144184a3f45e71cab01c..269f7f332206d5b590ca648a31a20538bfa1a6dd 100644 (file)
@@ -12,8 +12,9 @@
 /* system stuff */
 #include <ctype.h>
 #include <fcntl.h>
-#include <unistd.h>
+#include <limits.h>
 #include <locale.h>
+#include <unistd.h>
 
 /* postgreSQL stuff */
 #include "access/htup_details.h"
@@ -281,7 +282,7 @@ static Datum plperl_hash_to_datum(SV *src, TupleDesc td);
 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 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);
@@ -1472,7 +1473,7 @@ plperl_ref_from_pg_array(Datum arg, Oid typid)
 
    hv = newHV();
    (void) hv_store(hv, "array", 5, av, 0);
-   (void) hv_store(hv, "typeoid", 7, newSViv(typid), 0);
+   (void) hv_store(hv, "typeoid", 7, newSVuv(typid), 0);
 
    return sv_bless(newRV_noinc((SV *) hv),
                    gv_stashpv("PostgreSQL::InServer::ARRAY", 0));
@@ -3091,7 +3092,7 @@ plperl_spi_exec(char *query, int limit)
 
 
 static HV  *
-plperl_spi_execute_fetch_result(SPITupleTable *tuptable, int processed,
+plperl_spi_execute_fetch_result(SPITupleTable *tuptable, uint64 processed,
                                int status)
 {
    HV         *result;
@@ -3103,13 +3104,25 @@ plperl_spi_execute_fetch_result(SPITupleTable *tuptable, int processed,
    hv_store_string(result, "status",
                    cstr2sv(SPI_result_code_string(status)));
    hv_store_string(result, "processed",
-                   newSViv(processed));
+                   (processed > (uint64) INT_MAX) ?
+                   newSVnv((double) processed) :
+                   newSViv((int) processed));
 
    if (status > 0 && tuptable)
    {
        AV         *rows;
        SV         *row;
-       int         i;
+       uint64      i;
+
+       /*
+        * av_extend's 2nd argument is declared I32.  It's possible we could
+        * nonetheless push more than INT_MAX elements into a Perl array, but
+        * let's just fail instead of trying.
+        */
+       if (processed > (uint64) INT_MAX)
+           ereport(ERROR,
+                   (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+           errmsg("query result has too many rows to fit in a Perl array")));
 
        rows = newAV();
        av_extend(rows, processed);
index bd58d5f444fc741f54a18e8ad60eac62f589249c..b63ecacdecf7db228695f0947eb5465830362cac 100644 (file)
@@ -1601,8 +1601,8 @@ exec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt)
        {
            case PLPGSQL_GETDIAG_ROW_COUNT:
                exec_assign_value(estate, var,
-                                 UInt32GetDatum(estate->eval_processed),
-                                 false, INT4OID, -1);
+                                 UInt64GetDatum(estate->eval_processed),
+                                 false, INT8OID, -1);
                break;
 
            case PLPGSQL_GETDIAG_RESULT_OID:
@@ -2856,7 +2856,7 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
                       PLpgSQL_stmt_return_query *stmt)
 {
    Portal      portal;
-   uint32      processed = 0;
+   uint64      processed = 0;
    TupleConversionMap *tupmap;
 
    if (!estate->retisset)
@@ -2887,7 +2887,7 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
 
    while (true)
    {
-       int         i;
+       uint64          i;
 
        SPI_cursor_fetch(portal, true, 50);
        if (SPI_processed == 0)
@@ -3579,7 +3579,7 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
    if (stmt->into)
    {
        SPITupleTable *tuptab = SPI_tuptable;
-       uint32      n = SPI_processed;
+       uint64      n = SPI_processed;
        PLpgSQL_rec *rec = NULL;
        PLpgSQL_row *row = NULL;
 
@@ -3769,7 +3769,7 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
    if (stmt->into)
    {
        SPITupleTable *tuptab = SPI_tuptable;
-       uint32      n = SPI_processed;
+       uint64      n = SPI_processed;
        PLpgSQL_rec *rec = NULL;
        PLpgSQL_row *row = NULL;
 
@@ -4043,7 +4043,7 @@ exec_stmt_fetch(PLpgSQL_execstate *estate, PLpgSQL_stmt_fetch *stmt)
    SPITupleTable *tuptab;
    Portal      portal;
    char       *curname;
-   uint32      n;
+   uint64      n;
 
    /* ----------
     * Get the portal of the cursor by name
@@ -5151,7 +5151,7 @@ exec_for_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_forq *stmt,
    SPITupleTable *tuptab;
    bool        found = false;
    int         rc = PLPGSQL_RC_OK;
-   int         n;
+   uint64      n;
 
    /*
     * Determine if we assign to a record or a row
@@ -5182,7 +5182,7 @@ exec_for_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_forq *stmt,
     * If the query didn't return any rows, set the target to NULL and fall
     * through with found = false.
     */
-   if (n <= 0)
+   if (n == 0)
    {
        exec_move_row(estate, rec, row, NULL, tuptab->tupdesc);
        exec_eval_cleanup(estate);
@@ -5195,7 +5195,7 @@ exec_for_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_forq *stmt,
     */
    while (n > 0)
    {
-       int         i;
+       uint64      i;
 
        for (i = 0; i < n; i++)
        {
index a1e900d7336e7b079c75a7bf64926d183b5ff131..2deece43eb792aa83704f005d303360d642592e4 100644 (file)
@@ -820,7 +820,7 @@ typedef struct PLpgSQL_execstate
 
    /* temporary state for results from evaluation of query or expr */
    SPITupleTable *eval_tuptable;
-   uint32      eval_processed;
+   uint64      eval_processed;
    Oid         eval_lastoid;
    ExprContext *eval_econtext; /* for executing simple expressions */
 
index 103571ba15cc0befd1fea5fede49f0dfc67c4b82..44ba76e765e2cdcd1f253b1799b9c7fd6926e74a 100644 (file)
@@ -6,6 +6,8 @@
 
 #include "postgres.h"
 
+#include <limits.h>
+
 #include "access/xact.h"
 #include "mb/pg_wchar.h"
 #include "utils/memutils.h"
@@ -446,11 +448,23 @@ PLy_cursor_fetch(PyObject *self, PyObject *args)
        ret->status = PyInt_FromLong(SPI_OK_FETCH);
 
        Py_DECREF(ret->nrows);
-       ret->nrows = PyInt_FromLong(SPI_processed);
+       ret->nrows = (SPI_processed > (uint64) LONG_MAX) ?
+           PyFloat_FromDouble((double) SPI_processed) :
+           PyInt_FromLong((long) SPI_processed);
 
        if (SPI_processed != 0)
        {
-           int         i;
+           uint64      i;
+
+           /*
+            * PyList_New() and PyList_SetItem() use Py_ssize_t for list size
+            * and list indices; so we cannot support a result larger than
+            * PY_SSIZE_T_MAX.
+            */
+           if (SPI_processed > (uint64) PY_SSIZE_T_MAX)
+               ereport(ERROR,
+                       (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                        errmsg("query result has too many rows to fit in a Python list")));
 
            Py_DECREF(ret->rows);
            ret->rows = PyList_New(SPI_processed);
index 58e78ecebcb39139ee677727cf1b6807722ed03d..7d84629f48fcebaa1aaa98fcc7a3317704c97550 100644 (file)
@@ -6,6 +6,8 @@
 
 #include "postgres.h"
 
+#include <limits.h>
+
 #include "access/htup_details.h"
 #include "access/xact.h"
 #include "catalog/pg_type.h"
@@ -29,7 +31,8 @@
 
 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 PyObject *PLy_spi_execute_fetch_result(SPITupleTable *tuptable,
+                            uint64 rows, int status);
 static void PLy_spi_exception_set(PyObject *excclass, ErrorData *edata);
 
 
@@ -382,7 +385,7 @@ PLy_spi_execute_query(char *query, long limit)
 }
 
 static PyObject *
-PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
+PLy_spi_execute_fetch_result(SPITupleTable *tuptable, uint64 rows, int status)
 {
    PLyResultObject *result;
    volatile MemoryContext oldcontext;
@@ -394,16 +397,19 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
    if (status > 0 && tuptable == NULL)
    {
        Py_DECREF(result->nrows);
-       result->nrows = PyInt_FromLong(rows);
+       result->nrows = (rows > (uint64) LONG_MAX) ?
+           PyFloat_FromDouble((double) rows) :
+           PyInt_FromLong((long) rows);
    }
    else if (status > 0 && tuptable != NULL)
    {
        PLyTypeInfo args;
-       int         i;
        MemoryContext cxt;
 
        Py_DECREF(result->nrows);
-       result->nrows = PyInt_FromLong(rows);
+       result->nrows = (rows > (uint64) LONG_MAX) ?
+           PyFloat_FromDouble((double) rows) :
+           PyInt_FromLong((long) rows);
 
        cxt = AllocSetContextCreate(CurrentMemoryContext,
                                    "PL/Python temp context",
@@ -419,6 +425,18 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
 
            if (rows)
            {
+               uint64      i;
+
+               /*
+                * PyList_New() and PyList_SetItem() use Py_ssize_t for list
+                * size and list indices; so we cannot support a result larger
+                * than PY_SSIZE_T_MAX.
+                */
+               if (rows > (uint64) PY_SSIZE_T_MAX)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                            errmsg("query result has too many rows to fit in a Python list")));
+
                Py_DECREF(result->rows);
                result->rows = PyList_New(rows);
 
index 105b6186f64b8ca1ded782ef7618b5576c29f96c..5b27c731b6eb55d200455a567adaeb7341331890 100644 (file)
@@ -226,7 +226,7 @@ static int pltcl_process_SPI_result(Tcl_Interp *interp,
                         Tcl_Obj *loop_body,
                         int spi_rc,
                         SPITupleTable *tuptable,
-                        int ntuples);
+                        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,
@@ -235,7 +235,7 @@ static int pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp,
                  int objc, Tcl_Obj *const objv[]);
 
 static void pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname,
-                      int tupno, HeapTuple tuple, TupleDesc tupdesc);
+                      uint64 tupno, HeapTuple tuple, TupleDesc tupdesc);
 static Tcl_Obj *pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc);
 
 
@@ -481,7 +481,7 @@ pltcl_init_load_unknown(Tcl_Interp *interp)
    int         tcl_rc;
    Tcl_DString unknown_src;
    char       *part;
-   int         i;
+   uint64      i;
    int         fno;
 
    /************************************************************
@@ -2007,10 +2007,9 @@ pltcl_process_SPI_result(Tcl_Interp *interp,
                         Tcl_Obj *loop_body,
                         int spi_rc,
                         SPITupleTable *tuptable,
-                        int ntuples)
+                        uint64 ntuples)
 {
    int         my_rc = TCL_OK;
-   int         i;
    int         loop_rc;
    HeapTuple  *tuples;
    TupleDesc   tupdesc;
@@ -2021,7 +2020,7 @@ pltcl_process_SPI_result(Tcl_Interp *interp,
        case SPI_OK_INSERT:
        case SPI_OK_DELETE:
        case SPI_OK_UPDATE:
-           Tcl_SetObjResult(interp, Tcl_NewIntObj(ntuples));
+           Tcl_SetObjResult(interp, Tcl_NewWideIntObj(ntuples));
            break;
 
        case SPI_OK_UTILITY:
@@ -2060,6 +2059,8 @@ pltcl_process_SPI_result(Tcl_Interp *interp,
                 * 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,
@@ -2085,7 +2086,7 @@ pltcl_process_SPI_result(Tcl_Interp *interp,
 
            if (my_rc == TCL_OK)
            {
-               Tcl_SetObjResult(interp, Tcl_NewIntObj(ntuples));
+               Tcl_SetObjResult(interp, Tcl_NewWideIntObj(ntuples));
            }
            break;
 
@@ -2472,7 +2473,7 @@ pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp,
  **********************************************************************/
 static void
 pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname,
-                      int tupno, HeapTuple tuple, TupleDesc tupdesc)
+                      uint64 tupno, HeapTuple tuple, TupleDesc tupdesc)
 {
    int         i;
    char       *outputstr;
@@ -2498,7 +2499,7 @@ pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname,
    {
        arrptr = &arrayname;
        nameptr = &attname;
-       Tcl_SetVar2Ex(interp, arrayname, ".tupno", Tcl_NewIntObj(tupno), 0);
+       Tcl_SetVar2Ex(interp, arrayname, ".tupno", Tcl_NewWideIntObj(tupno), 0);
    }
 
    for (i = 0; i < tupdesc->natts; i++)
index 6367ce7d7ffeb8297de00fc0b3d98d6a9dad1094..e7826a4513b3f69eb162172689dd36813185361f 100644 (file)
@@ -362,7 +362,7 @@ funny_dup17(PG_FUNCTION_ARGS)
               *fieldval,
               *fieldtype;
    char       *when;
-   int         inserted;
+   uint64      inserted;
    int         selected = 0;
    int         ret;
 
@@ -443,7 +443,7 @@ funny_dup17(PG_FUNCTION_ARGS)
                                                                        ))));
    }
 
-   elog(DEBUG4, "funny_dup17 (fired %s) on level %3d: %d/%d tuples inserted/selected",
+   elog(DEBUG4, "funny_dup17 (fired %s) on level %3d: " UINT64_FORMAT "/%d tuples inserted/selected",
         when, *level, inserted, selected);
 
    SPI_finish();