On Mon, 18 Aug 2008, Tom Lane wrote:

> What would make more sense is to redesign the large-object stuff to be
> somewhat modern and featureful, and provide stream-access APIs (think
> lo_read, lo_seek, etc) that allow offsets wider than 32 bits.

A few years ago, I was working on such a project for a company I used to
work for.  The company changed directions shortly thereafter, and the
project was dropped, but perhaps the patch might still be useful as a
starting point for someone else.

The original patch is
http://archives.postgresql.org/pgsql-hackers/2005-09/msg01026.php, and the
advice I was working on implementing was in
http://archives.postgresql.org/pgsql-hackers/2005-09/msg01063.php

I am attaching the latest version of the patch I found around.  As it was
almost 3 years ago, I am a little fuzzy on where I left off, but I do
remember that I was trying to work through the suggestions Tom Lane gave
in that second linked email.  I would recommend discarding the libpq
changes, since that seemed to not pass muster.

Note that this patch was against 8.0.3.  There only seem to be a few
issues applying it to the current head, but I haven't really dug into them
to see how difficult it would be to update.  Luckily, the large object
code is fairly slow-moving, so there aren't too many conflicts.  One thing
I did notice is that it looks like someone extracted one of the functions
I wrote in this patch and applied it as a 32-bit version.  Good for them.
I'm glad someone got some use out of this project, and perhaps more use
will come of it.



-- 
At the source of every error which is blamed on the computer you will
find at least two human errors, including the error of blaming it on
the computer.
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-10-03 
11:43:36.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_seek(cookies[fd], offset, whence);
+
+       MemoryContextSwitchTo(currentContext);
+
+       PG_RETURN_INT64(status);
+}
+
 Datum
 lo_creat(PG_FUNCTION_ARGS)
 {
@@ -283,6 +311,165 @@
        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_length(PG_FUNCTION_ARGS)
+{
+       int32           fd = PG_GETARG_INT32(0);
+       int32           sz = 0;
+       MemoryContext currentContext;
+
+       if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
+       {
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                errmsg("invalid large-object descriptor: %d", 
fd)));
+               PG_RETURN_INT32(-1);
+       }
+       Assert(fscxt != NULL);
+       currentContext = MemoryContextSwitchTo(fscxt);
+
+       sz = inv_length(cookies[fd]);
+
+       MemoryContextSwitchTo(currentContext);
+
+       PG_RETURN_INT32(sz);
+}
+
+Datum
+lo_length64(PG_FUNCTION_ARGS)
+{
+       int32           fd = PG_GETARG_INT32(0);
+       int64           sz = 0;
+       MemoryContext currentContext;
+
+       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);
+
+       sz = inv_length(cookies[fd]);
+
+       MemoryContextSwitchTo(currentContext);
+
+       PG_RETURN_INT64(sz);
+}
+
+Datum
+lo_truncate(PG_FUNCTION_ARGS)
+{
+       int32           fd = PG_GETARG_INT32(0);
+       int32           offset = PG_GETARG_INT32(1);
+       int             status = 0;
+       MemoryContext currentContext;
+
+       if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
+       {
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                errmsg("invalid large-object descriptor: %d", 
fd)));
+               PG_RETURN_INT32(-1);
+       }
+       Assert(fscxt != NULL);
+       currentContext = MemoryContextSwitchTo(fscxt);
+
+       status = inv_truncate(cookies[fd], offset);
+
+       MemoryContextSwitchTo(currentContext);
+
+       PG_RETURN_INT32(status);
+}
+
+
+Datum
+lo_truncate64(PG_FUNCTION_ARGS)
+{
+       int32           fd = PG_GETARG_INT32(0);
+       int64           offset = PG_GETARG_INT64(1);
+       int             status = 0;
+       MemoryContext currentContext;
+
+       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_truncate(cookies[fd], offset);
+
+       MemoryContextSwitchTo(currentContext);
+
+       PG_RETURN_INT32(status);
+}
+
+Datum
+lo_stat(PG_FUNCTION_ARGS)
+{
+       Oid                     lobjId = PG_GETARG_OID(0);
+       MemoryContext currentContext;
+       int32 sz = 0;
+
+       if (fscxt == NULL)
+       {
+               CreateFSContext();
+       }
+
+       currentContext = MemoryContextSwitchTo(fscxt);
+       sz = inv_stat (lobjId);
+       MemoryContextSwitchTo(currentContext);
+
+       PG_RETURN_INT32(sz);
+}
+
+Datum
+lo_stat64(PG_FUNCTION_ARGS)
+{
+       Oid                     lobjId = PG_GETARG_OID(0);
+       MemoryContext currentContext;
+       int64 sz = 0;
+
+       if (fscxt == NULL)
+       {
+               CreateFSContext();
+       }
+
+       currentContext = MemoryContextSwitchTo(fscxt);
+       sz = inv_stat (lobjId);
+       MemoryContextSwitchTo(currentContext);
+
+       PG_RETURN_INT64(sz);
+}
+
 Datum
 lo_unlink(PG_FUNCTION_ARGS)
 {
Binary files 
postgresql-8.0.3-orig/src/backend/storage/large_object/.inv_api.c.swp and 
postgresql-8.0.3/src/backend/storage/large_object/.inv_api.c.swp differ
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-10-03 
12:15:39.000000000 -0700
@@ -255,23 +255,21 @@
  * NOTE: LOs can contain gaps, just like Unix files.  We actually return
  * the offset of the last byte + 1.
  */
-static uint32
-inv_getsize(LargeObjectDesc *obj_desc)
+static int64
+inv_getsize(Oid lobjId)
 {
        bool            found = false;
-       uint32          lastbyte = 0;
+       int64           lastbyte = 0;
        ScanKeyData skey[1];
        IndexScanDesc sd;
        HeapTuple       tuple;
 
-       Assert(PointerIsValid(obj_desc));
-
        open_lo_relation();
 
        ScanKeyInit(&skey[0],
                                Anum_pg_largeobject_loid,
                                BTEqualStrategyNumber, F_OIDEQ,
-                               ObjectIdGetDatum(obj_desc->id));
+                               ObjectIdGetDatum(lobjId));
 
        sd = index_beginscan(lo_heap_r, lo_index_r,
                                                 SnapshotNow, 1, skey);
@@ -298,7 +296,7 @@
                                heap_tuple_untoast_attr((varattrib *) 
datafield);
                        pfreeit = true;
                }
