This patch implements the ability for large objects to be larger than 2GB.
I believe the limit to now be about 2TB, based on the fact that the large
object page size is 2048 bytes, and the page number is still 32 bits.

There are a few things about this patch which probably require tweaking or
at least a closer inspection from the list.

1) The lo_*64 functions are added to the catalog/pg_proc.h (spacing exact
location atm) with OID set to 0, all other entries in this file have OIDs
explicitly defined.

2) The lo_*64, in order to be convenient from the client end, have
functions added to libpq as the existing lo_* functions.  The client side
of libpq did not previously know anything about int64 or how to
send/receive them.  I added an include of postgres-fe.h (which according
to the comment in that file looks like it should go there) so int64 would
be defined, also implemented functions (code mostly stolen from the server
libpq format functions for same) to convert them to/from network byte
order.  I did this in a somewhat inconsistent way between the get and put,
as I did not want to change the existing api at all, and existing code as
little as possible.

3) The 32 bit box I tested this on was a PII 300MHz laptop.  Not exactly
the fastest.  The "test" consisted entirely of making sure it compiled.
Perhaps someone with a fast IA32 box and spare cycles can test it?  Also,
so far the only platforms I have tried to compile this on have been:

* Linux 2.6 (gentoo), AMD64, gcc-3.4.4
* Solaris 8, SPARCv9, gcc-3.4.2
* Linux 2.6 (debian unstable), i686, gcc-3.4.x (laptop, don't remember
exact version).

Would probably be a good idea to verify this on other platforms as well,
or at least other compilers.

Hopefully I did not break anything too badly with this.  All of the
regression tests still pass after the patch, and I made a version of the
tests/examples/testlo which uses 64bit (in the patch) which works also.  I
grepped in the regression tests, and I could not find any usage of large
objects in them, which I found to be rather odd, which is why I used
testlo and my new testlo64 to test them instead.

On Tue, 20 Sep 2005, Jonah H. Harris wrote:

> Cool. We look forward to it.
>
> On 9/19/05, Mark Dilger <[EMAIL PROTECTED]> wrote:
> >
> > Jonah H. Harris wrote:
> > > Mark,
> > >
> > > If you don't mind contributing the changes, we'd be glad to take a look
> > > at them. Thanks.
> > >
> > > -Jonah
> > >
> >
> > Ok, we will post it back soon. We have tested it on two different 64-bit
> > architectures (Sparc and AMD) and are now testing on pentium before
> > posting up
> > to the list.
> >
> > mark
> >
>
>
>
> --
> Respectfully,
>
> Jonah H. Harris, Database Internals Architect
> EnterpriseDB Corporation
> http://www.enterprisedb.com/
>

-- 
Mere nonexistence is a feeble excuse for declaring a thing unseeable. You
*can* see dragons.  You just have to look in the right direction.
        -- John Hasler
diff -Nur postgresql-8.0.3-orig/src/backend/libpq/be-fsstubs.c 
postgresql-8.0.3/src/backend/libpq/be-fsstubs.c
--- postgresql-8.0.3-orig/src/backend/libpq/be-fsstubs.c        2004-12-31 
13:59:50.000000000 -0800
+++ postgresql-8.0.3/src/backend/libpq/be-fsstubs.c     2005-09-18 
17:22:17.000000000 -0700
@@ -233,6 +233,34 @@
        PG_RETURN_INT32(status);
 }
 
+
+Datum
+lo_lseek64(PG_FUNCTION_ARGS)
+{
+       int32           fd = PG_GETARG_INT32(0);
+       int64           offset = PG_GETARG_INT64(1);
+       int32           whence = PG_GETARG_INT32(2);
+       MemoryContext currentContext;
+       int64                   status;
+
+       if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
+       {
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                errmsg("invalid large-object descriptor: %d", 
fd)));
+               PG_RETURN_INT64(-1);
+       }
+
+       Assert(fscxt != NULL);
+       currentContext = MemoryContextSwitchTo(fscxt);
+
+       status = inv_seek64(cookies[fd], offset, whence);
+
+       MemoryContextSwitchTo(currentContext);
+
+       PG_RETURN_INT64(status);
+}
+
 Datum
 lo_creat(PG_FUNCTION_ARGS)
 {
@@ -283,6 +311,28 @@
        PG_RETURN_INT32(inv_tell(cookies[fd]));
 }
 
