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.
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);
}