-               lastbyte = data->pageno * LOBLKSIZE + getbytealen(datafield);
+               lastbyte = (int64) data->pageno * LOBLKSIZE + 
getbytealen(datafield);
                if (pfreeit)
                        pfree(datafield);
                break;
@@ -309,12 +307,40 @@
        if (!found)
                ereport(ERROR,
                                (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                errmsg("large object %u does not exist", 
obj_desc->id)));
+                                errmsg("large object %u does not exist", 
lobjId)));
        return lastbyte;
 }
 
-int
-inv_seek(LargeObjectDesc *obj_desc, int offset, int whence)
+/*
+ * Determine size of a large object
+ *
+ * NOTE: LOs can contain gaps, just like Unix files.  We actually return
+ * the offset of the last byte + 1.
+ */
+static int64
+inv_fgetsize(LargeObjectDesc *obj_desc)
+{
+       Assert(PointerIsValid(obj_desc));
+
+       return inv_getsize(obj_desc->id);
+}
+
+int64
+inv_length(LargeObjectDesc * obj_desc)
+{
+       Assert(PointerIsValid(obj_desc));
+
+       return inv_fgetsize(obj_desc);
+}
+
+int64
+inv_stat(Oid lobjId)
+{
+       return inv_getsize(lobjId);
+}
+
+int64
+inv_seek(LargeObjectDesc *obj_desc, int64 offset, int whence)
 {
        Assert(PointerIsValid(obj_desc));
 
@@ -322,20 +348,20 @@
        {
                case SEEK_SET:
                        if (offset < 0)
-                               elog(ERROR, "invalid seek offset: %d", offset);
+                               elog(ERROR, "invalid seek offset: " 
INT64_FORMAT, offset);
                        obj_desc->offset = offset;
                        break;
                case SEEK_CUR:
-                       if (offset < 0 && obj_desc->offset < ((uint32) 
(-offset)))
-                               elog(ERROR, "invalid seek offset: %d", offset);
+                       if (offset < 0 && obj_desc->offset < ((uint64) 
(-offset)))
+                               elog(ERROR, "invalid seek offset: " 
INT64_FORMAT, offset);
                        obj_desc->offset += offset;
                        break;
                case SEEK_END:
                        {
-                               uint32          size = inv_getsize(obj_desc);
+                               int64           size = inv_fgetsize(obj_desc);
 
-                               if (offset < 0 && size < ((uint32) (-offset)))
-                                       elog(ERROR, "invalid seek offset: %d", 
offset);
+                               if (offset < 0 && size < ((uint64) (-offset)))
+                                       elog(ERROR, "invalid seek offset: " 
INT64_FORMAT, offset);
                                obj_desc->offset = size + offset;
                        }
                        break;
@@ -345,7 +371,7 @@
        return obj_desc->offset;
 }
 
-int
+int64
 inv_tell(LargeObjectDesc *obj_desc)
 {
        Assert(PointerIsValid(obj_desc));
@@ -357,11 +383,11 @@
 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;
+       int64           pageoff;
        ScanKeyData skey[2];
        IndexScanDesc sd;
        HeapTuple       tuple;
@@ -400,7 +426,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 = ((int64) data->pageno) * LOBLKSIZE;
                if (pageoff > obj_desc->offset)
                {
                        n = pageoff - obj_desc->offset;
@@ -447,6 +473,157 @@
 }
 
 int
+inv_truncate(LargeObjectDesc *obj_desc, int64 offset)
+{
+       int64 objsz = inv_fgetsize(obj_desc);
+       if (offset > objsz)
+       {
+               int64 tempoff = obj_desc->offset;
+               obj_desc->offset = offset - 1;
+               char nul = '\0';
+               int ret = inv_write(obj_desc, &nul, 1);
+               obj_desc->offset = tempoff;
+               if (ret < 0)
+                       return -1;
+               else
+                       return 0;
+       }
+       else
+       {
+               int                     n;
+               int                     off;
+               int                     len;
+               int32           pageno = (int32) (offset / LOBLKSIZE);
+               ScanKeyData skey[2];
+               IndexScanDesc sd;
+               HeapTuple       oldtuple;
+               Form_pg_largeobject olddata;
+               bytea      *datafield;
+               bool            pfreeit;
+               struct
+               {
+                       bytea           hdr;
+                       char            data[LOBLKSIZE];
+               }                       workbuf;
+               char       *workb = VARATT_DATA(&workbuf.hdr);
+               HeapTuple       newtup;
+               Datum           values[Natts_pg_largeobject];
+               char            nulls[Natts_pg_largeobject];
+               char            replace[Natts_pg_largeobject];
+               CatalogIndexState indstate;
+
+               Assert(PointerIsValid(obj_desc));
+
+               open_lo_relation();
+
+               indstate = CatalogOpenIndexes(lo_heap_r);
+
+               ScanKeyInit(&skey[0],
+                                       Anum_pg_largeobject_loid,
+                                       BTEqualStrategyNumber, F_OIDEQ,
+                                       ObjectIdGetDatum(obj_desc->id));
+
+               ScanKeyInit(&skey[1],
+                                       Anum_pg_largeobject_pageno,
+                                       BTGreaterEqualStrategyNumber, F_INT4GE,
+                                       Int32GetDatum(pageno));
+
+               sd = index_beginscan(lo_heap_r, lo_index_r,
+                                                        SnapshotNow, 2, skey);
+
+               oldtuple = NULL;
+               olddata = NULL;
+
+               while ((oldtuple = index_getnext(sd, ForwardScanDirection)) != 
NULL)
+               {
+                       olddata = (Form_pg_largeobject) GETSTRUCT(oldtuple);
+                       Assert(olddata->pageno >= pageno);
+
+                       /*
+                        * If we have a pre-existing page, see if it is the 
page we want
+                        * to write, or a later one.
+                        */
+                       if (olddata != NULL && olddata->pageno == pageno)
+                       {
+                               /*
+                                * Update an existing page with fresh data.
+                                *
+                                * First, load old data into workbuf
+                                */
+                               datafield = &(olddata->data);
+                               pfreeit = false;
+                               if (VARATT_IS_EXTENDED(datafield))
+                               {
+                                       datafield = (bytea *)
+                                               
heap_tuple_untoast_attr((varattrib *) datafield);
+                                       pfreeit = true;
+                               }
+                               len = getbytealen(datafield);
+                               Assert(len <= LOBLKSIZE);
+                               memcpy(workb, VARDATA(datafield), len);
+                               if (pfreeit)
+                                       pfree(datafield);
+
+                               /*
+                                * Fill any hole
+                                */
+                               off = (int) (offset % LOBLKSIZE);
+
+                               /*
+                                * Insert appropriate portion of new data
+                                */
+                               n = LOBLKSIZE - off;
+                               MemSet(workb + off, 0, n);
+                               /* compute valid length of new page */
+                               len = off;
+                               VARATT_SIZEP(&workbuf.hdr) = len + VARHDRSZ;
+
+                               /*
+                                * Form and insert updated tuple
+                                */
+                               memset(values, 0, sizeof(values));
+                               memset(nulls, ' ', sizeof(nulls));
+                               memset(replace, ' ', sizeof(replace));
+                               values[Anum_pg_largeobject_data - 1] = 
PointerGetDatum(&workbuf);
+                               replace[Anum_pg_largeobject_data - 1] = 'r';
+                               newtup = heap_modifytuple(oldtuple, lo_heap_r,
+                                                                               
  values, nulls, replace);
+                               simple_heap_update(lo_heap_r, &newtup->t_self, 
newtup);
+                               CatalogIndexInsert(indstate, newtup);
+                               heap_freetuple(newtup);
+
+                               /*
+                                * We're done with this old page.
+                                */
+                               oldtuple = NULL;
+                               olddata = NULL;
+                       }
+                       else
+                       {
+                               /*
+                                * Delete page.
+                                *
+                                * First, fill any hole
+                                */
+                               simple_heap_delete(lo_heap_r, 
&oldtuple->t_self);
+                       }
+               }
+
+               index_endscan(sd);
+
+               CatalogCloseIndexes(indstate);
+
+               /*
+                * Advance command counter so that my tuple updates will be 
seen by
+                * later large-object operations in this transaction.
+                */
+               CommandCounterIncrement();
+       }
+
+       return 0;
+}
+
+int
 inv_write(LargeObjectDesc *obj_desc, char *buf, int nbytes)
 {
        int                     nwritten = 0;
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-10-03 
11:56:27.000000000 -0700
@@ -1224,12 +1224,28 @@
 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_truncate                PGNSP PGUID 12 f f t f v 2 
23 "23 23" _null_ lo_truncate - _null_ ));
+DESCR("large object resize");
+DATA(insert OID = 0 (  lo_truncate64              PGNSP PGUID 12 f f t f v 2 
23 "23 20" _null_ lo_truncate64 - _null_ ));
+DESCR("large object resize (64bit)");
+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");
+DATA(insert OID = 0   (  lo_stat                  PGNSP PGUID 12 f f t f v 1 
23 "26" _null_  lo_stat - _null_ ));
+DESCR("large object size");
+DATA(insert OID = 0   (  lo_stat64                PGNSP PGUID 12 f f t f v 1 
20 "26" _null_  lo_stat64 - _null_ ));
+DESCR("large object size (64bit)");
+DATA(insert OID = 0   (  lo_length                PGNSP PGUID 12 f f t f v 1 
23 "23" _null_  lo_length - _null_ ));
+DESCR("large object size");
+DATA(insert OID = 0   (  lo_length64              PGNSP PGUID 12 f f t f v 1 
20 "23" _null_  lo_length64 - _null_ ));
+DESCR("large object size (64bit)");
 
 DATA(insert OID = 959 (  on_pl                    PGNSP PGUID 12 f f t f i 2  
16 "600 628" _null_      on_pl - _null_ ));
 DESCR("point on line?");
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-10-03 
11:42:43.000000000 -0700
@@ -32,8 +32,17 @@
 
 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);
 
