As a committer, I have looked into the patch and it seems it's good to
commit. However I want to make a small enhancement in the
documentation part:

1) lo_open section needs to mention about new 64bit APIs. Also it
   should include description about lo_truncate, but this is not 64bit
   APIs author's fault since it should had been there when lo_truncate
   was added.

2) Add mention that 64bit APIs are only available in PostgreSQL 9.3 or
   later and if the API is requested against older version of servers
   it will fail.

If there's no objection, I would like commit attached patches.
--
Tatsuo Ishii
SRA OSS, Inc. Japan
English: http://www.sraoss.co.jp/index_en.php
Japanese: http://www.sraoss.co.jp

> Hi Anzai-san,
> 
> The latest patch is fair enough for me, so let me hand over its reviewing
> for comitters.
> 
> Thanks,
> 
> 2012/10/1 Nozomi Anzai <an...@sraoss.co.jp>:
>> Here is 64-bit API for large object version 3 patch.
>>
>>> I checked this patch. It looks good, but here are still some points to be
>>> discussed.
>>>
>>> * I have a question. What is the meaning of INT64_IS_BUSTED?
>>>   It seems to me a marker to indicate a platform without 64bit support.
>>>   However, the commit 901be0fad4034c9cf8a3588fd6cf2ece82e4b8ce
>>>   says as follows:
>>>   | Remove all the special-case code for INT64_IS_BUSTED, per decision that
>>>   | we're not going to support that anymore.
>>
>> Removed INT64_IS_BUSTED.
>>
>>
>>> * At inv_seek(), it seems to me it checks offset correctness with wrong way,
>>>   as follows:
>>> |          case SEEK_SET:
>>> |              if (offset < 0)
>>> |                  elog(ERROR, "invalid seek offset: " INT64_FORMAT, 
>>> offset);
>>> |              obj_desc->offset = offset;
>>> |              break;
>>>   It is a right assumption, if large object size would be restricted to 2GB.
>>>   But the largest positive int64 is larger than expected limitation.
>>>   So, it seems to me it should be compared with (INT_MAX * PAGE_SIZE)
>>>   instead.
>>
>> Fixed.
>>
>>
>>> * At inv_write(), it definitely needs a check to prevent data-write upper 
>>> 4TB.
>>>   In case when obj_desc->offset is a bit below 4TB, an additional 1GB write
>>>   will break head of the large object because of "pageno" overflow.
>>
>> Added a such check.
>>
>>
>>> * Please also add checks on inv_read() to prevent LargeObjectDesc->offset
>>>   unexpectedly overflows 4TB boundary.
>>
>> Added a such check.
>>
>>
>>> * At inv_truncate(), variable "off" is re-defined to int64. Is it really 
>>> needed
>>>   change? All its usage is to store the result of "len % LOBLKSIZE".
>>
>> Fixed and back to int32.
>>
>>
>>> Thanks,
>>>
>>> 2012/9/24 Nozomi Anzai <an...@sraoss.co.jp>:
>>> > Here is 64-bit API for large object version 2 patch.
>>> >
>>> >> I checked this patch. It can be applied onto the latest master branch
>>> >> without any problems. My comments are below.
>>> >>
>>> >> 2012/9/11 Tatsuo Ishii <is...@postgresql.org>:
>>> >> > Ok, here is the patch to implement 64-bit API for large object, to
>>> >> > allow to use up to 4TB large objects(or 16TB if BLCKSZ changed to
>>> >> > 32KB). The patch is based on Jeremy Drake's patch posted on September
>>> >> > 23, 2005
>>> >> > (http://archives.postgresql.org/pgsql-hackers/2005-09/msg01026.php)
>>> >> > and reasonably updated/edited to adopt PostgreSQL 9.3 by Nozomi Anzai
>>> >> > for the backend part and Yugo Nagata for the rest(including
>>> >> > documentation patch).
>>> >> >
>>> >> > Here are changes made in the patch:
>>> >> >
>>> >> > 1) Frontend lo_* libpq functions(fe-lobj.c)(Yugo Nagata)
>>> >> >
>>> >> > lo_initialize() gathers backend 64-bit large object handling
>>> >> > function's oid, namely lo_lseek64, lo_tell64, lo_truncate64.
>>> >> >
>>> >> > If client calls lo_*64 functions and backend does not support them,
>>> >> > lo_*64 functions return error to caller. There might be an argument
>>> >> > since calls to lo_*64 functions can automatically be redirected to
>>> >> > 32-bit older API. I don't know this is worth the trouble though.
>>> >> >
>>> >> I think it should definitely return an error code when user tries to
>>> >> use lo_*64 functions towards the backend v9.2 or older, because
>>> >> fallback to 32bit API can raise unexpected errors if application
>>> >> intends to seek the area over than 2GB.
>>> >>
>>> >> > Currently lo_initialize() throws an error if one of oids are not
>>> >> > available. I doubt we do the same way for 64-bit functions since this
>>> >> > will make 9.3 libpq unable to access large objects stored in pre-9.2
>>> >> > PostgreSQL servers.
>>> >> >
>>> >> It seems to me the situation to split the case of pre-9.2 and post-9.3
>>> >> using a condition of "conn->sversion >= 90300".
>>> >
>>> > Fixed so, and tested it by deleteing the lo_tell64's row from pg_proc.
>>> >
>>> >
>>> >> > To pass 64-bit integer to PQfn, PQArgBlock is used like this: int *ptr
>>> >> > is a pointer to 64-bit integer and actual data is placed somewhere
>>> >> > else. There might be other way: add new member to union u to store
>>> >> > 64-bit integer:
>>> >> >
>>> >> > typedef struct
>>> >> > {
>>> >> >         int                     len;
>>> >> >         int                     isint;
>>> >> >         union
>>> >> >         {
>>> >> >                 int                *ptr;                /* can't use 
>>> >> > void (dec compiler barfs)   */
>>> >> >                 int                     integer;
>>> >> >                 int64           bigint;         /* 64-bit integer */
>>> >> >         }                       u;
>>> >> > } PQArgBlock;
>>> >> >
>>> >> > I'm a little bit worried about this way because PQArgBlock is a public
>>> >> > interface.
>>> >> >
>>> >> I'm inclined to add a new field for the union; that seems to me straight
>>> >> forward approach.
>>> >> For example, the manner in lo_seek64() seems to me confusable.
>>> >> It set 1 on "isint" field even though pointer is delivered actually.
>>> >>
>>> >> +       argv[1].isint = 1;
>>> >> +       argv[1].len = 8;
>>> >> +       argv[1].u.ptr = (int *) &len;
>>> >
>>> > Your proposal was not adopted per discussion.
>>> >
>>> >
>>> >> > Also we add new type "pg_int64":
>>> >> >
>>> >> > #ifndef NO_PG_INT64
>>> >> > #define HAVE_PG_INT64 1
>>> >> > typedef long long int pg_int64;
>>> >> > #endif
>>> >> >
>>> >> > in postgres_ext.h per suggestion from Tom Lane:
>>> >> > http://archives.postgresql.org/pgsql-hackers/2005-09/msg01062.php
>>> >> >
>>> >> I'm uncertain about context of this discussion.
>>> >>
>>> >> Does it make matter if we include <stdint.h> and use int64_t instead
>>> >> of the self defined data type?
>>> >
>>> > Your proposal was not adopted per discussion.
>>> > Per discussion, endiannness translation was moved to fe-lobj.c.
>>> >
>>> >
>>> >> > 2) Backend lo_* functions (be-fsstubs.c)(Nozomi Anzai)
>>> >> >
>>> >> > Add lo_lseek64, lo_tell64, lo_truncate64 so that they can handle
>>> >> > 64-bit seek position and data length. loread64 and lowrite64 are not
>>> >> > added because if a program tries to read/write more than 2GB at once,
>>> >> > it would be a sign that the program need to be re-designed anyway.
>>> >> >
>>> >> I think it is a reasonable.
>>> >>
>>> >> > 3) Backend inv_api.c functions(Nozomi Anzai)
>>> >> >
>>> >> > No need to add new functions. Just extend them to handle 64-bit data.
>>> >> >
>>> >> > BTW , what will happen if older 32-bit libpq accesses large objects
>>> >> > over 2GB?
>>> >> >
>>> >> > lo_read and lo_write: they can read or write lobjs using 32-bit API as
>>> >> > long as requested read/write data length is smaller than 2GB. So I
>>> >> > think we can safely allow them to access over 2GB lobjs.
>>> >> >
>>> >> > lo_lseek: again as long as requested offset is smaller than 2GB, there
>>> >> > would be no problem.
>>> >> >
>>> >> > lo_tell:if current seek position is beyond 2GB, returns an error.
>>> >> >
>>> >> Even though iteration of lo_lseek() may move the offset to 4TB, it also
>>> >> makes unavailable to use lo_tell() to obtain the current offset, so I 
>>> >> think
>>> >> it is reasonable behavior.
>>> >>
>>> >> However, error code is not an appropriate one.
>>> >>
>>> >> +       if (INT_MAX < offset)
>>> >> +       {
>>> >> +               ereport(ERROR,
>>> >> +                               (errcode(ERRCODE_UNDEFINED_OBJECT),
>>> >> +                                errmsg("invalid large-object
>>> >> descriptor: %d", fd)));
>>> >> +               PG_RETURN_INT32(-1);
>>> >> +       }
>>> >>
>>> >> According to the manpage of lseek(2)
>>> >>     EOVERFLOW
>>> >>         The resulting file offset cannot be represented in an off_t.
>>> >>
>>> >> Please add a new error code such as ERRCODE_BLOB_OFFSET_OVERFLOW.
>>> >
>>> > Changed the error code and error message. We added a new error code
>>> > "ERRCODE_UNDEFINED_OBJECT (22P07)".
>>> >
>>> >
>>> >> > 4) src/test/examples/testlo64.c added for 64-bit API example(Yugo 
>>> >> > Nagata)
>>> >> >
>>> >> > Comments and suggestions are welcome.
>>> >> >
>>> >> miscellaneous comments are below.
>>> >>
>>> >> Regression test is helpful. Even though no need to try to create 4TB 
>>> >> large
>>> >> object, it is helpful to write some chunks around the design boundary.
>>> >> Could you add some test cases that writes some chunks around 4TB offset.
>>> >
>>> > Added 64-bit lobj test items into regression test and confirmed it worked
>>> > rightly.
>>> >
>>> >
>>> >> Thanks,
>>> >> --
>>> >> KaiGai Kohei <kai...@kaigai.gr.jp>
>>> >>
>>> >>
>>> >> --
>>> >> Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
>>> >> To make changes to your subscription:
>>> >> http://www.postgresql.org/mailpref/pgsql-hackers
>>> >
>>> >
>>> > --
>>> > Nozomi Anzai
>>> > SRA OSS, Inc. Japan
>>> >
>>> >
>>> > --
>>> > Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
>>> > To make changes to your subscription:
>>> > http://www.postgresql.org/mailpref/pgsql-hackers
>>> >
>>>
>>>
>>>
>>> --
>>> KaiGai Kohei <kai...@kaigai.gr.jp>
>>>
>>>
>>> --
>>> Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
>>> To make changes to your subscription:
>>> http://www.postgresql.org/mailpref/pgsql-hackers
>>
>>
>> --
>> Nozomi Anzai
>> SRA OSS, Inc. Japan
>>
>>
>> --
>> Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
>> To make changes to your subscription:
>> http://www.postgresql.org/mailpref/pgsql-hackers
>>
> 
> 
> 
> -- 
> KaiGai Kohei <kai...@kaigai.gr.jp>
> 
> 
> -- 
> Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-hackers
diff --git a/doc/src/sgml/lobj.sgml b/doc/src/sgml/lobj.sgml
new file mode 100644
index 291409f..d34190f
*** a/doc/src/sgml/lobj.sgml
--- b/doc/src/sgml/lobj.sgml
***************
*** 41,47 ****
      larger than a single database page into a secondary storage area per table.
      This makes the large object facility partially obsolete.  One
      remaining advantage of the large object facility is that it allows values
!     up to 2 GB in size, whereas <acronym>TOAST</acronym>ed fields can be at
      most 1 GB.  Also, large objects can be randomly modified using a read/write
      API that is more efficient than performing such operations using
      <acronym>TOAST</acronym>.
--- 41,47 ----
      larger than a single database page into a secondary storage area per table.
      This makes the large object facility partially obsolete.  One
      remaining advantage of the large object facility is that it allows values
!     up to 4 TB in size, whereas <acronym>TOAST</acronym>ed fields can be at
      most 1 GB.  Also, large objects can be randomly modified using a read/write
      API that is more efficient than performing such operations using
      <acronym>TOAST</acronym>.
*************** int lo_open(PGconn *conn, Oid lobjId, in
*** 237,243 ****
       <function>lo_open</function> returns a (non-negative) large object
       descriptor for later use in <function>lo_read</function>,
       <function>lo_write</function>, <function>lo_lseek</function>,
!      <function>lo_tell</function>, and <function>lo_close</function>.
       The descriptor is only valid for
       the duration of the current transaction.
       On failure, -1 is returned.
--- 237,245 ----
       <function>lo_open</function> returns a (non-negative) large object
       descriptor for later use in <function>lo_read</function>,
       <function>lo_write</function>, <function>lo_lseek</function>,
! 	 <function>lo_lseek64</function>, <function>lo_tell</function>,
!      <function>lo_tell64</function>, <function>lo_truncate</function>,
! 	 <function>lo_truncate64</function>, and <function>lo_close</function>.
       The descriptor is only valid for
       the duration of the current transaction.
       On failure, -1 is returned.
*************** int lo_read(PGconn *conn, int fd, char *
*** 312,317 ****
--- 314,320 ----
       large object descriptor, call
  <synopsis>
  int lo_lseek(PGconn *conn, int fd, int offset, int whence);
+ pg_int64 lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence);
  </synopsis>
       <indexterm><primary>lo_lseek</></> This function moves the
       current location pointer for the large object descriptor identified by
*************** int lo_lseek(PGconn *conn, int fd, int o
*** 321,327 ****
--- 324,339 ----
       <symbol>SEEK_CUR</> (seek from current position), and
       <symbol>SEEK_END</> (seek from object end).  The return value is
       the new location pointer, or -1 on error.
+      <indexterm><primary>lo_lseek64</></> <function>lo_lseek64</function>
+      is a function for large objects larger than 2GB. <symbol>pg_int64</>
+      is defined as 8-byte integer type.
+ </para>
+ <para>
+      <function>lo_lseek64</> is new as of <productname>PostgreSQL</productname>
+      9.3; if this function is run against an older server version, it will
+      fail and return a negative value.
  </para>
+ 
  </sect2>
  
  <sect2 id="lo-tell">
*************** int lo_lseek(PGconn *conn, int fd, int o
*** 332,340 ****
--- 344,360 ----
       call
  <synopsis>
  int lo_tell(PGconn *conn, int fd);
+ pg_int64 lo_tell64(PGconn *conn, int fd);
  </synopsis>
       <indexterm><primary>lo_tell</></> If there is an error, the
       return value is negative.
+      <indexterm><primary>lo_tell64</></> <function>lo_tell64</function> is
+      a function for large objects larger than 2GB.
+ </para>
+ <para>
+      <function>lo_tell64</> is new as of <productname>PostgreSQL</productname>
+      9.3; if this function is run against an older server version, it will
+      fail and return a negative value.
  </para>
  </sect2>
  
*************** int lo_tell(PGconn *conn, int fd);
*** 345,350 ****
--- 365,371 ----
       To truncate a large object to a given length, call
  <synopsis>
  int lo_truncate(PGcon *conn, int fd, size_t len);
+ int lo_truncate64(PGcon *conn, int fd, pg_int64 len);
  </synopsis>
       <indexterm><primary>lo_truncate</></> truncates the large object
       descriptor <parameter>fd</> to length <parameter>len</>.  The
*************** int lo_truncate(PGcon *conn, int fd, siz
*** 352,357 ****
--- 373,380 ----
       previous <function>lo_open</function>.  If <parameter>len</> is
       greater than the current large object length, the large object
       is extended with null bytes ('\0').
+      <indexterm><primary>lo_truncate64</></> <function>lo_truncate64</function>
+      is a function for large objects larger than 2GB.
  </para>
  
  <para>
*************** int lo_truncate(PGcon *conn, int fd, siz
*** 359,365 ****
  </para>
  
  <para>
!      On success <function>lo_truncate</function> returns
       zero.  On error, the return value is negative.
  </para>
  
--- 382,388 ----
  </para>
  
  <para>
!      On success <function>lo_truncate</function>, <function>lo_truncate64</function> returns
       zero.  On error, the return value is negative.
  </para>
  
*************** int lo_truncate(PGcon *conn, int fd, siz
*** 368,373 ****
--- 391,401 ----
       8.3; if this function is run against an older server version, it will
       fail and return a negative value.
  </para>
+ <para>
+      <function>lo_truncate64</> is new as of <productname>PostgreSQL</productname>
+      9.3; if this function is run against an older server version, it will
+      fail and return a negative value.
+ </para>
  </sect2>
  
  <sect2 id="lo-close">
diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c
new file mode 100644
index 6f7e474..4bc81ba
*** a/src/backend/libpq/be-fsstubs.c
--- b/src/backend/libpq/be-fsstubs.c
***************
*** 39,44 ****
--- 39,45 ----
  #include "postgres.h"
  
  #include <fcntl.h>
+ #include <limits.h>
  #include <sys/stat.h>
  #include <unistd.h>
  
*************** lo_lseek(PG_FUNCTION_ARGS)
*** 216,222 ****
  	int32		fd = PG_GETARG_INT32(0);
  	int32		offset = PG_GETARG_INT32(1);
  	int32		whence = PG_GETARG_INT32(2);
! 	int			status;
  
  	if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
  		ereport(ERROR,
--- 217,223 ----
  	int32		fd = PG_GETARG_INT32(0);
  	int32		offset = PG_GETARG_INT32(1);
  	int32		whence = PG_GETARG_INT32(2);
! 	int64		status;
  
  	if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
  		ereport(ERROR,
*************** lo_lseek(PG_FUNCTION_ARGS)
*** 225,233 ****
--- 226,270 ----
  
  	status = inv_seek(cookies[fd], offset, whence);
  
+ 	if (INT_MAX < status)
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_BLOB_OFFSET_OVERFLOW),
+ 				 errmsg("offset overflow: %d", fd)));
+ 		PG_RETURN_INT32(-1);
+ 	}
+ 
  	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)
  {
*************** Datum
*** 264,276 ****
  lo_tell(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_INT32(inv_tell(cookies[fd]));
  }
  
  Datum
--- 301,346 ----
  lo_tell(PG_FUNCTION_ARGS)
  {
  	int32		fd = PG_GETARG_INT32(0);
+ 	int64		offset = 0;
  
  	if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
  		ereport(ERROR,
  				(errcode(ERRCODE_UNDEFINED_OBJECT),
  				 errmsg("invalid large-object descriptor: %d", fd)));
  
! 	offset = inv_tell(cookies[fd]);
! 
! 	if (INT_MAX < offset)
! 	{
! 		ereport(ERROR,
! 				(errcode(ERRCODE_BLOB_OFFSET_OVERFLOW),
! 				 errmsg("offset overflow: %d", fd)));
! 		PG_RETURN_INT32(-1);
! 	}
! 
! 	PG_RETURN_INT32(offset);
! }
! 
! 
! 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_truncate(PG_FUNCTION_ARGS)
*** 533,538 ****
--- 603,635 ----
  	PG_RETURN_INT32(0);
  }
  
+ Datum
+ lo_truncate64(PG_FUNCTION_ARGS)
+ {
+ 	int32		fd = PG_GETARG_INT32(0);
+ 	int64		len = PG_GETARG_INT64(1);
+ 
+ 	if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 				 errmsg("invalid large-object descriptor: %d", fd)));
+ 
+ 	/* Permission checks */
+ 	if (!lo_compat_privileges &&
+ 		pg_largeobject_aclcheck_snapshot(cookies[fd]->id,
+ 										 GetUserId(),
+ 										 ACL_UPDATE,
+ 									   cookies[fd]->snapshot) != ACLCHECK_OK)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 errmsg("permission denied for large object %u",
+ 						cookies[fd]->id)));
+ 
+ 	inv_truncate(cookies[fd], len);
+ 
+ 	PG_RETURN_INT32(0);
+ }
+ 
  /*
   * AtEOXact_LargeObject -
   *		 prepares large objects for transaction commit
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
new file mode 100644
index 3adfb15..3f5688b
*** a/src/backend/storage/large_object/inv_api.c
--- b/src/backend/storage/large_object/inv_api.c
*************** inv_drop(Oid lobjId)
*** 324,333 ****
   * 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)
  {
! 	uint32		lastbyte = 0;
  	ScanKeyData skey[1];
  	SysScanDesc sd;
  	HeapTuple	tuple;
--- 324,333 ----
   * NOTE: LOs can contain gaps, just like Unix files.  We actually return
   * the offset of the last byte + 1.
   */
! static uint64
  inv_getsize(LargeObjectDesc *obj_desc)
  {
! 	uint64		lastbyte = 0;
  	ScanKeyData skey[1];
  	SysScanDesc sd;
  	HeapTuple	tuple;
*************** inv_getsize(LargeObjectDesc *obj_desc)
*** 368,374 ****
  				heap_tuple_untoast_attr((struct varlena *) datafield);
  			pfreeit = true;
  		}
! 		lastbyte = data->pageno * LOBLKSIZE + getbytealen(datafield);
  		if (pfreeit)
  			pfree(datafield);
  	}
