Re: libpq: How are result sets fetched behind the scene?

Christian Barthel <[email protected]> Sat, 07 Sep 2019 14:43:58 +0200
Newsgroups gmane.comp.db.postgresql.sql
Message-ID <[email protected]>
Sebastien FLAESCH <[email protected]> writes:

> Is the whole result set fetched to the client app, not matter what row
> number is provided to the first PQgetvalue() call (or similar API call
> on result set data or meta-data)?

I have tested this as well and came to the same result as you.
The entire result set seems to be fetched at once.
Attached is a test program: I have loaded 30MB of random strings
and did a simple SELECT on the random data.  I stopped the output
with getchar() and looked at the network traffic and the memory
usage with top(1).  Everything gets allocated and fetched at once
as far as I can see. 

However, I would not build an application that "depends" on this 
behavior.  I think that it is better to use a declared cursor and
use FETCH.
-- 
Christian Barthel <[email protected]>
pg-fetchtest.c (text/x-csrc, 2.1 KB)
#include <stdio.h>
#include <stdlib.h>
#include "libpq-fe.h"

/* Source Code based on PostgreSQL/libpq Example Code */

/* 
begin; 
create table string_test (s char(32));
BEGIN
CREATE TABLE
insert into string_test(s) SELECT md5(random()::text) from generate_series(1,1000000);
commit;
 */

static void
exit_nicely(PGconn *conn)
{
    PQfinish(conn);
    exit(1);
}

int
main(int argc, char **argv)
{
    const char *conninfo;
    PGconn     *conn;
    PGresult   *res;
    int         nFields;
    int         i,
                j;

    if (argc > 1)
        conninfo = argv[1];
    else
        conninfo = "host=192.168.4.102 dbname=dbname";

    conn = PQconnectdb(conninfo);

    /* Check to see that the backend connection was successfully made */
    if (PQstatus(conn) != CONNECTION_OK)
    {
        fprintf(stderr, "Connection to database failed: %s",
                PQerrorMessage(conn));
        exit_nicely(conn);
    }

    res = PQexec(conn,
                 "SELECT pg_catalog.set_config('search_path', '', false)");
    if (PQresultStatus(res) != PGRES_TUPLES_OK)
    {
        fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
        PQclear(res);
        exit_nicely(conn);
    }

    PQclear(res);

    res = PQexec(conn, "BEGIN");
    if (PQresultStatus(res) != PGRES_COMMAND_OK)
    {
        fprintf(stderr, "BEGIN command failed: %s", PQerrorMessage(conn));
        PQclear(res);
        exit_nicely(conn);
    }
    PQclear(res);


    res = PQexec(conn, "select s from public.string_test");
    if (PQresultStatus(res) != PGRES_TUPLES_OK)
    {
        fprintf(stderr, "PQexec failed: %s", PQerrorMessage(conn));
        PQclear(res);
        exit_nicely(conn);
    }

    nFields = PQnfields(res);
    for (i = 0; i < nFields; i++)
        printf("%-15s", PQfname(res, i));
    printf("\n-----\n");

    for (i = 0; i < PQntuples(res); i++)
    {
        for (j = 0; j < nFields; j++)
            printf("%-15s", PQgetvalue(res, i, j));
        printf("\n");
				getchar();
    }

    PQclear(res);

    res = PQexec(conn, "ABORT");
    PQclear(res);
    PQfinish(conn);
    return 0;
}