+
+Datum
+lo_tell64(PG_FUNCTION_ARGS)
+{
+       int32           fd = PG_GETARG_INT32(0);
+
+       if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
+       {
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                errmsg("invalid large-object descriptor: %d", 
fd)));
+               PG_RETURN_INT64(-1);
+       }
+
+       /*
+        * We assume we do not need to switch contexts for inv_tell. That is
+        * true for now, but is probably more than this module ought to
+        * assume...
+        */
+       PG_RETURN_INT64(inv_tell(cookies[fd]));
+}
+
 Datum
 lo_unlink(PG_FUNCTION_ARGS)
 {
diff -Nur postgresql-8.0.3-orig/src/backend/storage/large_object/inv_api.c 
postgresql-8.0.3/src/backend/storage/large_object/inv_api.c
--- postgresql-8.0.3-orig/src/backend/storage/large_object/inv_api.c    
2004-12-31 14:00:59.000000000 -0800
+++ postgresql-8.0.3/src/backend/storage/large_object/inv_api.c 2005-09-18 
19:28:24.000000000 -0700
@@ -255,11 +255,11 @@
  * NOTE: LOs can contain gaps, just like Unix files.  We actually return
  * the offset of the last byte + 1.
  */
-static uint32
+static uint64
 inv_getsize(LargeObjectDesc *obj_desc)
 {
        bool            found = false;
-       uint32          lastbyte = 0;
+       uint64          lastbyte = 0;
        ScanKeyData skey[1];
        IndexScanDesc sd;
        HeapTuple       tuple;
@@ -298,7 +298,7 @@
                                heap_tuple_untoast_attr((varattrib *) 
datafield);
                        pfreeit = true;
                }
-               lastbyte = data->pageno * LOBLKSIZE + getbytealen(datafield);
+               lastbyte = (uint64) data->pageno * LOBLKSIZE + 
getbytealen(datafield);
                if (pfreeit)
                        pfree(datafield);
                break;
@@ -353,15 +353,55 @@
        return obj_desc->offset;
 }
 
