Search code examples
postgresqllibpq

libpq: lo_open() returns 0


I am trying to create large objects using libpq and encountered the problem. In the following code:

Oid                 oid;
int                 fd;

oid = lo_creat( pgConn, INV_READ | INV_WRITE );
fd = lo_open( pgConn, oid , INV_WRITE );

lo_open() returns 0, which is not an error according to the docs, but causes subsequent lo_write() to fail with "Invalid descriptor" error.

I read about functions "taking place within an SQL transaction block" and just tried to envelop my code in

PQexec( pgConn, "BEGIN" );
...
PQexec( pgConn, "COMMIT" );

The things changed. lo_open() still returned 0, but if I immediately call it once more, it worked!

The same is true for reading.

I feel I missed something, but cannot guess what.

UPD: After running the code from the answer below it occurred to me that 0 was a valid descriptor, and I got the initial error because I had not explicitly started a transaction. Surprisingly, I have to call "BEGIN" and "COMMIT" when reading large objects, too.


Solution

  • It's hard to tell you anything when you don't check or quote your error messages.

    Please try this SSCCE and go from there:

    #include <stdio.h>
    #include <stdlib.h>
    
    #include <libpq-fe.h>
    #include <libpq/libpq-fs.h>
    
    void check_PGconn(PGconn *conn, ConnStatusType status_ok, char* errmsg)
    {
        if (PQstatus(conn) != status_ok) {
            if (errmsg!=NULL) {
                fprintf(stderr, "%s: %s", errmsg, PQerrorMessage(conn));
            } else {
                fprintf(stderr, "%s", PQerrorMessage(conn));
            }
            PQfinish(conn);
            exit(1);
        }
    }
    
    int check_PGresult(PGconn *conn, PGresult *res, ExecStatusType status_ok, char* errmsg)
    {
        if (PQresultStatus(res) != status_ok) {
            if (errmsg!=NULL) {
                fprintf(stderr, "%s: %s", errmsg, PQerrorMessage(conn));
            } else {
                fprintf(stderr, "%s", PQerrorMessage(conn));
            }
            return 0;
        }
        return 1;
    }
    
    int main(int argc, char** argv)
    {
        char* conninfo;
        PGconn* conn;
        PGresult* res;
        int res_ok;
        Oid loid;
        int lofd;
        int lo_res;
        static char lo_content[] = "Lorem ipsum dolor sit amet, fabulas conclusionemque ius ad.";
    
        if (argc > 1) conninfo = argv[1]; else conninfo = "";
    
        conn = PQconnectdb(conninfo);
        check_PGconn(conn, CONNECTION_OK, "Connection to database failed");
    
        /* Create large object */
    
        res = PQexec(conn, "BEGIN");
        res_ok = check_PGresult(conn, res, PGRES_COMMAND_OK, "BEGIN failed");
        if ( !res_ok ) exit(1);
        PQclear(res);
    
        loid = lo_creat(conn, INV_READ|INV_WRITE);
        if (loid == 0) {
            fprintf(stderr, "lo_creat failed: %s", PQerrorMessage(conn));
            exit(1);
        }
    
        lofd = lo_open(conn, loid, INV_WRITE);
        if ( lofd == -1 ) {
            fprintf(stderr, "lo_open failed: %s", PQerrorMessage(conn));
            exit(1);
        }
    
        lo_res = lo_write(conn, lofd, lo_content, sizeof(lo_content));
        if ( lo_res == -1 ) {
            fprintf(stderr, "lo_write failed: %s", PQerrorMessage(conn));
            exit(1);
        }
    
        lo_res = lo_close(conn, lofd);
        if ( lo_res == -1 ) {
            fprintf(stderr, "lo_close failed: %s", PQerrorMessage(conn));
            exit(1);
        }
    
        res = PQexec(conn, "COMMIT");
        res_ok = check_PGresult(conn, res, PGRES_COMMAND_OK, "COMMIT failed");
        if ( !res_ok ) exit(1);
        PQclear(res);
    
        /* Delete large object */
    
        res = PQexec(conn, "BEGIN");
        res_ok = check_PGresult(conn, res, PGRES_COMMAND_OK, "BEGIN failed");
        if ( !res_ok ) exit(1);
        PQclear(res);
    
        lo_res = lo_unlink(conn, loid);
        if ( lo_res == -1 ) {
            fprintf(stderr, "lo_unlink failed: %s", PQerrorMessage(conn));
            exit(1);
        }
    
        res = PQexec(conn, "COMMIT");
        res_ok = check_PGresult(conn, res, PGRES_COMMAND_OK, "COMMIT failed");
        if ( !res_ok ) exit(1);
        PQclear(res);
    
        PQfinish(conn);
    }