--- 368,374 ----
  				heap_tuple_untoast_attr((struct varlena *) datafield);
  			pfreeit = true;
  		}
! 		lastbyte = (uint64) data->pageno * LOBLKSIZE + getbytealen(datafield);
  		if (pfreeit)
  			pfree(datafield);
  	}
*************** inv_getsize(LargeObjectDesc *obj_desc)
*** 378,407 ****
  	return lastbyte;
  }
  
! int
! inv_seek(LargeObjectDesc *obj_desc, int offset, int whence)
  {
  	Assert(PointerIsValid(obj_desc));
  
  	switch (whence)
  	{
  		case SEEK_SET:
! 			if (offset < 0)
! 				elog(ERROR, "invalid seek offset: %d", offset);
  			obj_desc->offset = offset;
  			break;
  		case SEEK_CUR:
! 			if (offset < 0 && obj_desc->offset < ((uint32) (-offset)))
! 				elog(ERROR, "invalid seek offset: %d", offset);
  			obj_desc->offset += offset;
  			break;
  		case SEEK_END:
  			{
! 				uint32		size = inv_getsize(obj_desc);
  
! 				if (offset < 0 && size < ((uint32) (-offset)))
! 					elog(ERROR, "invalid seek offset: %d", offset);
! 				obj_desc->offset = size + offset;
  			}
  			break;
  		default:
--- 378,408 ----
  	return lastbyte;
  }
  
! int64
! inv_seek(LargeObjectDesc *obj_desc, int64 offset, int whence)
  {
  	Assert(PointerIsValid(obj_desc));
  
  	switch (whence)
  	{
  		case SEEK_SET:
! 			if (offset < 0 || offset >= MAX_LARGE_OBJECT_SIZE)
! 				elog(ERROR, "invalid seek offset: " INT64_FORMAT, offset);
  			obj_desc->offset = offset;
  			break;
  		case SEEK_CUR:
! 			if ((offset + obj_desc->offset) < 0 ||
! 			   (offset + obj_desc->offset) >= MAX_LARGE_OBJECT_SIZE)
! 				elog(ERROR, "invalid seek offset: " INT64_FORMAT, offset);
  			obj_desc->offset += offset;
  			break;
  		case SEEK_END:
  			{
! 				int64		pos = inv_getsize(obj_desc) + offset;
  
! 				if (pos < 0 || pos >= MAX_LARGE_OBJECT_SIZE)
! 					elog(ERROR, "invalid seek offset: " INT64_FORMAT, offset);
! 				obj_desc->offset = pos;
  			}
  			break;
  		default:
*************** inv_seek(LargeObjectDesc *obj_desc, int 
*** 410,416 ****
  	return obj_desc->offset;
  }
  
! int
  inv_tell(LargeObjectDesc *obj_desc)
  {
  	Assert(PointerIsValid(obj_desc));
--- 411,417 ----
  	return obj_desc->offset;
  }
  
! int64
  inv_tell(LargeObjectDesc *obj_desc)
  {
  	Assert(PointerIsValid(obj_desc));
*************** int
*** 422,432 ****
  inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
  {
  	int			nread = 0;
! 	int			n;
! 	int			off;
  	int			len;
  	int32		pageno = (int32) (obj_desc->offset / LOBLKSIZE);
! 	uint32		pageoff;
  	ScanKeyData skey[2];
  	SysScanDesc sd;
  	HeapTuple	tuple;
--- 423,433 ----
  inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
  {
  	int			nread = 0;
! 	int64		n;
! 	int64		off;
  	int			len;
  	int32		pageno = (int32) (obj_desc->offset / LOBLKSIZE);
! 	uint64		pageoff;
  	ScanKeyData skey[2];
  	SysScanDesc sd;
  	HeapTuple	tuple;
*************** inv_read(LargeObjectDesc *obj_desc, char
*** 437,442 ****
--- 438,446 ----
  	if (nbytes <= 0)
  		return 0;
  
+ 	if ((nbytes + obj_desc->offset) > MAX_LARGE_OBJECT_SIZE)
+ 		elog(ERROR, "invalid read request size: %d", nbytes);
+ 
  	open_lo_relation();
  
  	ScanKeyInit(&skey[0],
*************** inv_read(LargeObjectDesc *obj_desc, char
*** 467,473 ****
  		 * 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;
  		if (pageoff > obj_desc->offset)
  		{
  			n = pageoff - obj_desc->offset;
--- 471,477 ----
  		 * there may be missing pages if the LO contains unwritten "holes". We
  		 * want missing sections to read out as zeroes.
  		 */
! 		pageoff = ((uint64) data->pageno) * LOBLKSIZE;
  		if (pageoff > obj_desc->offset)
  		{
  			n = pageoff - obj_desc->offset;
*************** inv_write(LargeObjectDesc *obj_desc, con
*** 560,565 ****
--- 564,572 ----
  	if (nbytes <= 0)
  		return 0;
  
+ 	if ((nbytes + obj_desc->offset) > MAX_LARGE_OBJECT_SIZE)
+ 		elog(ERROR, "invalid write request size: %d", nbytes);
+ 
  	open_lo_relation();
  
  	indstate = CatalogOpenIndexes(lo_heap_r);
*************** inv_write(LargeObjectDesc *obj_desc, con
*** 718,727 ****
  }
  
  void
! inv_truncate(LargeObjectDesc *obj_desc, int len)
  {
  	int32		pageno = (int32) (len / LOBLKSIZE);
! 	int			off;
  	ScanKeyData skey[2];
  	SysScanDesc sd;
  	HeapTuple	oldtuple;
--- 725,734 ----
  }
  
  void
! inv_truncate(LargeObjectDesc *obj_desc, int64 len)
  {
  	int32		pageno = (int32) (len / LOBLKSIZE);
! 	int32		off;
  	ScanKeyData skey[2];
  	SysScanDesc sd;
  	HeapTuple	oldtuple;
diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt
new file mode 100644
index 3e04164..db8ab53
*** a/src/backend/utils/errcodes.txt
--- b/src/backend/utils/errcodes.txt
*************** Section: Class 22 - Data Exception
*** 199,204 ****
--- 199,205 ----
  2200N    E    ERRCODE_INVALID_XML_CONTENT                                    invalid_xml_content
  2200S    E    ERRCODE_INVALID_XML_COMMENT                                    invalid_xml_comment
  2200T    E    ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION                     invalid_xml_processing_instruction
+ 22P07    E    ERRCODE_BLOB_OFFSET_OVERFLOW                                   blob_offset_overflow
  
  Section: Class 23 - Integrity Constraint Violation
  
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
new file mode 100644
index 77a3b41..a2da836
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DATA(insert OID = 955 (  lowrite		   PGN
*** 1040,1053 ****
--- 1040,1059 ----
  DESCR("large object write");
  DATA(insert OID = 956 (  lo_lseek		   PGNSP PGUID 12 1 0 0 0 f f f f t f v 3 0 23 "23 23 23" _null_ _null_ _null_ _null_	lo_lseek _null_ _null_ _null_ ));
  DESCR("large object seek");
+ DATA(insert OID = 3170 (  lo_lseek64		   PGNSP PGUID 12 1 0 0 0 f f f f t f v 3 0 20 "23 20 23" _null_ _null_ _null_ _null_	lo_lseek64 _null_ _null_ _null_ ));
+ DESCR("large object seek (64 bit)");
  DATA(insert OID = 957 (  lo_creat		   PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 26 "23" _null_ _null_ _null_ _null_ lo_creat _null_ _null_ _null_ ));
  DESCR("large object create");
  DATA(insert OID = 715 (  lo_create		   PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 26 "26" _null_ _null_ _null_ _null_ lo_create _null_ _null_ _null_ ));
  DESCR("large object create");
  DATA(insert OID = 958 (  lo_tell		   PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 23 "23" _null_ _null_ _null_ _null_ lo_tell _null_ _null_ _null_ ));
  DESCR("large object position");
+ DATA(insert OID = 3171 (  lo_tell64		   PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 20 "23" _null_ _null_ _null_ _null_ lo_tell64 _null_ _null_ _null_ ));
+ DESCR("large object position (64 bit)");
  DATA(insert OID = 1004 (  lo_truncate	   PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 23 "23 23" _null_ _null_ _null_ _null_ lo_truncate _null_ _null_ _null_ ));
  DESCR("truncate large object");
+ DATA(insert OID = 3172 (  lo_truncate64	   PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 23 "23 20" _null_ _null_ _null_ _null_ lo_truncate64 _null_ _null_ _null_ ));
+ DESCR("truncate large object (64 bit)");
  
  DATA(insert OID = 959 (  on_pl			   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "600 628" _null_ _null_ _null_ _null_	on_pl _null_ _null_ _null_ ));
  DATA(insert OID = 960 (  on_sl			   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "601 628" _null_ _null_ _null_ _null_	on_sl _null_ _null_ _null_ ));
diff --git a/src/include/libpq/be-fsstubs.h b/src/include/libpq/be-fsstubs.h
new file mode 100644
index 0c832da..d74ea0e
*** a/src/include/libpq/be-fsstubs.h
--- b/src/include/libpq/be-fsstubs.h
*************** extern Datum lowrite(PG_FUNCTION_ARGS);
*** 34,41 ****
--- 34,44 ----
  
  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_truncate(PG_FUNCTION_ARGS);
+ extern Datum lo_truncate64(PG_FUNCTION_ARGS);
  
  /*
   * compatibility option for access control
diff --git a/src/include/postgres_ext.h b/src/include/postgres_ext.h
new file mode 100644
index b6ebb7a..76502de
*** a/src/include/postgres_ext.h
--- b/src/include/postgres_ext.h
*************** typedef unsigned int Oid;
*** 56,59 ****
--- 56,64 ----
  #define PG_DIAG_SOURCE_LINE		'L'
  #define PG_DIAG_SOURCE_FUNCTION 'R'
  
+ #ifndef NO_PG_INT64
+ #define HAVE_PG_INT64 1
+ typedef long long int pg_int64;
+ #endif
+ 
  #endif
diff --git a/src/include/storage/large_object.h b/src/include/storage/large_object.h
new file mode 100644
index 1fe07ee..52f01c6
*** a/src/include/storage/large_object.h
--- b/src/include/storage/large_object.h
*************** typedef struct LargeObjectDesc
*** 37,43 ****
  	Oid			id;				/* LO's identifier */
  	Snapshot	snapshot;		/* snapshot to use */
  	SubTransactionId subid;		/* owning subtransaction ID */
! 	uint32		offset;			/* current seek pointer */
  	int			flags;			/* locking info, etc */
  
  /* flag bits: */
--- 37,43 ----
  	Oid			id;				/* LO's identifier */
  	Snapshot	snapshot;		/* snapshot to use */
  	SubTransactionId subid;		/* owning subtransaction ID */
! 	uint64		offset;			/* current seek pointer */
  	int			flags;			/* locking info, etc */
  
  /* flag bits: */
*************** typedef struct LargeObjectDesc
*** 62,68 ****
   * This avoids unnecessary tuple updates caused by partial-page writes.
   */
  #define LOBLKSIZE		(BLCKSZ / 4)
! 
  
  /*
   * Function definitions...
--- 62,71 ----
   * This avoids unnecessary tuple updates caused by partial-page writes.
   */
  #define LOBLKSIZE		(BLCKSZ / 4)
! /*
!  * Maximum byte length for each large object
! */
! #define MAX_LARGE_OBJECT_SIZE	INT64CONST(INT_MAX * LOBLKSIZE)
  
  /*
   * Function definitions...
*************** extern Oid	inv_create(Oid lobjId);
*** 74,83 ****
  extern LargeObjectDesc *inv_open(Oid lobjId, int flags, MemoryContext mcxt);
  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 int	inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes);
  extern int	inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes);
! extern void inv_truncate(LargeObjectDesc *obj_desc, int len);
  
  #endif   /* LARGE_OBJECT_H */
--- 77,86 ----
  extern LargeObjectDesc *inv_open(Oid lobjId, int flags, MemoryContext mcxt);
  extern void inv_close(LargeObjectDesc *obj_desc);
  extern int	inv_drop(Oid lobjId);
! extern int64	inv_seek(LargeObjectDesc *obj_desc, int64 offset, int whence);
! extern int64	inv_tell(LargeObjectDesc *obj_desc);
  extern int	inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes);
  extern int	inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes);
! extern void inv_truncate(LargeObjectDesc *obj_desc, int64 len);
  
  #endif   /* LARGE_OBJECT_H */
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
new file mode 100644
index 9d95e26..56d0bb8
*** a/src/interfaces/libpq/exports.txt
--- b/src/interfaces/libpq/exports.txt
*************** PQping                    158
*** 161,163 ****
--- 161,166 ----
  PQpingParams              159
  PQlibVersion              160
  PQsetSingleRowMode        161
+ lo_lseek64                162
+ lo_tell64                 163
+ lo_truncate64             164
diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c
new file mode 100644
index f3a6d03..fb17ac8
*** a/src/interfaces/libpq/fe-lobj.c
--- b/src/interfaces/libpq/fe-lobj.c
***************
*** 37,46 ****
--- 37,52 ----
  #include "libpq-int.h"
  #include "libpq/libpq-fs.h"		/* must come after sys/stat.h */
  
+ /* for ntohl/htonl */
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ 
  #define LO_BUFSIZE		  8192
  
  static int	lo_initialize(PGconn *conn);
  static Oid	lo_import_internal(PGconn *conn, const char *filename, Oid oid);
+ static pg_int64	lo_hton64(pg_int64 host64);
+ static pg_int64	lo_ntoh64(pg_int64 net64);
  
  /*
   * lo_open
*************** lo_truncate(PGconn *conn, int fd, size_t
*** 174,179 ****
--- 180,238 ----
  	}
  }
  
+ /*
+  * lo_truncate64
+  *	  truncates an existing large object to the given size
+  *
+  * returns 0 upon success
+  * returns -1 upon failure
+  */
+ #ifdef HAVE_PG_INT64
+ int
+ lo_truncate64(PGconn *conn, int fd, pg_int64 len)
+ {
+ 	PQArgBlock	argv[2];
+ 	PGresult   *res;
+ 	int			retval;
+ 	int			result_len;
+ 
+ 	if (conn == NULL || conn->lobjfuncs == NULL)
+ 	{
+ 		if (lo_initialize(conn) < 0)
+ 			return -1;
+ 	}
+ 
+ 	if (conn->lobjfuncs->fn_lo_truncate64 == 0)
+ 	{
+ 		printfPQExpBuffer(&conn->errorMessage,
+ 			libpq_gettext("cannot determine OID of function lo_truncate64\n"));
+ 		return -1;
+ 	}
+ 
+ 	argv[0].isint = 1;
+ 	argv[0].len = 4;
+ 	argv[0].u.integer = fd;
+ 
+ 	len = lo_hton64(len);
+ 	argv[1].isint = 0;
+ 	argv[1].len = 8;
+ 	argv[1].u.ptr = (int *) &len;
+ 
+ 	res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate64,
+ 			   &retval, &result_len, 1, argv, 2);
+ 
+ 	if (PQresultStatus(res) == PGRES_COMMAND_OK)
+ 	{
+ 		PQclear(res);
+ 		return retval;
+ 	}
+ 	else
+ 	{
+ 		PQclear(res);
+ 		return -1;
+ 	}
+ }
+ #endif
  
  /*
   * lo_read
*************** lo_lseek(PGconn *conn, int fd, int offse
*** 311,316 ****
--- 370,432 ----
  }
  
  /*
+  * lo_lseek64
+  *	  change the current read or write location on a large object
+  * currently, only L_SET is a legal value for whence
+  *
+  */
+ 
+ #ifdef HAVE_PG_INT64
+ pg_int64
+ lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence)
+ {
+ 	PQArgBlock	argv[3];
+ 	PGresult   *res;
+ 	pg_int64		retval;
+ 	int			result_len;
+ 
+ 	if (conn == NULL || 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;
+ 
+ 	offset = lo_hton64(offset);
+ 	argv[1].isint = 0;
+ 	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, 0, argv, 3);
+ 	if (PQresultStatus(res) == PGRES_COMMAND_OK)
+ 	{
+ 		PQclear(res);
+ 		return lo_ntoh64((pg_int64)retval);
+ 	}
+ 	else
+ 	{
+ 		PQclear(res);
+ 		return -1;
+ 	}
+ }
+ #endif
+ 
+ /*
   * lo_creat
   *	  create a new large object
   * the mode is ignored (once upon a time it had a use)
*************** lo_tell(PGconn *conn, int fd)
*** 436,441 ****
--- 552,603 ----
  }
  
  /*
+  * lo_tell64
+  *	  returns the current seek location of the large object
+  *
+  */
+ #ifdef HAVE_PG_INT64
+ pg_int64
+ lo_tell64(PGconn *conn, int fd)
+ {
+ 	pg_int64	retval;
+ 	PQArgBlock	argv[1];
+ 	PGresult   *res;
+ 	int			result_len;
+ 
+ 	if (conn == NULL || 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, 0, argv, 1);
+ 	if (PQresultStatus(res) == PGRES_COMMAND_OK)
+ 	{
+ 		PQclear(res);
+ 		return lo_ntoh64((pg_int64) retval);
+ 	}
+ 	else
+ 	{
+ 		PQclear(res);
+ 		return -1;
+ 	}
+ }
+ #endif
+ 
+ /*
   * lo_unlink
   *	  delete a file
   *
*************** lo_initialize(PGconn *conn)
*** 713,720 ****
--- 875,885 ----
  			"'lo_create', "
  			"'lo_unlink', "
  			"'lo_lseek', "
+ 			"'lo_lseek64', "
  			"'lo_tell', "
+ 			"'lo_tell64', "
  			"'lo_truncate', "
+ 			"'lo_truncate64', "
  			"'loread', "
  			"'lowrite') "
  			"and pronamespace = (select oid from pg_catalog.pg_namespace "
*************** lo_initialize(PGconn *conn)
*** 765,774 ****
--- 930,945 ----
  			lobjfuncs->fn_lo_unlink = foid;
  		else if (strcmp(fname, "lo_lseek") == 0)
  			lobjfuncs->fn_lo_lseek = foid;
+ 		else if (strcmp(fname, "lo_lseek64") == 0)
+ 			lobjfuncs->fn_lo_lseek64 = foid;
  		else if (strcmp(fname, "lo_tell") == 0)
  			lobjfuncs->fn_lo_tell = foid;
+ 		else if (strcmp(fname, "lo_tell64") == 0)
+ 			lobjfuncs->fn_lo_tell64 = foid;
  		else if (strcmp(fname, "lo_truncate") == 0)
  			lobjfuncs->fn_lo_truncate = foid;
+ 		else if (strcmp(fname, "lo_truncate64") == 0)
+ 			lobjfuncs->fn_lo_truncate64 = foid;
  		else if (strcmp(fname, "loread") == 0)
  			lobjfuncs->fn_lo_read = foid;
  		else if (strcmp(fname, "lowrite") == 0)
*************** lo_initialize(PGconn *conn)
*** 836,845 ****
  		free(lobjfuncs);
  		return -1;
  	}
! 
  	/*
  	 * Put the structure into the connection control
  	 */
  	conn->lobjfuncs = lobjfuncs;
  	return 0;
  }
--- 1007,1082 ----
  		free(lobjfuncs);
  		return -1;
  	}
! 	if (conn->sversion >= 90300)
! 	{
! 		if (lobjfuncs->fn_lo_lseek64 == 0)
! 		{
! 			printfPQExpBuffer(&conn->errorMessage,
! 					libpq_gettext("cannot determine OID of function lo_lseek64\n"));
! 			free(lobjfuncs);
! 			return -1;
! 		}
! 		if (lobjfuncs->fn_lo_tell64 == 0)
! 		{
! 			printfPQExpBuffer(&conn->errorMessage,
! 					libpq_gettext("cannot determine OID of function lo_tell64\n"));
! 			free(lobjfuncs);
! 			return -1;
! 		}
! 		if (lobjfuncs->fn_lo_truncate64 == 0)
! 		{
! 			printfPQExpBuffer(&conn->errorMessage,
! 					libpq_gettext("cannot determine OID of function lo_truncate64\n"));
! 			free(lobjfuncs);
! 			return -1;
! 		}
! 	}
  	/*
  	 * Put the structure into the connection control
  	 */
  	conn->lobjfuncs = lobjfuncs;
  	return 0;
  }
+ 
+ /*
+  * lo_hton64
+  *	  converts an 64-bit integer from host byte order to network byte order
+  */
+ static pg_int64
+ lo_hton64(pg_int64 host64)
+ {
+ 	pg_int64 	result;
+ 	uint32_t	h32, l32;
+ 
+ 	/* High order half first, since we're doing MSB-first */
+ 	h32 = (uint32_t) (host64 >> 32);
+ 
+ 	/* Now the low order half */
+ 	l32 = (uint32_t) (host64 & 0xffffffff);
+ 
+ 	result = htonl(l32);
+ 	result <<= 32;
+ 	result |= htonl(h32);
+ 
+ 	return result;
+ }
+ 
+ /*
+  * lo_ntoh64
+  *	  converts an 64-bit integer from network byte order to host byte order
+  */
+ static pg_int64
+ lo_ntoh64(pg_int64 net64)
+ {
+ 	pg_int64 	result;
+ 	uint32_t	h32, l32;
+ 
+ 	l32 = (uint32_t) (net64 >> 32);
+ 	h32 = (uint32_t) (net64 & 0xffffffff);
+ 
+ 	result = ntohl(h32);
+ 	result <<= 32;
+ 	result |= ntohl(l32);
+ 
+ 	return result;
+ }
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
new file mode 100644
index 9d05dd2..73568ca
*** a/src/interfaces/libpq/libpq-fe.h
--- b/src/interfaces/libpq/libpq-fe.h
*************** extern Oid	lo_import(PGconn *conn, const
*** 548,553 ****
--- 548,559 ----
  extern Oid	lo_import_with_oid(PGconn *conn, const char *filename, Oid lobjId);
  extern int	lo_export(PGconn *conn, Oid lobjId, const char *filename);
  
+ #ifdef HAVE_PG_INT64
+ extern pg_int64	lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence);
+ extern pg_int64	lo_tell64(PGconn *conn, int fd);
+ extern int	lo_truncate64(PGconn *conn, int fd, pg_int64 len);
+ #endif
+ 
  /* === in fe-misc.c === */
  
  /* Get the version of the libpq library in use */
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
new file mode 100644
index 4a6c8fe..375821e
*** a/src/interfaces/libpq/libpq-int.h
--- b/src/interfaces/libpq/libpq-int.h
*************** typedef struct pgLobjfuncs
*** 271,278 ****
--- 271,281 ----
  	Oid			fn_lo_create;	/* OID of backend function lo_create	*/
  	Oid			fn_lo_unlink;	/* OID of backend function lo_unlink	*/
  	Oid			fn_lo_lseek;	/* OID of backend function lo_lseek		*/
+ 	Oid			fn_lo_lseek64;	/* OID of backend function lo_lseek64		*/
  	Oid			fn_lo_tell;		/* OID of backend function lo_tell		*/
+ 	Oid			fn_lo_tell64;		/* OID of backend function lo_tell64		*/
  	Oid			fn_lo_truncate; /* OID of backend function lo_truncate	*/
+ 	Oid			fn_lo_truncate64; /* OID of backend function lo_truncate64	*/
  	Oid			fn_lo_read;		/* OID of backend function LOread		*/
  	Oid			fn_lo_write;	/* OID of backend function LOwrite		*/
  } PGlobjfuncs;
diff --git a/src/test/examples/Makefile b/src/test/examples/Makefile
new file mode 100644
index bbc6ee1..aee5c04
*** a/src/test/examples/Makefile
--- b/src/test/examples/Makefile
*************** override CPPFLAGS := -I$(libpq_srcdir) $
*** 14,20 ****
  override LDLIBS := $(libpq_pgport) $(LDLIBS)
  
  
! PROGS = testlibpq testlibpq2 testlibpq3 testlibpq4 testlo
  
  all: $(PROGS)
  
--- 14,20 ----
  override LDLIBS := $(libpq_pgport) $(LDLIBS)
  
  
! PROGS = testlibpq testlibpq2 testlibpq3 testlibpq4 testlo testlo64
  
  all: $(PROGS)
  
diff --git a/src/test/regress/input/largeobject.source b/src/test/regress/input/largeobject.source
new file mode 100644
index 40f40f8..4984d78
*** a/src/test/regress/input/largeobject.source
--- b/src/test/regress/input/largeobject.source
*************** SELECT lo_tell(fd) FROM lotest_stash_val
*** 125,130 ****
--- 125,153 ----
  SELECT lo_close(fd) FROM lotest_stash_values;
  END;
  
+ -- Test 64-bit largelbject functions.
+ BEGIN;
+ UPDATE lotest_stash_values SET fd = lo_open(loid, CAST(x'20000' | x'40000' AS integer));
+ 
+ SELECT lo_lseek64(fd, 4294967296, 0) FROM lotest_stash_values;
+ SELECT lowrite(fd, 'offset:4GB') FROM lotest_stash_values;
+ SELECT lo_tell64(fd) FROM lotest_stash_values;
+ 
+ SELECT lo_lseek64(fd, -10, 1) FROM lotest_stash_values;
+ SELECT lo_tell64(fd) FROM lotest_stash_values;
+ SELECT loread(fd, 10) FROM lotest_stash_values;
+ 
+ SELECT lo_truncate64(fd, 5000000000) FROM lotest_stash_values;
+ SELECT lo_lseek64(fd, 0, 2) FROM lotest_stash_values;
+ SELECT lo_tell64(fd) FROM lotest_stash_values;
+ 
+ SELECT lo_truncate64(fd, 3000000000) FROM lotest_stash_values;
+ SELECT lo_lseek64(fd, 0, 2) FROM lotest_stash_values;
+ SELECT lo_tell64(fd) FROM lotest_stash_values;
+ 
+ SELECT lo_close(fd) FROM lotest_stash_values;
+ END;
+ 
  -- lo_unlink(lobjId oid) returns integer
  -- return value appears to always be 1
  SELECT lo_unlink(loid) from lotest_stash_values;
diff --git a/src/test/regress/output/largeobject.source b/src/test/regress/output/largeobject.source
new file mode 100644
index 55aaf8f..74c4772
*** a/src/test/regress/output/largeobject.source
--- b/src/test/regress/output/largeobject.source
*************** SELECT lo_close(fd) FROM lotest_stash_va
*** 210,215 ****
--- 210,297 ----
  (1 row)
  
  END;
+ -- Test 64-bit largelbject functions.
+ BEGIN;
+ UPDATE lotest_stash_values SET fd = lo_open(loid, CAST(x'20000' | x'40000' AS integer));
+ SELECT lo_lseek64(fd, 4294967296, 0) FROM lotest_stash_values;
+  lo_lseek64 
+ ------------
+  4294967296
+ (1 row)
+ 
+ SELECT lowrite(fd, 'offset:4GB') FROM lotest_stash_values;
+  lowrite 
+ ---------
+       10
+ (1 row)
+ 
+ SELECT lo_tell64(fd) FROM lotest_stash_values;
+  lo_tell64  
+ ------------
+  4294967306
+ (1 row)
+ 
+ SELECT lo_lseek64(fd, -10, 1) FROM lotest_stash_values;
+  lo_lseek64 
+ ------------
+  4294967296
+ (1 row)
+ 
+ SELECT lo_tell64(fd) FROM lotest_stash_values;
+  lo_tell64  
+ ------------
+  4294967296
+ (1 row)
+ 
+ SELECT loread(fd, 10) FROM lotest_stash_values;
+    loread   
+ ------------
+  offset:4GB
+ (1 row)
+ 
+ SELECT lo_truncate64(fd, 5000000000) FROM lotest_stash_values;
+  lo_truncate64 
+ ---------------
+              0
+ (1 row)
+ 
+ SELECT lo_lseek64(fd, 0, 2) FROM lotest_stash_values;
+  lo_lseek64 
+ ------------
+  5000000000
+ (1 row)
+ 
+ SELECT lo_tell64(fd) FROM lotest_stash_values;
+  lo_tell64  
+ ------------
+  5000000000
+ (1 row)
+ 
+ SELECT lo_truncate64(fd, 3000000000) FROM lotest_stash_values;
+  lo_truncate64 
+ ---------------
+              0
+ (1 row)
+ 
+ SELECT lo_lseek64(fd, 0, 2) FROM lotest_stash_values;
+  lo_lseek64 
+ ------------
+  3000000000
+ (1 row)
+ 
+ SELECT lo_tell64(fd) FROM lotest_stash_values;
+  lo_tell64  
+ ------------
+  3000000000
+ (1 row)
+ 
+ SELECT lo_close(fd) FROM lotest_stash_values;
+  lo_close 
+ ----------
+         0
+ (1 row)
+ 
+ END;
  -- lo_unlink(lobjId oid) returns integer
  -- return value appears to always be 1
  SELECT lo_unlink(loid) from lotest_stash_values;
-- 
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