diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c index 76d18315677..6708d817fba 100644 --- a/contrib/auto_explain/auto_explain.c +++ b/contrib/auto_explain/auto_explain.c @@ -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(); diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c index 9ce60e696c9..3d9b8e45d9f 100644 --- a/contrib/pg_stat_statements/pg_stat_statements.c +++ b/contrib/pg_stat_statements/pg_stat_statements.c @@ -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; diff --git a/contrib/spi/refint.c b/contrib/spi/refint.c index 26022107410..01dd717522c 100644 --- a/contrib/spi/refint.c +++ b/contrib/spi/refint.c @@ -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 diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c index 1ea4a635cd5..787c02d08fc 100644 --- a/contrib/tablefunc/tablefunc.c +++ b/contrib/tablefunc/tablefunc.c @@ -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; diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c index 655c5322cdf..ac28996867b 100644 --- a/contrib/xml2/xpath.c +++ b/contrib/xml2/xpath.c @@ -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; diff --git a/doc/src/sgml/spi.sgml b/doc/src/sgml/spi.sgml index c099fcfad3a..9ae7126ae7f 100644 --- a/doc/src/sgml/spi.sgml +++ b/doc/src/sgml/spi.sgml @@ -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->tupdesc; SPITupleTable *tuptable = SPI_tuptable; char buf[8192]; - int i, j; + 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", @@ -4173,9 +4174,9 @@ execq(text *sql, int cnt) a shared library (details are in .): -CREATE FUNCTION execq(text, integer) RETURNS integer +CREATE FUNCTION execq(text, integer) RETURNS int8 AS 'filename' - LANGUAGE C; + LANGUAGE C STRICT; diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c index fcb03313092..cb7a145ee5d 100644 --- a/src/backend/commands/createas.c +++ b/src/backend/commands/createas.c @@ -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); diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c index 8c045c090b4..50a54e74633 100644 --- a/src/backend/commands/portalcmds.c +++ b/src/backend/commands/portalcmds.c @@ -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, diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 76f7297c077..687256279ab 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -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; /* diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index c3cdad4abf7..6e14c9d2967 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -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; diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 3357c0d2524..3d04c23b4e0 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -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; diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index 6893b0f4239..fcdc4c347c7 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -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; } diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c index 6b105964bd1..da100f7c0f2 100644 --- a/src/backend/utils/adt/numutils.c +++ b/src/backend/utils/adt/numutils.c @@ -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 +} diff --git a/src/backend/utils/adt/tsquery_rewrite.c b/src/backend/utils/adt/tsquery_rewrite.c index 0870afd867f..28f328ddb31 100644 --- a/src/backend/utils/adt/tsquery_rewrite.c +++ b/src/backend/utils/adt/tsquery_rewrite.c @@ -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); diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c index 186b3d337ad..f6d3fb5d7b4 100644 --- a/src/backend/utils/adt/tsvector_op.c +++ b/src/backend/utils/adt/tsvector_op.c @@ -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); diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index 56179f822ed..7ed5bcb93dd 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -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) { diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 1a44085a2ce..44fac278a49 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -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); diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h index 8c3ca261237..1792fb12172 100644 --- a/src/include/executor/spi.h +++ b/src/include/executor/spi.h @@ -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; diff --git a/src/include/executor/spi_priv.h b/src/include/executor/spi_priv.h index 3187230c020..e8084dff091 100644 --- a/src/include/executor/spi_priv.h +++ b/src/include/executor/spi_priv.h @@ -21,7 +21,7 @@ typedef struct { /* current results */ - uint32 processed; /* by Executor */ + 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 b6ae93fe0d0..e73a82427ca 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -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 diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 064a0509c4d..d35ec810450 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -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 */ diff --git a/src/include/postgres.h b/src/include/postgres.h index 453147e1f0b..cde939b7cab 100644 --- a/src/include/postgres.h +++ b/src/include/postgres.h @@ -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. diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h index 4f1fd139a36..e04fc4330d9 100644 --- a/src/include/tcop/pquery.h +++ b/src/include/tcop/pquery.h @@ -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); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 115f8afb45b..59a00bbcbcf 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -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 diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h index 4236215b796..7250c9c1bb3 100644 --- a/src/include/utils/portal.h +++ b/src/include/utils/portal.h @@ -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 */ diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c index cd917ab8e46..269f7f33220 100644 --- a/src/pl/plperl/plperl.c +++ b/src/pl/plperl/plperl.c @@ -12,8 +12,9 @@ /* system stuff */ #include #include -#include +#include #include +#include /* 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); diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index bd58d5f444f..b63ecacdecf 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -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++) { diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index a1e900d7336..2deece43eb7 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -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 */ diff --git a/src/pl/plpython/plpy_cursorobject.c b/src/pl/plpython/plpy_cursorobject.c index 103571ba15c..44ba76e765e 100644 --- a/src/pl/plpython/plpy_cursorobject.c +++ b/src/pl/plpython/plpy_cursorobject.c @@ -6,6 +6,8 @@ #include "postgres.h" +#include + #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); diff --git a/src/pl/plpython/plpy_spi.c b/src/pl/plpython/plpy_spi.c index 58e78ecebcb..7d84629f48f 100644 --- a/src/pl/plpython/plpy_spi.c +++ b/src/pl/plpython/plpy_spi.c @@ -6,6 +6,8 @@ #include "postgres.h" +#include + #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); diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c index 105b6186f64..5b27c731b6e 100644 --- a/src/pl/tcl/pltcl.c +++ b/src/pl/tcl/pltcl.c @@ -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++) diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c index 6367ce7d7ff..e7826a4513b 100644 --- a/src/test/regress/regress.c +++ b/src/test/regress/regress.c @@ -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();