diff --git a/doc/src/sgml/lobj.sgml b/doc/src/sgml/lobj.sgml
index 291409fde0b4bc3dd393701faeb165912f095a23..66467e00f376f274c1845d7553aa6794ecd85128 100644
--- a/doc/src/sgml/lobj.sgml
+++ b/doc/src/sgml/lobj.sgml
@@ -41,7 +41,7 @@
     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
+    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>.
@@ -237,7 +237,9 @@ int lo_open(PGconn *conn, Oid lobjId, int mode);
      <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>.
+	 <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.
@@ -312,6 +314,7 @@ int lo_read(PGconn *conn, int fd, char *buf, size_t len);
      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
@@ -321,7 +324,16 @@ int lo_lseek(PGconn *conn, int fd, int offset, int whence);
      <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">
@@ -332,9 +344,17 @@ int lo_lseek(PGconn *conn, int fd, int offset, int whence);
      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>
 
@@ -345,6 +365,7 @@ int lo_tell(PGconn *conn, int fd);
      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
@@ -352,6 +373,8 @@ int lo_truncate(PGcon *conn, int fd, size_t len);
      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>
@@ -359,7 +382,7 @@ int lo_truncate(PGcon *conn, int fd, size_t len);
 </para>
 
 <para>
-     On success <function>lo_truncate</function> returns
+     On success <function>lo_truncate</function> and <function>lo_truncate64</function> returns
      zero.  On error, the return value is negative.
 </para>
 
@@ -368,6 +391,11 @@ int lo_truncate(PGcon *conn, int fd, size_t len);
      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
index 6f7e474f675485766c7ffca7312d0f1e0adbb83d..4bc81ba9f4d11230c8ea8c003d0b61ec16c8e813 100644
--- a/src/backend/libpq/be-fsstubs.c
+++ b/src/backend/libpq/be-fsstubs.c
@@ -39,6 +39,7 @@
 #include "postgres.h"
 
 #include <fcntl.h>
+#include <limits.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
@@ -216,7 +217,7 @@ lo_lseek(PG_FUNCTION_ARGS)
 	int32		fd = PG_GETARG_INT32(0);
 	int32		offset = PG_GETARG_INT32(1);
 	int32		whence = PG_GETARG_INT32(2);
-	int			status;
+	int64		status;
 
 	if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
 		ereport(ERROR,
@@ -225,9 +226,45 @@ lo_lseek(PG_FUNCTION_ARGS)
 
 	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)
 {
@@ -262,15 +299,48 @@ lo_create(PG_FUNCTION_ARGS)
 
 Datum
 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);
+	}
 
-	PG_RETURN_INT32(inv_tell(cookies[fd]));
+	/*
+	 * 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
@@ -533,6 +603,33 @@ lo_truncate(PG_FUNCTION_ARGS)
 	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
index 3adfb159b8baa49939951da0cecf789575652dbe..3f5688b939b0c09b629b3419f531f1b0dd23d6fb 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -324,10 +324,10 @@ inv_drop(Oid lobjId)
  * NOTE: LOs can contain gaps, just like Unix files.  We actually return
  * the offset of the last byte + 1.
  */
-static uint32
+static uint64
 inv_getsize(LargeObjectDesc *obj_desc)
 {
-	uint32		lastbyte = 0;
+	uint64		lastbyte = 0;
 	ScanKeyData skey[1];
 	SysScanDesc sd;
 	HeapTuple	tuple;
@@ -368,7 +368,7 @@ inv_getsize(LargeObjectDesc *obj_desc)
 				heap_tuple_untoast_attr((struct varlena *) datafield);
 			pfreeit = true;
 		}
-		lastbyte = data->pageno * LOBLKSIZE + getbytealen(datafield);
+		lastbyte = (uint64) data->pageno * LOBLKSIZE + getbytealen(datafield);
 		if (pfreeit)
 			pfree(datafield);
 	}
@@ -378,30 +378,31 @@ inv_getsize(LargeObjectDesc *obj_desc)
 	return lastbyte;
 }
 