+int64
+inv_seek64(LargeObjectDesc *obj_desc, int64 offset, int whence)
+{
+       Assert(PointerIsValid(obj_desc));
+
+       switch (whence)
+       {
+               case SEEK_SET:
+                       if (offset < 0)
+                               elog(ERROR, "invalid seek offset: " 
INT64_FORMAT, offset);
+                       obj_desc->offset = offset;
+                       break;
+               case SEEK_CUR:
+                       if (offset < 0 && obj_desc->offset < ((uint64) 
(-offset)))
+                               elog(ERROR, "invalid seek offset: " 
INT64_FORMAT, offset);
+                       obj_desc->offset += offset;
+                       break;
+               case SEEK_END:
+                       {
+                               uint64          size = inv_getsize(obj_desc);
+
+                               if (offset < 0 && size < ((uint64) (-offset)))
+                                       elog(ERROR, "invalid seek offset: " 
INT64_FORMAT, offset);
+                               obj_desc->offset = size + offset;
+                       }
+                       break;
+               default:
+                       elog(ERROR, "invalid whence: %d", whence);
+       }
+       return obj_desc->offset;
+}
+
+int64
+inv_tell64(LargeObjectDesc *obj_desc)
+{
+       Assert(PointerIsValid(obj_desc));
+
+       return obj_desc->offset;
+}
+
 int
 inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
 {
        int                     nread = 0;
-       int                     n;
-       int                     off;
+       int64                   n;
+       int64                   off;
        int                     len;
        int32           pageno = (int32) (obj_desc->offset / LOBLKSIZE);
-       uint32          pageoff;
+       uint64          pageoff;
        ScanKeyData skey[2];
        IndexScanDesc sd;
        HeapTuple       tuple;
@@ -400,7 +440,7 @@
                 * there may be missing pages if the LO contains unwritten
                 * "holes". We want missing sections to read out as zeroes.
                 */
-               pageoff = ((uint32) data->pageno) * LOBLKSIZE;
+               pageoff = ((uint64) data->pageno) * LOBLKSIZE;
                if (pageoff > obj_desc->offset)
                {
                        n = pageoff - obj_desc->offset;
diff -Nur postgresql-8.0.3-orig/src/include/catalog/pg_proc.h 
postgresql-8.0.3/src/include/catalog/pg_proc.h
--- postgresql-8.0.3-orig/src/include/catalog/pg_proc.h 2004-12-31 
14:03:25.000000000 -0800
+++ postgresql-8.0.3/src/include/catalog/pg_proc.h      2005-09-17 
23:18:47.000000000 -0700
@@ -1224,10 +1224,14 @@
 DESCR("large object read");
 DATA(insert OID = 955 (  lowrite                  PGNSP PGUID 12 f f t f v 2 
23 "23 17" _null_ lowrite - _null_ ));
 DESCR("large object write");
+DATA(insert OID = 0   (  lo_lseek64               PGNSP PGUID 12 f f t f v 3 
20 "23 20 23" _null_      lo_lseek64 - _null_ ));
+DESCR("large object seek (64bit)");
 DATA(insert OID = 956 (  lo_lseek                 PGNSP PGUID 12 f f t f v 3 
23 "23 23 23" _null_      lo_lseek - _null_ ));
 DESCR("large object seek");
 DATA(insert OID = 957 (  lo_creat                 PGNSP PGUID 12 f f t f v 1 
26 "23" _null_  lo_creat - _null_ ));
 DESCR("large object create");
+DATA(insert OID = 0   (  lo_tell64                PGNSP PGUID 12 f f t f v 1 
20 "23" _null_  lo_tell64 - _null_ ));
+DESCR("large object position (64bit)");
 DATA(insert OID = 958 (  lo_tell                  PGNSP PGUID 12 f f t f v 1 
23 "23" _null_  lo_tell - _null_ ));
 DESCR("large object position");
 
diff -Nur postgresql-8.0.3-orig/src/include/libpq/be-fsstubs.h 
postgresql-8.0.3/src/include/libpq/be-fsstubs.h
--- postgresql-8.0.3-orig/src/include/libpq/be-fsstubs.h        2004-12-31 
14:03:32.000000000 -0800
+++ postgresql-8.0.3/src/include/libpq/be-fsstubs.h     2005-09-17 
23:18:47.000000000 -0700
@@ -32,6 +32,8 @@
 
 extern Datum lo_lseek(PG_FUNCTION_ARGS);
 extern Datum lo_tell(PG_FUNCTION_ARGS);
+extern Datum lo_lseek64(PG_FUNCTION_ARGS);
+extern Datum lo_tell64(PG_FUNCTION_ARGS);
 extern Datum lo_unlink(PG_FUNCTION_ARGS);
 
 /*
diff -Nur postgresql-8.0.3-orig/src/include/storage/large_object.h 
postgresql-8.0.3/src/include/storage/large_object.h
--- postgresql-8.0.3-orig/src/include/storage/large_object.h    2004-12-31 
14:03:42.000000000 -0800
+++ postgresql-8.0.3/src/include/storage/large_object.h 2005-09-17 
23:18:47.000000000 -0700
@@ -33,7 +33,7 @@
 {
        Oid                     id;                             /* LO's 
identifier */
        SubTransactionId subid;         /* owning subtransaction ID */
-       uint32          offset;                 /* current seek pointer */
+       uint64          offset;                 /* current seek pointer */
        int                     flags;                  /* locking info, etc */
 
 /* flag bits: */
@@ -71,6 +71,8 @@
 extern int     inv_drop(Oid lobjId);
 extern int     inv_seek(LargeObjectDesc *obj_desc, int offset, int whence);
 extern int     inv_tell(LargeObjectDesc *obj_desc);
+extern int64   inv_seek64(LargeObjectDesc *obj_desc, int64 offset, int whence);
+extern int64   inv_tell64(LargeObjectDesc *obj_desc);
 extern int     inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes);
 extern int     inv_write(LargeObjectDesc *obj_desc, char *buf, int nbytes);
 
diff -Nur postgresql-8.0.3-orig/src/interfaces/libpq/fe-lobj.c 
postgresql-8.0.3/src/interfaces/libpq/fe-lobj.c
--- postgresql-8.0.3-orig/src/interfaces/libpq/fe-lobj.c        2004-12-31 
14:03:50.000000000 -0800
+++ postgresql-8.0.3/src/interfaces/libpq/fe-lobj.c     2005-09-19 
00:39:20.000000000 -0700
@@ -264,6 +264,61 @@
 }
 
 /*
+ * lo_lseek64
+ *       change the current read or write location on a large object
+ * currently, only L_SET is a legal value for whence
+ *
+ */
+
+int64
+lo_lseek64(PGconn *conn, int fd, int64 offset, int whence)
+{
+       PQArgBlock      argv[3];
+       PGresult   *res;
+       int64                   retval;
+       int                     result_len;
+
+       if (conn->lobjfuncs == NULL)
+       {
+               if (lo_initialize(conn) < 0)
+                       return -1;
+       }
+
+       if (conn->lobjfuncs->fn_lo_lseek64 == 0)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                 libpq_gettext("cannot determine OID of function 
lo_lseek64\n"));
+
+               return -1;
+       }
+
+       argv[0].isint = 1;
+       argv[0].len = 4;
+       argv[0].u.integer = fd;
+
+       argv[1].isint = 1;
+       argv[1].len = 8;
+       argv[1].u.ptr = (int *) &offset;
+
+       argv[2].isint = 1;
+       argv[2].len = 4;
+       argv[2].u.integer = whence;
+
+       res = PQfn(conn, conn->lobjfuncs->fn_lo_lseek64,
+                          (int *)&retval, &result_len, 1, argv, 3);
+       if (PQresultStatus(res) == PGRES_COMMAND_OK)
+       {
+               PQclear(res);
+               return retval;
+       }
+       else
+       {
+               PQclear(res);
+               return -1;
+       }
+}
+
+/*
  * lo_creat
  *       create a new large object
  * the mode is a bitmask describing different attributes of the new object
@@ -305,6 +360,53 @@
 
 
 /*
+ * lo_tell64
+ *       returns the current seek location of the large object
+ *
+ */
+
+int64
+lo_tell64(PGconn *conn, int fd)
+{
+       int64                   retval;
+       PQArgBlock      argv[1];
+       PGresult   *res;
+       int                     result_len;
+
+       if (conn->lobjfuncs == NULL)
+       {
+               if (lo_initialize(conn) < 0)
+                       return -1;
+       }
+
+       if (conn->lobjfuncs->fn_lo_tell64 == 0)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                 libpq_gettext("cannot determine OID of function 
lo_tell64\n"));
+
+               return -1;
+       }
+
+       argv[0].isint = 1;
+       argv[0].len = 4;
+       argv[0].u.integer = fd;
+
+       res = PQfn(conn, conn->lobjfuncs->fn_lo_tell64,
+                          (int *) &retval, &result_len, 1, argv, 1);
+       if (PQresultStatus(res) == PGRES_COMMAND_OK)
+       {
+               PQclear(res);
+               return retval;
+       }
+       else
+       {
+               PQclear(res);
+               return -1;
+       }
+}
+
+
+/*
  * lo_tell
  *       returns the current seek location of the large object
  *
@@ -570,7 +672,9 @@
                        "'lo_creat', "
                        "'lo_unlink', "
                        "'lo_lseek', "
+                       "'lo_lseek64', "
                        "'lo_tell', "
+                       "'lo_tell64', "
                        "'loread', "
                        "'lowrite') "
                        "and pronamespace = (select oid from 
pg_catalog.pg_namespace "
@@ -583,6 +687,8 @@
                        "or proname = 'lo_unlink' "
                        "or proname = 'lo_lseek' "
                        "or proname = 'lo_tell' "
+                       "or proname = 'lo_lseek64' "
+                       "or proname = 'lo_tell64' "
                        "or proname = 'loread' "
                        "or proname = 'lowrite'";
 
@@ -621,6 +727,10 @@
                        lobjfuncs->fn_lo_lseek = foid;
                else if (!strcmp(fname, "lo_tell"))
                        lobjfuncs->fn_lo_tell = foid;
+               else if (!strcmp(fname, "lo_lseek64"))
+                       lobjfuncs->fn_lo_lseek64 = foid;
+               else if (!strcmp(fname, "lo_tell64"))
+                       lobjfuncs->fn_lo_tell64 = foid;
                else if (!strcmp(fname, "loread"))
                        lobjfuncs->fn_lo_read = foid;
                else if (!strcmp(fname, "lowrite"))
diff -Nur postgresql-8.0.3-orig/src/interfaces/libpq/fe-misc.c 
postgresql-8.0.3/src/interfaces/libpq/fe-misc.c
--- postgresql-8.0.3-orig/src/interfaces/libpq/fe-misc.c        2004-12-31 
14:03:50.000000000 -0800
+++ postgresql-8.0.3/src/interfaces/libpq/fe-misc.c     2005-09-18 
19:26:58.000000000 -0700
@@ -223,6 +223,27 @@
                        conn->inCursor += 4;
                        *result = (int) ntohl(tmp4);
                        break;
+               case 8:
+                       if (conn->inCursor + 8 > conn->inEnd)
+                               return EOF;
+                       else
+                       {
+                               int64 * i64res = (int64 *)result;
+                               uint32 h32;
+                               memcpy(&h32, conn->inBuffer + conn->inCursor, 
4);
+                               conn->inCursor += 4;
+                               memcpy(&tmp4, conn->inBuffer + conn->inCursor, 
4);
+                               conn->inCursor += 4;
+#ifdef INT64_IS_BUSTED
+                               /* just lose the high half */
+                               *i64res = (int) ntohl(tmp4);
+#else
+                               *i64res = (int) ntohl(h32);
+                               *i64res <<= 32;
+                               *i64res |= (uint32) ntohl(tmp4);
+#endif
+                       }
+                       break;
                default:
                        pqInternalNotice(&conn->noticeHooks,
                                                 "integer of size %lu not 
supported by pqGetInt",
@@ -231,12 +252,23 @@
        }
 
        if (conn->Pfdebug)
