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;