-int
-inv_seek(LargeObjectDesc *obj_desc, int offset, int whence)
+int64
+inv_seek(LargeObjectDesc *obj_desc, int64 offset, int whence)
 {
 	Assert(PointerIsValid(obj_desc));
 
 	switch (whence)
 	{
 		case SEEK_SET:
-			if (offset < 0)
-				elog(ERROR, "invalid seek offset: %d", offset);
+			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 < 0 && obj_desc->offset < ((uint32) (-offset)))
-				elog(ERROR, "invalid seek offset: %d", offset);
+			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:
 			{
-				uint32		size = inv_getsize(obj_desc);
+				int64		pos = inv_getsize(obj_desc) + offset;
 
-				if (offset < 0 && size < ((uint32) (-offset)))
-					elog(ERROR, "invalid seek offset: %d", offset);
-				obj_desc->offset = size + offset;
+				if (pos < 0 || pos >= MAX_LARGE_OBJECT_SIZE)
+					elog(ERROR, "invalid seek offset: " INT64_FORMAT, offset);
+				obj_desc->offset = pos;
 			}
 			break;
 		default:
@@ -410,7 +411,7 @@ inv_seek(LargeObjectDesc *obj_desc, int offset, int whence)
 	return obj_desc->offset;
 }
 
-int
+int64
 inv_tell(LargeObjectDesc *obj_desc)
 {
 	Assert(PointerIsValid(obj_desc));
@@ -422,11 +423,11 @@ int
 inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
 {
 	int			nread = 0;
-	int			n;
-	int			off;
+	int64		n;
+	int64		off;
 	int			len;
 	int32		pageno = (int32) (obj_desc->offset / LOBLKSIZE);
-	uint32		pageoff;
+	uint64		pageoff;
 	ScanKeyData skey[2];
 	SysScanDesc sd;
 	HeapTuple	tuple;
@@ -437,6 +438,9 @@ inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
 	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],
@@ -467,7 +471,7 @@ inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
 		 * there may be missing pages if the LO contains unwritten "holes". We
 		 * want missing sections to read out as zeroes.
 		 */
-		pageoff = ((uint32) data->pageno) * LOBLKSIZE;
+		pageoff = ((uint64) data->pageno) * LOBLKSIZE;
 		if (pageoff > obj_desc->offset)
 		{
 			n = pageoff - obj_desc->offset;
@@ -560,6 +564,9 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 	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);
@@ -718,10 +725,10 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 }
 
 void
-inv_truncate(LargeObjectDesc *obj_desc, int len)
+inv_truncate(LargeObjectDesc *obj_desc, int64 len)
 {
 	int32		pageno = (int32) (len / LOBLKSIZE);
-	int			off;
+	int32		off;
 	ScanKeyData skey[2];
 	SysScanDesc sd;
 	HeapTuple	oldtuple;
diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt
index 3e04164956303f6fcdef84a15c617e3075956b05..db8ab53af26b7536ea45c8151ea6defe832c25b3 100644
--- a/src/backend/utils/errcodes.txt
+++ b/src/backend/utils/errcodes.txt
@@ -199,6 +199,7 @@ Section: Class 22 - Data Exception
 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
index 77a3b413ce525c5c5854f1ce60964c0c6812552c..a2da836ff2cdd02a497571b875ae6d6e66481d44 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1040,14 +1040,20 @@ DATA(insert OID = 955 (  lowrite		   PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 23
 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
index 0c832da6e43e31cf33057a1f99872a6349b4d5c8..d74ea0eee258b9463174d90e9b17472887f6fa35 100644
--- a/src/include/libpq/be-fsstubs.h
+++ b/src/include/libpq/be-fsstubs.h
@@ -34,8 +34,11 @@ extern Datum lowrite(PG_FUNCTION_ARGS);
 
 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
index b6ebb7aac3f0d17ab1629e02d96e89c8f547e047..76502de647b4e94c399904e8f3cd35ec8c522f19 100644
--- a/src/include/postgres_ext.h
+++ b/src/include/postgres_ext.h
@@ -56,4 +56,9 @@ typedef unsigned int Oid;
 #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
index 1fe07ee43ac6c32feb0440965f954c177b36ff1c..52f01c6e3c393311dfb852bdd6ff7b944bc87155 100644
--- a/src/include/storage/large_object.h
+++ b/src/include/storage/large_object.h
@@ -37,7 +37,7 @@ typedef struct LargeObjectDesc
 	Oid			id;				/* LO's identifier */
 	Snapshot	snapshot;		/* snapshot to use */
 	SubTransactionId subid;		/* owning subtransaction ID */
-	uint32		offset;			/* current seek pointer */
+	uint64		offset;			/* current seek pointer */
 	int			flags;			/* locking info, etc */
 
 /* flag bits: */
@@ -62,7 +62,10 @@ typedef struct LargeObjectDesc
  * 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...
@@ -74,10 +77,10 @@ extern Oid	inv_create(Oid lobjId);
 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 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, int len);
+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
index 9d95e262be3fbf26731c0926013db56dbc4e00ab..56d0bb8dc5816ee128fc8234f95585aca20ebdea 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -161,3 +161,6 @@ PQping                    158
 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
index f3a6d0341c13ce644a7f1b2f15919385b85d6a6e..fb17ac8b1e9cc47f9f0f8187754fe7aa92772d13 100644
--- a/src/interfaces/libpq/fe-lobj.c
+++ b/src/interfaces/libpq/fe-lobj.c
@@ -37,10 +37,16 @@
 #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
@@ -174,6 +180,59 @@ lo_truncate(PGconn *conn, int fd, size_t len)
 	}
 }
 