-               fprintf(conn->Pfdebug, "From backend (#%lu)> %d\n", (unsigned 
long) bytes, *result);
+               fprintf(conn->Pfdebug, "From backend (#%lu)> " INT64_FORMAT 
"\n", (unsigned long) bytes, (bytes == 8) ? *(int64 *)result : (int64)*result);
 
        return 0;
 }
 
 /*
+ * pqGetInt64
+ *     read an 8 byte integer and convert from network byte order
+ *     to local byte order
+ */
+int
+pqGetInt64(int64 *result, PGconn *conn)
+{
+       return pqGetInt((int *)result, 8, conn);
+}
+
+/*
  * pqPutInt
  * write an integer of 2 or 4 bytes, converting from host byte order
  * to network byte order.
@@ -272,6 +304,41 @@
        return 0;
 }
 
+
+/*
+ * pqPutInt64
+ * write an integer of 8 bytes, converting from host byte order
+ * to network byte order.
+ */
+int
+pqPutInt64(int64 value, PGconn *conn)
+{
+       uint32          n32;
+
+       /* High order half first, since we're doing MSB-first */
+#ifdef INT64_IS_BUSTED
+       /* don't try a right shift of 32 on a 32-bit word */
+       n32 = (value < 0) ? -1 : 0;
+#else
+       n32 = (uint32) (value >> 32);
+#endif
+       n32 = htonl(n32);
+       if (pqPutMsgBytes((const char *) &n32, 4, conn))
+               return EOF;
+
+       /* Now the low order half */
+       n32 = (uint32) value;
+       n32 = htonl(n32);
+
+       if (pqPutMsgBytes((const char *) &n32, 4, conn))
+               return EOF;
+
+       if (conn->Pfdebug)
+               fprintf(conn->Pfdebug, "To backend (8#)> " INT64_FORMAT "\n", 
value);
+
+       return 0;
+}
+
 /*
  * Make sure conn's output buffer can hold bytes_needed bytes (caller must
  * include already-stored data into the value!)
diff -Nur postgresql-8.0.3-orig/src/interfaces/libpq/fe-protocol3.c 
postgresql-8.0.3/src/interfaces/libpq/fe-protocol3.c
--- postgresql-8.0.3-orig/src/interfaces/libpq/fe-protocol3.c   2004-12-31 
14:03:50.000000000 -0800
+++ postgresql-8.0.3/src/interfaces/libpq/fe-protocol3.c        2005-09-18 
16:47:10.000000000 -0700
@@ -1233,11 +1233,24 @@
 
                if (args[i].isint)
                {
-                       if (pqPutInt(args[i].u.integer, args[i].len, conn))
+                       if (args[i].len <= 4)
                        {
-                               pqHandleSendFailure(conn);
-                               return NULL;
+                               if (pqPutInt(args[i].u.integer, args[i].len, 
conn))
+                               {
+                                       pqHandleSendFailure(conn);
+                                       return NULL;
+                               }
+                       }
+                       else
+                       {
+                               if (pqPutInt64(*(int64 *)args[i].u.ptr, conn))
+                               {
+                                       pqHandleSendFailure(conn);
+                                       return NULL;
+                               }
                        }
+
+
                }
                else
                {
diff -Nur postgresql-8.0.3-orig/src/interfaces/libpq/libpq-fe.h 
postgresql-8.0.3/src/interfaces/libpq/libpq-fe.h
--- postgresql-8.0.3-orig/src/interfaces/libpq/libpq-fe.h       2004-12-31 
14:03:50.000000000 -0800
+++ postgresql-8.0.3/src/interfaces/libpq/libpq-fe.h    2005-09-17 
23:18:47.000000000 -0700
@@ -27,6 +27,7 @@
  * such as Oid.
  */
 #include "postgres_ext.h"