+extern Datum lo_length(PG_FUNCTION_ARGS);
+extern Datum lo_length64(PG_FUNCTION_ARGS);
+extern Datum lo_stat(PG_FUNCTION_ARGS);
+extern Datum lo_stat64(PG_FUNCTION_ARGS);
+extern Datum lo_truncate(PG_FUNCTION_ARGS);
+extern Datum lo_truncate64(PG_FUNCTION_ARGS);
+
 /*
  * These are not fmgr-callable, but are available to C code.
  * Probably these should have had the underscore-free names,
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-10-03 
11:12:42.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: */
@@ -69,9 +69,12 @@
 extern LargeObjectDesc *inv_open(Oid lobjId, int flags);
 extern void inv_close(LargeObjectDesc *obj_desc);
 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_seek(LargeObjectDesc *obj_desc, int64 offset, int whence);
+extern int64   inv_tell(LargeObjectDesc *obj_desc);
+extern int64   inv_length(LargeObjectDesc *obj_desc);
+extern int64   inv_stat(Oid lobjId);
 extern int     inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes);
 extern int     inv_write(LargeObjectDesc *obj_desc, char *buf, int nbytes);
+extern int     inv_truncate(LargeObjectDesc *obj_desc, int64 offset);
 
 #endif   /* LARGE_OBJECT_H */
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-20 
22:30:48.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-20 
22:30:48.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-20 
22:30:48.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-20 
22:30:48.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-20 
22:30:48.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/interfaces/libpq/libpq.rc 
postgresql-8.0.3/src/interfaces/libpq/libpq.rc
--- postgresql-8.0.3-orig/src/interfaces/libpq/libpq.rc 2005-05-09 
19:16:53.000000000 -0700
+++ postgresql-8.0.3/src/interfaces/libpq/libpq.rc      2005-09-20 
23:31:58.000000000 -0700
@@ -1,8 +1,8 @@
 #include <winver.h>
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 8,0,3,5129
- PRODUCTVERSION 8,0,3,5129
+ FILEVERSION 8,0,3,5263
+ PRODUCTVERSION 8,0,3,5263
  FILEFLAGSMASK 0x3fL
  FILEFLAGS 0
  FILEOS VOS__WINDOWS32
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-20 22:30:48.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-20 
22:30:48.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;
+}
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to