+/*
+ * 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
@@ -310,6 +369,63 @@ lo_lseek(PGconn *conn, int fd, int offset, int whence)
 	}
 }
 
+/*
+ * 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
@@ -435,6 +551,52 @@ lo_tell(PGconn *conn, int fd)
 	}
 }
 
+/*
+ * 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
@@ -713,8 +875,11 @@ lo_initialize(PGconn *conn)
 			"'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 "
@@ -765,10 +930,16 @@ lo_initialize(PGconn *conn)
 			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)
@@ -836,10 +1007,76 @@ lo_initialize(PGconn *conn)
 		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
index 9d05dd20605a84ab4c4ec9d61ef8697fb2f3b77e..73568ca23c38b09a3a42b5d31078f179d687688d 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -548,6 +548,12 @@ extern Oid	lo_import(PGconn *conn, const char *filename);
 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
index 4a6c8fedf2b6b7f2f72d588c9c504dcc8d26cc0f..375821e017fd7a93c1abc58a0f7fe7adaa354cd9 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -271,8 +271,11 @@ typedef struct pgLobjfuncs
 	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
index bbc6ee1d36631258d11dbc656e6abf16333d8fdf..aee5c044075885e15de65ffef5be0951ed538f99 100644
--- a/src/test/examples/Makefile
+++ b/src/test/examples/Makefile
@@ -14,7 +14,7 @@ override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS)
 override LDLIBS := $(libpq_pgport) $(LDLIBS)
 
 
-PROGS = testlibpq testlibpq2 testlibpq3 testlibpq4 testlo
+PROGS = testlibpq testlibpq2 testlibpq3 testlibpq4 testlo testlo64
 
 all: $(PROGS)
 
diff --git a/src/test/examples/testlo64.c b/src/test/examples/testlo64.c
new file mode 100644
index 0000000000000000000000000000000000000000..6ab7f524caafabb1591598f6111cd042a634ac8e
--- /dev/null
+++ b/src/test/examples/testlo64.c
@@ -0,0 +1,320 @@
+/*-------------------------------------------------------------------------
+ *
+ * testlo64.c
+ *	  test using large objects with libpq using 64-bit APIs
+ *
+ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/test/examples/testlo64.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#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, pg_int64 start, int len)
+{
+	int			lobj_fd;
+	char	   *buf;
+	int			nbytes;
+	int			nread;
+	pg_int64		pos;
+
+	lobj_fd = lo_open(conn, lobjId, INV_READ);
+	if (lobj_fd < 0)
+		fprintf(stderr, "can't open large object %u", lobjId);
+
+	if (lo_tell64(conn, lobj_fd) < 0)
+	{
+		fprintf(stderr, "error lo_tell64: %s\n", PQerrorMessage(conn));
+	}
+
+	if ((pos=lo_lseek64(conn, lobj_fd, start, SEEK_SET)) < 0)
+	{
+		fprintf(stderr, "error lo_lseek64: %s\n", PQerrorMessage(conn));
+		return;
+	}
+
+	fprintf(stderr, "before read: retval of lo_lseek64 : %lld\n", (long long int) pos);
+
+	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");
+
+	pos = lo_tell64(conn, lobj_fd);
+	fprintf(stderr, "after read: retval of lo_tell64 : %lld\n\n", (long long int) pos);
+
+	lo_close(conn, lobj_fd);
+}
+
+static void
+overwrite(PGconn *conn, Oid lobjId, pg_int64 start, int len)
+{
+	int			lobj_fd;
+	char	   *buf;
+	int			nbytes;
+	int			nwritten;
+	int			i;
+	pg_int64		pos;
+
+	lobj_fd = lo_open(conn, lobjId, INV_READ | INV_WRITE);
+	if (lobj_fd < 0)
+		fprintf(stderr, "can't open large object %u", lobjId);
+
+	if ((pos=lo_lseek64(conn, lobj_fd, start, SEEK_SET)) < 0)
+	{
+		fprintf(stderr, "error lo_lseek64: %s\n", PQerrorMessage(conn));
+		return;
+	}
+	fprintf(stderr, "before write: retval of lo_lseek64 : %lld\n", (long long int) pos);
+
+	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);
+
+	pos = lo_tell64(conn, lobj_fd);
+	fprintf(stderr, "after write: retval of lo_tell64 : %lld\n\n", (long long int) pos);
+
+	lo_close(conn, lobj_fd);
+}
+
+static void
+my_truncate(PGconn *conn, Oid lobjId, size_t len)
+{
+	int			lobj_fd;
+
+	lobj_fd = lo_open(conn, lobjId, INV_READ | INV_WRITE);
+	if (lobj_fd < 0)
+		fprintf(stderr, "can't open large object %u", lobjId);
+
+	if (lo_truncate64(conn, lobj_fd, len) < 0)
+	{
+		fprintf(stderr, "error lo_truncate64: %s\n", PQerrorMessage(conn));
+		return;
+	}
+
+
+	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,
+			   *out_filename2;
+	char	   *database;
+	Oid			lobjOid;
+	PGconn	   *conn;
+	PGresult   *res;
+
+	if (argc != 5)
+	{
+		fprintf(stderr, "Usage: %s database_name in_filename out_filename out_filename2\n",
+				argv[0]);
+		exit(1);
+	}
+
+	database = argv[1];
+	in_filename = argv[2];
+	out_filename = argv[3];
+	out_filename2 = argv[4];
+
+	/*
+	 * 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));
+
+		printf("truncating to 3294968000 byte\n");
+		my_truncate(conn, lobjOid, 3294968000ULL);
+
+		printf("exporting truncated large object to file \"%s\" ...\n", out_filename2);
+		if (!lo_export(conn, lobjOid, out_filename2))
+			fprintf(stderr, "%s\n", PQerrorMessage(conn));
+
+	}
+
+	res = PQexec(conn, "end");
+	PQclear(res);
+	PQfinish(conn);
+	return 0;
+}
diff --git a/src/test/regress/input/largeobject.source b/src/test/regress/input/largeobject.source
index 40f40f8c76497e4e67c772f5ec6e46035f164b1a..4984d78a0694490cf53621efc854cde1f26d12b2 100644
--- a/src/test/regress/input/largeobject.source
+++ b/src/test/regress/input/largeobject.source
@@ -125,6 +125,29 @@ SELECT lo_tell(fd) FROM lotest_stash_values;
 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
index 55aaf8f2afe07384f262f1c4ace0f672350cce66..74c4772b03aba5cc3cdc75a546f3819bb26b62a5 100644
--- a/src/test/regress/output/largeobject.source
+++ b/src/test/regress/output/largeobject.source
@@ -209,6 +209,88 @@ SELECT lo_close(fd) FROM lotest_stash_values;
         0
 (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