+#include "postgres_fe.h"
 
 /* SSL type is needed here only to declare PQgetssl() */
 #ifdef USE_SSL
@@ -479,8 +480,10 @@
 extern int     lo_read(PGconn *conn, int fd, char *buf, size_t len);
 extern int     lo_write(PGconn *conn, int fd, char *buf, size_t len);
 extern int     lo_lseek(PGconn *conn, int fd, int offset, int whence);
+extern int64   lo_lseek64(PGconn *conn, int fd, int64 offset, int whence);
 extern Oid     lo_creat(PGconn *conn, int mode);
 extern int     lo_tell(PGconn *conn, int fd);
+extern int64   lo_tell64(PGconn *conn, int fd);
 extern int     lo_unlink(PGconn *conn, Oid lobjId);
 extern Oid     lo_import(PGconn *conn, const char *filename);
 extern int     lo_export(PGconn *conn, Oid lobjId, const char *filename);
diff -Nur postgresql-8.0.3-orig/src/interfaces/libpq/libpq-int.h 
postgresql-8.0.3/src/interfaces/libpq/libpq-int.h
--- postgresql-8.0.3-orig/src/interfaces/libpq/libpq-int.h      2005-01-05 
16:59:47.000000000 -0800
+++ postgresql-8.0.3/src/interfaces/libpq/libpq-int.h   2005-09-18 
16:05:46.000000000 -0700
@@ -232,6 +232,8 @@
        Oid                     fn_lo_unlink;   /* OID of backend function 
lo_unlink    */
        Oid                     fn_lo_lseek;    /* OID of backend function 
lo_lseek             */
        Oid                     fn_lo_tell;             /* OID of backend 
function lo_tell              */
+       Oid                     fn_lo_lseek64;  /* OID of backend function 
lo_lseek64           */
+       Oid                     fn_lo_tell64;           /* OID of backend 
function lo_tell64            */
        Oid                     fn_lo_read;             /* OID of backend 
function LOread               */
        Oid                     fn_lo_write;    /* OID of backend function 
LOwrite              */
 } PGlobjfuncs;
@@ -457,7 +459,9 @@
 extern int     pqGetnchar(char *s, size_t len, PGconn *conn);
 extern int     pqPutnchar(const char *s, size_t len, PGconn *conn);
 extern int     pqGetInt(int *result, size_t bytes, PGconn *conn);
+extern int     pqGetInt64(int64 *result, PGconn *conn);
 extern int     pqPutInt(int value, size_t bytes, PGconn *conn);
+extern int     pqPutInt64(int64 value, PGconn *conn);
 extern int     pqPutMsgStart(char msg_type, bool force_len, PGconn *conn);
 extern int     pqPutMsgEnd(PGconn *conn);
 extern int     pqReadData(PGconn *conn);
diff -Nur postgresql-8.0.3-orig/src/test/examples/Makefile 
postgresql-8.0.3/src/test/examples/Makefile
--- postgresql-8.0.3-orig/src/test/examples/Makefile    2005-03-25 
10:18:41.000000000 -0800
+++ postgresql-8.0.3/src/test/examples/Makefile 2005-09-17 23:18:47.000000000 
-0700
@@ -6,11 +6,11 @@
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS)
+override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS) -g
 override LDLIBS := $(libpq_pgport) $(LDLIBS)
 
 
-PROGS = testlibpq testlibpq2 testlibpq3 testlibpq4 testlo
+PROGS = testlibpq testlibpq2 testlibpq3 testlibpq4 testlo testlo64
 
 all: $(PROGS)
 
diff -Nur postgresql-8.0.3-orig/src/test/examples/testlo64.c 
postgresql-8.0.3/src/test/examples/testlo64.c
--- postgresql-8.0.3-orig/src/test/examples/testlo64.c  1969-12-31 
16:00:00.000000000 -0800
+++ postgresql-8.0.3/src/test/examples/testlo64.c       2005-09-19 
00:52:16.000000000 -0700
@@ -0,0 +1,267 @@
+/*-------------------------------------------------------------------------
+ *
+ * testlo.c
+ *       test using large objects with libpq
+ *
+ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       $PostgreSQL: pgsql/src/test/examples/testlo.c,v 1.25 2004/12/31 
22:03:58 pgsql Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "libpq-fe.h"
+#include "libpq/libpq-fs.h"
+
+#define BUFSIZE                        1024
+
+/*
+ * importFile -
+ *       import file "in_filename" into database as large object "lobjOid"
+ *
+ */
+static Oid
+importFile(PGconn *conn, char *filename)
+{
+       Oid                     lobjId;
+       int                     lobj_fd;
+       char            buf[BUFSIZE];
+       int                     nbytes,
+                               tmp;
+       int                     fd;
+
+       /*
+        * open the file to be read in
+        */
+       fd = open(filename, O_RDONLY, 0666);
+       if (fd < 0)
+       {                                                       /* error */
+               fprintf(stderr, "can't open unix file\"%s\"\n", filename);
+       }
+
+       /*
+        * create the large object
+        */
+       lobjId = lo_creat(conn, INV_READ | INV_WRITE);
+       if (lobjId == 0)
+               fprintf(stderr, "can't create large object");
+
+       lobj_fd = lo_open(conn, lobjId, INV_WRITE);
+
+       /*
+        * read in from the Unix file and write to the inversion file
+        */
+       while ((nbytes = read(fd, buf, BUFSIZE)) > 0)
+       {
+               tmp = lo_write(conn, lobj_fd, buf, nbytes);
+               if (tmp < nbytes)
+                       fprintf(stderr, "error while reading \"%s\"", filename);
+       }
+
+       close(fd);
+       lo_close(conn, lobj_fd);
+
+       return lobjId;
+}
+
+static void
+pickout(PGconn *conn, Oid lobjId, int64 start, int len)
+{
+       int                     lobj_fd;
+       char       *buf;
+       int                     nbytes;
+       int                     nread;
+
+       lobj_fd = lo_open(conn, lobjId, INV_READ);
+       if (lobj_fd < 0)
+               fprintf(stderr, "can't open large object %u", lobjId);
+
+       if (lo_lseek64(conn, lobj_fd, start, SEEK_SET) < 0)
+       {
+               fprintf(stderr, "error lo_lseek64: %s\n", PQerrorMessage(conn));
+               return;
+       }
+
+       buf = malloc(len + 1);
+
+       nread = 0;
+       while (len - nread > 0)
+       {
+               nbytes = lo_read(conn, lobj_fd, buf, len - nread);
+               buf[nbytes] = '\0';
+               fprintf(stderr, ">>> %s", buf);
+               nread += nbytes;
+               if (nbytes <= 0)
+                       break;                          /* no more data? */
+       }
+       free(buf);
+       fprintf(stderr, "\n");
+       lo_close(conn, lobj_fd);
+}
+
+static void
+overwrite(PGconn *conn, Oid lobjId, int64 start, int len)
+{
+       int                     lobj_fd;
+       char       *buf;
+       int                     nbytes;
+       int                     nwritten;
+       int                     i;
+
+       lobj_fd = lo_open(conn, lobjId, INV_READ);
+       if (lobj_fd < 0)
+               fprintf(stderr, "can't open large object %u", lobjId);
+
+       lo_lseek64(conn, lobj_fd, start, SEEK_SET);
+       buf = malloc(len + 1);
+
+       for (i = 0; i < len; i++)
+               buf[i] = 'X';
+       buf[i] = '\0';
+
+       nwritten = 0;
+       while (len - nwritten > 0)
+       {
+               nbytes = lo_write(conn, lobj_fd, buf + nwritten, len - 
nwritten);
+               nwritten += nbytes;
+               if (nbytes <= 0)
+               {
+                       fprintf(stderr, "\nWRITE FAILED!\n");
+                       break;
+               }
+       }
+       free(buf);
+       fprintf(stderr, "\n");
+       lo_close(conn, lobj_fd);
+}
+
+
+/*
+ * exportFile -
+ *       export large object "lobjOid" to file "out_filename"
+ *
+ */
+static void
+exportFile(PGconn *conn, Oid lobjId, char *filename)
+{
+       int                     lobj_fd;
+       char            buf[BUFSIZE];
+       int                     nbytes,
+                               tmp;
+       int                     fd;
+
+       /*
+        * create an inversion "object"
+        */
+       lobj_fd = lo_open(conn, lobjId, INV_READ);
+       if (lobj_fd < 0)
+               fprintf(stderr, "can't open large object %u", lobjId);
+
+       /*
+        * open the file to be written to
+        */
+       fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
+       if (fd < 0)
+       {                                                       /* error */
+               fprintf(stderr, "can't open unix file\"%s\"",
+                               filename);
+       }
+
+       /*
+        * read in from the Unix file and write to the inversion file
+        */
+       while ((nbytes = lo_read(conn, lobj_fd, buf, BUFSIZE)) > 0)
+       {
+               tmp = write(fd, buf, nbytes);
+               if (tmp < nbytes)
+               {
+                       fprintf(stderr, "error while writing \"%s\"",
+                                       filename);
+               }
+       }
+
+       lo_close(conn, lobj_fd);
+       close(fd);
+
+       return;
+}
+
+static void
+exit_nicely(PGconn *conn)
+{
+       PQfinish(conn);
+       exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+       char       *in_filename,
+                          *out_filename;
+       char       *database;
+       Oid                     lobjOid;
+       PGconn     *conn;
+       PGresult   *res;
+
+       if (argc != 4)
+       {
+               fprintf(stderr, "Usage: %s database_name in_filename 
out_filename\n",
+                               argv[0]);
+               exit(1);
+       }
+
+       database = argv[1];
+       in_filename = argv[2];
+       out_filename = argv[3];
+
+       /*
+        * set up the connection
+        */
+       conn = PQsetdb(NULL, NULL, NULL, NULL, database);
+
+       /* 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, "begin");
+       PQclear(res);
+       printf("importing file \"%s\" ...\n", in_filename);
+/*     lobjOid = importFile(conn, in_filename); */
+       lobjOid = lo_import(conn, in_filename);
+       if (lobjOid == 0)
+               fprintf(stderr, "%s\n", PQerrorMessage(conn));
+       else
+       {
+               printf("\tas large object %u.\n", lobjOid);
+
+               printf("picking out bytes 4294967000-4294968000 of the large 
object\n");
+               pickout(conn, lobjOid, 4294967000ULL, 1000);
+
+               printf("overwriting bytes 4294967000-4294968000 of the large 
object with X's\n");
+               overwrite(conn, lobjOid, 4294967000ULL, 1000);
+
+               printf("exporting large object to file \"%s\" ...\n", 
out_filename);
+/*             exportFile(conn, lobjOid, out_filename); */
+               if (!lo_export(conn, lobjOid, out_filename))
+                       fprintf(stderr, "%s\n", PQerrorMessage(conn));
+       }
+
+       res = PQexec(conn, "end");
+       PQclear(res);
+       PQfinish(conn);
+       return 0;
+}
---------------------------(end of broadcast)---------------------------
TIP 2: Don't 'kill -9' the postmaster

Reply via email to